PictureTest.cpp revision 0205aba7d5e8802d2a3ef55d999f5aa41db3adc9
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#if SK_SUPPORT_GPU 10#include "SkBlurImageFilter.h" 11#endif 12#include "SkCanvas.h" 13#include "SkColorPriv.h" 14#include "SkDashPathEffect.h" 15#include "SkData.h" 16#include "SkDecodingImageGenerator.h" 17#include "SkError.h" 18#if SK_SUPPORT_GPU 19#include "SkGpuDevice.h" 20#endif 21#include "SkImageEncoder.h" 22#include "SkImageGenerator.h" 23#include "SkPaint.h" 24#include "SkPicture.h" 25#include "SkPictureRecorder.h" 26#include "SkPictureUtils.h" 27#include "SkRRect.h" 28#include "SkRandom.h" 29#include "SkShader.h" 30#include "SkStream.h" 31 32#if SK_SUPPORT_GPU 33#include "SkSurface.h" 34#include "GrContextFactory.h" 35#include "GrPictureUtils.h" 36#endif 37#include "Test.h" 38 39static const int gColorScale = 30; 40static const int gColorOffset = 60; 41 42static void make_bm(SkBitmap* bm, int w, int h, SkColor color, bool immutable) { 43 bm->allocN32Pixels(w, h); 44 bm->eraseColor(color); 45 if (immutable) { 46 bm->setImmutable(); 47 } 48} 49 50static void make_checkerboard(SkBitmap* bm, int w, int h, bool immutable) { 51 SkASSERT(w % 2 == 0); 52 SkASSERT(h % 2 == 0); 53 bm->allocPixels(SkImageInfo::Make(w, h, kAlpha_8_SkColorType, 54 kPremul_SkAlphaType)); 55 SkAutoLockPixels lock(*bm); 56 for (int y = 0; y < h; y += 2) { 57 uint8_t* s = bm->getAddr8(0, y); 58 for (int x = 0; x < w; x += 2) { 59 *s++ = 0xFF; 60 *s++ = 0x00; 61 } 62 s = bm->getAddr8(0, y + 1); 63 for (int x = 0; x < w; x += 2) { 64 *s++ = 0x00; 65 *s++ = 0xFF; 66 } 67 } 68 if (immutable) { 69 bm->setImmutable(); 70 } 71} 72 73static void init_paint(SkPaint* paint, const SkBitmap &bm) { 74 SkShader* shader = SkShader::CreateBitmapShader(bm, 75 SkShader::kClamp_TileMode, 76 SkShader::kClamp_TileMode); 77 paint->setShader(shader)->unref(); 78} 79 80typedef void (*DrawBitmapProc)(SkCanvas*, const SkBitmap&, 81 const SkBitmap&, const SkPoint&, 82 SkTDArray<SkPixelRef*>* usedPixRefs); 83 84static void drawpaint_proc(SkCanvas* canvas, const SkBitmap& bm, 85 const SkBitmap& altBM, const SkPoint& pos, 86 SkTDArray<SkPixelRef*>* usedPixRefs) { 87 SkPaint paint; 88 init_paint(&paint, bm); 89 90 canvas->drawPaint(paint); 91 *usedPixRefs->append() = bm.pixelRef(); 92} 93 94static void drawpoints_proc(SkCanvas* canvas, const SkBitmap& bm, 95 const SkBitmap& altBM, const SkPoint& pos, 96 SkTDArray<SkPixelRef*>* usedPixRefs) { 97 SkPaint paint; 98 init_paint(&paint, bm); 99 100 // draw a rect 101 SkPoint points[5] = { 102 { pos.fX, pos.fY }, 103 { pos.fX + bm.width() - 1, pos.fY }, 104 { pos.fX + bm.width() - 1, pos.fY + bm.height() - 1 }, 105 { pos.fX, pos.fY + bm.height() - 1 }, 106 { pos.fX, pos.fY }, 107 }; 108 109 canvas->drawPoints(SkCanvas::kPolygon_PointMode, 5, points, paint); 110 *usedPixRefs->append() = bm.pixelRef(); 111} 112 113static void drawrect_proc(SkCanvas* canvas, const SkBitmap& bm, 114 const SkBitmap& altBM, const SkPoint& pos, 115 SkTDArray<SkPixelRef*>* usedPixRefs) { 116 SkPaint paint; 117 init_paint(&paint, bm); 118 119 SkRect r = { 0, 0, SkIntToScalar(bm.width()), SkIntToScalar(bm.height()) }; 120 r.offset(pos.fX, pos.fY); 121 122 canvas->drawRect(r, paint); 123 *usedPixRefs->append() = bm.pixelRef(); 124} 125 126static void drawoval_proc(SkCanvas* canvas, const SkBitmap& bm, 127 const SkBitmap& altBM, const SkPoint& pos, 128 SkTDArray<SkPixelRef*>* usedPixRefs) { 129 SkPaint paint; 130 init_paint(&paint, bm); 131 132 SkRect r = { 0, 0, SkIntToScalar(bm.width()), SkIntToScalar(bm.height()) }; 133 r.offset(pos.fX, pos.fY); 134 135 canvas->drawOval(r, paint); 136 *usedPixRefs->append() = bm.pixelRef(); 137} 138 139static void drawrrect_proc(SkCanvas* canvas, const SkBitmap& bm, 140 const SkBitmap& altBM, const SkPoint& pos, 141 SkTDArray<SkPixelRef*>* usedPixRefs) { 142 SkPaint paint; 143 init_paint(&paint, bm); 144 145 SkRect r = { 0, 0, SkIntToScalar(bm.width()), SkIntToScalar(bm.height()) }; 146 r.offset(pos.fX, pos.fY); 147 148 SkRRect rr; 149 rr.setRectXY(r, SkIntToScalar(bm.width())/4, SkIntToScalar(bm.height())/4); 150 canvas->drawRRect(rr, paint); 151 *usedPixRefs->append() = bm.pixelRef(); 152} 153 154static void drawpath_proc(SkCanvas* canvas, const SkBitmap& bm, 155 const SkBitmap& altBM, const SkPoint& pos, 156 SkTDArray<SkPixelRef*>* usedPixRefs) { 157 SkPaint paint; 158 init_paint(&paint, bm); 159 160 SkPath path; 161 path.lineTo(bm.width()/2.0f, SkIntToScalar(bm.height())); 162 path.lineTo(SkIntToScalar(bm.width()), 0); 163 path.close(); 164 path.offset(pos.fX, pos.fY); 165 166 canvas->drawPath(path, paint); 167 *usedPixRefs->append() = bm.pixelRef(); 168} 169 170static void drawbitmap_proc(SkCanvas* canvas, const SkBitmap& bm, 171 const SkBitmap& altBM, const SkPoint& pos, 172 SkTDArray<SkPixelRef*>* usedPixRefs) { 173 canvas->drawBitmap(bm, pos.fX, pos.fY, NULL); 174 *usedPixRefs->append() = bm.pixelRef(); 175} 176 177static void drawbitmap_withshader_proc(SkCanvas* canvas, const SkBitmap& bm, 178 const SkBitmap& altBM, const SkPoint& pos, 179 SkTDArray<SkPixelRef*>* usedPixRefs) { 180 SkPaint paint; 181 init_paint(&paint, bm); 182 183 // The bitmap in the paint is ignored unless we're drawing an A8 bitmap 184 canvas->drawBitmap(altBM, pos.fX, pos.fY, &paint); 185 *usedPixRefs->append() = bm.pixelRef(); 186 *usedPixRefs->append() = altBM.pixelRef(); 187} 188 189static void drawsprite_proc(SkCanvas* canvas, const SkBitmap& bm, 190 const SkBitmap& altBM, const SkPoint& pos, 191 SkTDArray<SkPixelRef*>* usedPixRefs) { 192 const SkMatrix& ctm = canvas->getTotalMatrix(); 193 194 SkPoint p(pos); 195 ctm.mapPoints(&p, 1); 196 197 canvas->drawSprite(bm, (int)p.fX, (int)p.fY, NULL); 198 *usedPixRefs->append() = bm.pixelRef(); 199} 200 201#if 0 202// Although specifiable, this case doesn't seem to make sense (i.e., the 203// bitmap in the shader is never used). 204static void drawsprite_withshader_proc(SkCanvas* canvas, const SkBitmap& bm, 205 const SkBitmap& altBM, const SkPoint& pos, 206 SkTDArray<SkPixelRef*>* usedPixRefs) { 207 SkPaint paint; 208 init_paint(&paint, bm); 209 210 const SkMatrix& ctm = canvas->getTotalMatrix(); 211 212 SkPoint p(pos); 213 ctm.mapPoints(&p, 1); 214 215 canvas->drawSprite(altBM, (int)p.fX, (int)p.fY, &paint); 216 *usedPixRefs->append() = bm.pixelRef(); 217 *usedPixRefs->append() = altBM.pixelRef(); 218} 219#endif 220 221static void drawbitmaprect_proc(SkCanvas* canvas, const SkBitmap& bm, 222 const SkBitmap& altBM, const SkPoint& pos, 223 SkTDArray<SkPixelRef*>* usedPixRefs) { 224 SkRect r = { 0, 0, SkIntToScalar(bm.width()), SkIntToScalar(bm.height()) }; 225 226 r.offset(pos.fX, pos.fY); 227 canvas->drawBitmapRectToRect(bm, NULL, r, NULL); 228 *usedPixRefs->append() = bm.pixelRef(); 229} 230 231static void drawbitmaprect_withshader_proc(SkCanvas* canvas, 232 const SkBitmap& bm, 233 const SkBitmap& altBM, 234 const SkPoint& pos, 235 SkTDArray<SkPixelRef*>* usedPixRefs) { 236 SkPaint paint; 237 init_paint(&paint, bm); 238 239 SkRect r = { 0, 0, SkIntToScalar(bm.width()), SkIntToScalar(bm.height()) }; 240 r.offset(pos.fX, pos.fY); 241 242 // The bitmap in the paint is ignored unless we're drawing an A8 bitmap 243 canvas->drawBitmapRectToRect(altBM, NULL, r, &paint); 244 *usedPixRefs->append() = bm.pixelRef(); 245 *usedPixRefs->append() = altBM.pixelRef(); 246} 247 248static void drawtext_proc(SkCanvas* canvas, const SkBitmap& bm, 249 const SkBitmap& altBM, const SkPoint& pos, 250 SkTDArray<SkPixelRef*>* usedPixRefs) { 251 SkPaint paint; 252 init_paint(&paint, bm); 253 paint.setTextSize(SkIntToScalar(1.5*bm.width())); 254 255 canvas->drawText("0", 1, pos.fX, pos.fY+bm.width(), paint); 256 *usedPixRefs->append() = bm.pixelRef(); 257} 258 259static void drawpostext_proc(SkCanvas* canvas, const SkBitmap& bm, 260 const SkBitmap& altBM, const SkPoint& pos, 261 SkTDArray<SkPixelRef*>* usedPixRefs) { 262 SkPaint paint; 263 init_paint(&paint, bm); 264 paint.setTextSize(SkIntToScalar(1.5*bm.width())); 265 266 SkPoint point = { pos.fX, pos.fY + bm.height() }; 267 canvas->drawPosText("O", 1, &point, paint); 268 *usedPixRefs->append() = bm.pixelRef(); 269} 270 271static void drawtextonpath_proc(SkCanvas* canvas, const SkBitmap& bm, 272 const SkBitmap& altBM, const SkPoint& pos, 273 SkTDArray<SkPixelRef*>* usedPixRefs) { 274 SkPaint paint; 275 276 init_paint(&paint, bm); 277 paint.setTextSize(SkIntToScalar(1.5*bm.width())); 278 279 SkPath path; 280 path.lineTo(SkIntToScalar(bm.width()), 0); 281 path.offset(pos.fX, pos.fY+bm.height()); 282 283 canvas->drawTextOnPath("O", 1, path, NULL, paint); 284 *usedPixRefs->append() = bm.pixelRef(); 285} 286 287static void drawverts_proc(SkCanvas* canvas, const SkBitmap& bm, 288 const SkBitmap& altBM, const SkPoint& pos, 289 SkTDArray<SkPixelRef*>* usedPixRefs) { 290 SkPaint paint; 291 init_paint(&paint, bm); 292 293 SkPoint verts[4] = { 294 { pos.fX, pos.fY }, 295 { pos.fX + bm.width(), pos.fY }, 296 { pos.fX + bm.width(), pos.fY + bm.height() }, 297 { pos.fX, pos.fY + bm.height() } 298 }; 299 SkPoint texs[4] = { { 0, 0 }, 300 { SkIntToScalar(bm.width()), 0 }, 301 { SkIntToScalar(bm.width()), SkIntToScalar(bm.height()) }, 302 { 0, SkIntToScalar(bm.height()) } }; 303 uint16_t indices[6] = { 0, 1, 2, 0, 2, 3 }; 304 305 canvas->drawVertices(SkCanvas::kTriangles_VertexMode, 4, verts, texs, NULL, NULL, 306 indices, 6, paint); 307 *usedPixRefs->append() = bm.pixelRef(); 308} 309 310// Return a picture with the bitmaps drawn at the specified positions. 311static SkPicture* record_bitmaps(const SkBitmap bm[], 312 const SkPoint pos[], 313 SkTDArray<SkPixelRef*> analytic[], 314 int count, 315 DrawBitmapProc proc) { 316 SkPictureRecorder recorder; 317 SkCanvas* canvas = recorder.beginRecording(1000, 1000, NULL, 0); 318 for (int i = 0; i < count; ++i) { 319 analytic[i].rewind(); 320 canvas->save(); 321 SkRect clipRect = SkRect::MakeXYWH(pos[i].fX, pos[i].fY, 322 SkIntToScalar(bm[i].width()), 323 SkIntToScalar(bm[i].height())); 324 canvas->clipRect(clipRect, SkRegion::kIntersect_Op); 325 proc(canvas, bm[i], bm[count+i], pos[i], &analytic[i]); 326 canvas->restore(); 327 } 328 return recorder.endRecording(); 329} 330 331static void rand_rect(SkRect* rect, SkRandom& rand, SkScalar W, SkScalar H) { 332 rect->fLeft = rand.nextRangeScalar(-W, 2*W); 333 rect->fTop = rand.nextRangeScalar(-H, 2*H); 334 rect->fRight = rect->fLeft + rand.nextRangeScalar(0, W); 335 rect->fBottom = rect->fTop + rand.nextRangeScalar(0, H); 336 337 // we integralize rect to make our tests more predictable, since Gather is 338 // a little sloppy. 339 SkIRect ir; 340 rect->round(&ir); 341 rect->set(ir); 342} 343 344static void draw(SkPicture* pic, int width, int height, SkBitmap* result) { 345 make_bm(result, width, height, SK_ColorBLACK, false); 346 347 SkCanvas canvas(*result); 348 canvas.drawPicture(*pic); 349} 350 351template <typename T> int find_index(const T* array, T elem, int count) { 352 for (int i = 0; i < count; ++i) { 353 if (array[i] == elem) { 354 return i; 355 } 356 } 357 return -1; 358} 359 360// Return true if 'ref' is found in array[] 361static bool find(SkPixelRef const * const * array, SkPixelRef const * ref, int count) { 362 return find_index<const SkPixelRef*>(array, ref, count) >= 0; 363} 364 365// Look at each pixel that is inside 'subset', and if its color appears in 366// colors[], find the corresponding value in refs[] and append that ref into 367// array, skipping duplicates of the same value. 368// Note that gathering pixelRefs from rendered colors suffers from the problem 369// that multiple simultaneous textures (e.g., A8 for alpha and 8888 for color) 370// isn't easy to reconstruct. 371static void gather_from_image(const SkBitmap& bm, SkPixelRef* const refs[], 372 int count, SkTDArray<SkPixelRef*>* array, 373 const SkRect& subset) { 374 SkIRect ir; 375 subset.roundOut(&ir); 376 377 if (!ir.intersect(0, 0, bm.width()-1, bm.height()-1)) { 378 return; 379 } 380 381 // Since we only want to return unique values in array, when we scan we just 382 // set a bit for each index'd color found. In practice we only have a few 383 // distinct colors, so we just use an int's bits as our array. Hence the 384 // assert that count <= number-of-bits-in-our-int. 385 SkASSERT((unsigned)count <= 32); 386 uint32_t bitarray = 0; 387 388 SkAutoLockPixels alp(bm); 389 390 for (int y = ir.fTop; y < ir.fBottom; ++y) { 391 for (int x = ir.fLeft; x < ir.fRight; ++x) { 392 SkPMColor pmc = *bm.getAddr32(x, y); 393 // the only good case where the color is not found would be if 394 // the color is transparent, meaning no bitmap was drawn in that 395 // pixel. 396 if (pmc) { 397 uint32_t index = SkGetPackedR32(pmc); 398 SkASSERT(SkGetPackedG32(pmc) == index); 399 SkASSERT(SkGetPackedB32(pmc) == index); 400 if (0 == index) { 401 continue; // background color 402 } 403 SkASSERT(0 == (index - gColorOffset) % gColorScale); 404 index = (index - gColorOffset) / gColorScale; 405 SkASSERT(static_cast<int>(index) < count); 406 bitarray |= 1 << index; 407 } 408 } 409 } 410 411 for (int i = 0; i < count; ++i) { 412 if (bitarray & (1 << i)) { 413 *array->append() = refs[i]; 414 } 415 } 416} 417 418static void gather_from_analytic(const SkPoint pos[], SkScalar w, SkScalar h, 419 const SkTDArray<SkPixelRef*> analytic[], 420 int count, 421 SkTDArray<SkPixelRef*>* result, 422 const SkRect& subset) { 423 for (int i = 0; i < count; ++i) { 424 SkRect rect = SkRect::MakeXYWH(pos[i].fX, pos[i].fY, w, h); 425 426 if (SkRect::Intersects(subset, rect)) { 427 result->append(analytic[i].count(), analytic[i].begin()); 428 } 429 } 430} 431 432 433static const struct { 434 const DrawBitmapProc proc; 435 const char* const desc; 436} gProcs[] = { 437 {drawpaint_proc, "drawpaint"}, 438 {drawpoints_proc, "drawpoints"}, 439 {drawrect_proc, "drawrect"}, 440 {drawoval_proc, "drawoval"}, 441 {drawrrect_proc, "drawrrect"}, 442 {drawpath_proc, "drawpath"}, 443 {drawbitmap_proc, "drawbitmap"}, 444 {drawbitmap_withshader_proc, "drawbitmap_withshader"}, 445 {drawsprite_proc, "drawsprite"}, 446#if 0 447 {drawsprite_withshader_proc, "drawsprite_withshader"}, 448#endif 449 {drawbitmaprect_proc, "drawbitmaprect"}, 450 {drawbitmaprect_withshader_proc, "drawbitmaprect_withshader"}, 451 {drawtext_proc, "drawtext"}, 452 {drawpostext_proc, "drawpostext"}, 453 {drawtextonpath_proc, "drawtextonpath"}, 454 {drawverts_proc, "drawverts"}, 455}; 456 457static void create_textures(SkBitmap* bm, SkPixelRef** refs, int num, int w, int h) { 458 // Our convention is that the color components contain an encoding of 459 // the index of their corresponding bitmap/pixelref. (0,0,0,0) is 460 // reserved for the background 461 for (int i = 0; i < num; ++i) { 462 make_bm(&bm[i], w, h, 463 SkColorSetARGB(0xFF, 464 gColorScale*i+gColorOffset, 465 gColorScale*i+gColorOffset, 466 gColorScale*i+gColorOffset), 467 true); 468 refs[i] = bm[i].pixelRef(); 469 } 470 471 // The A8 alternate bitmaps are all BW checkerboards 472 for (int i = 0; i < num; ++i) { 473 make_checkerboard(&bm[num+i], w, h, true); 474 refs[num+i] = bm[num+i].pixelRef(); 475 } 476} 477 478static void test_gatherpixelrefs(skiatest::Reporter* reporter) { 479 const int IW = 32; 480 const int IH = IW; 481 const SkScalar W = SkIntToScalar(IW); 482 const SkScalar H = W; 483 484 static const int N = 4; 485 SkBitmap bm[2*N]; 486 SkPixelRef* refs[2*N]; 487 SkTDArray<SkPixelRef*> analytic[N]; 488 489 const SkPoint pos[N] = { 490 { 0, 0 }, { W, 0 }, { 0, H }, { W, H } 491 }; 492 493 create_textures(bm, refs, N, IW, IH); 494 495 SkRandom rand; 496 for (size_t k = 0; k < SK_ARRAY_COUNT(gProcs); ++k) { 497 SkAutoTUnref<SkPicture> pic( 498 record_bitmaps(bm, pos, analytic, N, gProcs[k].proc)); 499 500 REPORTER_ASSERT(reporter, pic->willPlayBackBitmaps() || N == 0); 501 // quick check for a small piece of each quadrant, which should just 502 // contain 1 or 2 bitmaps. 503 for (size_t i = 0; i < SK_ARRAY_COUNT(pos); ++i) { 504 SkRect r; 505 r.set(2, 2, W - 2, H - 2); 506 r.offset(pos[i].fX, pos[i].fY); 507 SkAutoDataUnref data(SkPictureUtils::GatherPixelRefs(pic, r)); 508 if (!data) { 509 ERRORF(reporter, "SkPictureUtils::GatherPixelRefs returned " 510 "NULL for %s.", gProcs[k].desc); 511 continue; 512 } 513 SkPixelRef** gatheredRefs = (SkPixelRef**)data->data(); 514 int count = static_cast<int>(data->size() / sizeof(SkPixelRef*)); 515 REPORTER_ASSERT(reporter, 1 == count || 2 == count); 516 if (1 == count) { 517 REPORTER_ASSERT(reporter, gatheredRefs[0] == refs[i]); 518 } else if (2 == count) { 519 REPORTER_ASSERT(reporter, 520 (gatheredRefs[0] == refs[i] && gatheredRefs[1] == refs[i+N]) || 521 (gatheredRefs[1] == refs[i] && gatheredRefs[0] == refs[i+N])); 522 } 523 } 524 525 SkBitmap image; 526 draw(pic, 2*IW, 2*IH, &image); 527 528 // Test a bunch of random (mostly) rects, and compare the gather results 529 // with a deduced list of refs by looking at the colors drawn. 530 for (int j = 0; j < 100; ++j) { 531 SkRect r; 532 rand_rect(&r, rand, 2*W, 2*H); 533 534 SkTDArray<SkPixelRef*> fromImage; 535 gather_from_image(image, refs, N, &fromImage, r); 536 537 SkTDArray<SkPixelRef*> fromAnalytic; 538 gather_from_analytic(pos, W, H, analytic, N, &fromAnalytic, r); 539 540 SkData* data = SkPictureUtils::GatherPixelRefs(pic, r); 541 size_t dataSize = data ? data->size() : 0; 542 int gatherCount = static_cast<int>(dataSize / sizeof(SkPixelRef*)); 543 SkASSERT(gatherCount * sizeof(SkPixelRef*) == dataSize); 544 SkPixelRef** gatherRefs = data ? (SkPixelRef**)(data->data()) : NULL; 545 SkAutoDataUnref adu(data); 546 547 // Everything that we saw drawn should appear in the analytic list 548 // but the analytic list may contain some pixelRefs that were not 549 // seen in the image (e.g., A8 textures used as masks) 550 for (int i = 0; i < fromImage.count(); ++i) { 551 if (-1 == fromAnalytic.find(fromImage[i])) { 552 ERRORF(reporter, "PixelRef missing %d %s", 553 i, gProcs[k].desc); 554 } 555 } 556 557 /* 558 * GatherPixelRefs is conservative, so it can return more bitmaps 559 * than are strictly required. Thus our check here is only that 560 * Gather didn't miss any that we actually needed. Even that isn't 561 * a strict requirement on Gather, which is meant to be quick and 562 * only mostly-correct, but at the moment this test should work. 563 */ 564 for (int i = 0; i < fromAnalytic.count(); ++i) { 565 bool found = find(gatherRefs, fromAnalytic[i], gatherCount); 566 if (!found) { 567 ERRORF(reporter, "PixelRef missing %d %s", 568 i, gProcs[k].desc); 569 } 570#if 0 571 // enable this block of code to debug failures, as it will rerun 572 // the case that failed. 573 if (!found) { 574 SkData* data = SkPictureUtils::GatherPixelRefs(pic, r); 575 size_t dataSize = data ? data->size() : 0; 576 } 577#endif 578 } 579 } 580 } 581} 582 583static void test_gatherpixelrefsandrects(skiatest::Reporter* reporter) { 584 const int IW = 32; 585 const int IH = IW; 586 const SkScalar W = SkIntToScalar(IW); 587 const SkScalar H = W; 588 589 static const int N = 4; 590 SkBitmap bm[2*N]; 591 SkPixelRef* refs[2*N]; 592 SkTDArray<SkPixelRef*> analytic[N]; 593 594 const SkPoint pos[N] = { 595 { 0, 0 }, { W, 0 }, { 0, H }, { W, H } 596 }; 597 598 create_textures(bm, refs, N, IW, IH); 599 600 SkRandom rand; 601 for (size_t k = 0; k < SK_ARRAY_COUNT(gProcs); ++k) { 602 SkAutoTUnref<SkPicture> pic( 603 record_bitmaps(bm, pos, analytic, N, gProcs[k].proc)); 604 605 REPORTER_ASSERT(reporter, pic->willPlayBackBitmaps() || N == 0); 606 607 SkAutoTUnref<SkPictureUtils::SkPixelRefContainer> prCont( 608 new SkPictureUtils::SkPixelRefsAndRectsList); 609 610 SkPictureUtils::GatherPixelRefsAndRects(pic, prCont); 611 612 // quick check for a small piece of each quadrant, which should just 613 // contain 1 or 2 bitmaps. 614 for (size_t i = 0; i < SK_ARRAY_COUNT(pos); ++i) { 615 SkRect r; 616 r.set(2, 2, W - 2, H - 2); 617 r.offset(pos[i].fX, pos[i].fY); 618 619 SkTDArray<SkPixelRef*> gatheredRefs; 620 prCont->query(r, &gatheredRefs); 621 622 int count = gatheredRefs.count(); 623 REPORTER_ASSERT(reporter, 1 == count || 2 == count); 624 if (1 == count) { 625 REPORTER_ASSERT(reporter, gatheredRefs[0] == refs[i]); 626 } else if (2 == count) { 627 REPORTER_ASSERT(reporter, 628 (gatheredRefs[0] == refs[i] && gatheredRefs[1] == refs[i+N]) || 629 (gatheredRefs[1] == refs[i] && gatheredRefs[0] == refs[i+N])); 630 } 631 } 632 633 SkBitmap image; 634 draw(pic, 2*IW, 2*IH, &image); 635 636 // Test a bunch of random (mostly) rects, and compare the gather results 637 // with the analytic results and the pixel refs seen in a rendering. 638 for (int j = 0; j < 100; ++j) { 639 SkRect r; 640 rand_rect(&r, rand, 2*W, 2*H); 641 642 SkTDArray<SkPixelRef*> fromImage; 643 gather_from_image(image, refs, N, &fromImage, r); 644 645 SkTDArray<SkPixelRef*> fromAnalytic; 646 gather_from_analytic(pos, W, H, analytic, N, &fromAnalytic, r); 647 648 SkTDArray<SkPixelRef*> gatheredRefs; 649 prCont->query(r, &gatheredRefs); 650 651 // Everything that we saw drawn should appear in the analytic list 652 // but the analytic list may contain some pixelRefs that were not 653 // seen in the image (e.g., A8 textures used as masks) 654 for (int i = 0; i < fromImage.count(); ++i) { 655 REPORTER_ASSERT(reporter, -1 != fromAnalytic.find(fromImage[i])); 656 } 657 658 // Everything in the analytic list should appear in the gathered 659 // list. 660 for (int i = 0; i < fromAnalytic.count(); ++i) { 661 REPORTER_ASSERT(reporter, -1 != gatheredRefs.find(fromAnalytic[i])); 662 } 663 } 664 } 665} 666 667#ifdef SK_DEBUG 668// Ensure that deleting SkPicturePlayback does not assert. Asserts only fire in debug mode, so only 669// run in debug mode. 670static void test_deleting_empty_playback() { 671 SkPictureRecorder recorder; 672 // Creates an SkPictureRecord 673 recorder.beginRecording(0, 0, NULL, 0); 674 // Turns that into an SkPicturePlayback 675 SkAutoTUnref<SkPicture> picture(recorder.endRecording()); 676 // Deletes the old SkPicturePlayback, and creates a new SkPictureRecord 677 recorder.beginRecording(0, 0, NULL, 0); 678} 679 680// Ensure that serializing an empty picture does not assert. Likewise only runs in debug mode. 681static void test_serializing_empty_picture() { 682 SkPictureRecorder recorder; 683 recorder.beginRecording(0, 0, NULL, 0); 684 SkAutoTUnref<SkPicture> picture(recorder.endRecording()); 685 SkDynamicMemoryWStream stream; 686 picture->serialize(&stream); 687} 688#endif 689 690static void rand_op(SkCanvas* canvas, SkRandom& rand) { 691 SkPaint paint; 692 SkRect rect = SkRect::MakeWH(50, 50); 693 694 SkScalar unit = rand.nextUScalar1(); 695 if (unit <= 0.3) { 696// SkDebugf("save\n"); 697 canvas->save(); 698 } else if (unit <= 0.6) { 699// SkDebugf("restore\n"); 700 canvas->restore(); 701 } else if (unit <= 0.9) { 702// SkDebugf("clip\n"); 703 canvas->clipRect(rect); 704 } else { 705// SkDebugf("draw\n"); 706 canvas->drawPaint(paint); 707 } 708} 709 710#if SK_SUPPORT_GPU 711static void test_gpu_veto(skiatest::Reporter* reporter) { 712 713 SkPictureRecorder recorder; 714 715 SkCanvas* canvas = recorder.beginRecording(100, 100, NULL, 0); 716 { 717 SkPath path; 718 path.moveTo(0, 0); 719 path.lineTo(50, 50); 720 721 SkScalar intervals[] = { 1.0f, 1.0f }; 722 SkAutoTUnref<SkDashPathEffect> dash(SkDashPathEffect::Create(intervals, 2, 0)); 723 724 SkPaint paint; 725 paint.setStyle(SkPaint::kStroke_Style); 726 paint.setPathEffect(dash); 727 728 canvas->drawPath(path, paint); 729 } 730 SkAutoTUnref<SkPicture> picture(recorder.endRecording()); 731 // path effects currently render an SkPicture undesireable for GPU rendering 732 REPORTER_ASSERT(reporter, !picture->suitableForGpuRasterization(NULL)); 733 734 canvas = recorder.beginRecording(100, 100, NULL, 0); 735 { 736 SkPath path; 737 738 path.moveTo(0, 0); 739 path.lineTo(0, 50); 740 path.lineTo(25, 25); 741 path.lineTo(50, 50); 742 path.lineTo(50, 0); 743 path.close(); 744 REPORTER_ASSERT(reporter, !path.isConvex()); 745 746 SkPaint paint; 747 paint.setAntiAlias(true); 748 for (int i = 0; i < 50; ++i) { 749 canvas->drawPath(path, paint); 750 } 751 } 752 picture.reset(recorder.endRecording()); 753 // A lot of AA concave paths currently render an SkPicture undesireable for GPU rendering 754 REPORTER_ASSERT(reporter, !picture->suitableForGpuRasterization(NULL)); 755 756 canvas = recorder.beginRecording(100, 100, NULL, 0); 757 { 758 SkPath path; 759 760 path.moveTo(0, 0); 761 path.lineTo(0, 50); 762 path.lineTo(25, 25); 763 path.lineTo(50, 50); 764 path.lineTo(50, 0); 765 path.close(); 766 REPORTER_ASSERT(reporter, !path.isConvex()); 767 768 SkPaint paint; 769 paint.setAntiAlias(true); 770 paint.setStyle(SkPaint::kStroke_Style); 771 paint.setStrokeWidth(0); 772 for (int i = 0; i < 50; ++i) { 773 canvas->drawPath(path, paint); 774 } 775 } 776 picture.reset(recorder.endRecording()); 777 // hairline stroked AA concave paths are fine for GPU rendering 778 REPORTER_ASSERT(reporter, picture->suitableForGpuRasterization(NULL)); 779} 780 781static void test_gpu_picture_optimization(skiatest::Reporter* reporter, 782 GrContextFactory* factory) { 783 784 GrContext* context = factory->get(GrContextFactory::kNative_GLContextType); 785 786 static const int kWidth = 100; 787 static const int kHeight = 100; 788 789 SkAutoTUnref<SkPicture> pict; 790 791 // create a picture with the structure: 792 // 1) 793 // SaveLayer 794 // Restore 795 // 2) 796 // SaveLayer 797 // Translate 798 // SaveLayer w/ bound 799 // Restore 800 // Restore 801 // 3) 802 // SaveLayer w/ copyable paint 803 // Restore 804 // 4) 805 // SaveLayer w/ non-copyable paint 806 // Restore 807 { 808 SkPictureRecorder recorder; 809 810 SkCanvas* c = recorder.beginRecording(kWidth, kHeight, NULL, 0); 811 // 1) 812 c->saveLayer(NULL, NULL); 813 c->restore(); 814 815 // 2) 816 c->saveLayer(NULL, NULL); 817 c->translate(kWidth/2, kHeight/2); 818 SkRect r = SkRect::MakeXYWH(0, 0, kWidth/2, kHeight/2); 819 c->saveLayer(&r, NULL); 820 c->restore(); 821 c->restore(); 822 823 // 3) 824 { 825 SkPaint p; 826 p.setColor(SK_ColorRED); 827 c->saveLayer(NULL, &p); 828 c->restore(); 829 } 830 // 4) 831 // TODO: this case will need to be removed once the paint's are immutable 832 { 833 SkPaint p; 834 SkBitmap bmp; 835 bmp.allocN32Pixels(10, 10); 836 bmp.eraseColor(SK_ColorGREEN); 837 bmp.setAlphaType(kOpaque_SkAlphaType); 838 SkShader* shader = SkShader::CreateBitmapShader(bmp, 839 SkShader::kClamp_TileMode, SkShader::kClamp_TileMode); 840 p.setShader(shader)->unref(); 841 842 c->saveLayer(NULL, &p); 843 c->restore(); 844 } 845 846 pict.reset(recorder.endRecording()); 847 } 848 849 // Now test out the SaveLayer extraction 850 { 851 SkImageInfo info = SkImageInfo::MakeN32Premul(kWidth, kHeight); 852 853 SkAutoTUnref<SkSurface> surface(SkSurface::NewScratchRenderTarget(context, info)); 854 855 SkCanvas* canvas = surface->getCanvas(); 856 857 canvas->EXPERIMENTAL_optimize(pict); 858 859 SkPicture::AccelData::Key key = GPUAccelData::ComputeAccelDataKey(); 860 861 const SkPicture::AccelData* data = pict->EXPERIMENTAL_getAccelData(key); 862 REPORTER_ASSERT(reporter, NULL != data); 863 864 const GPUAccelData *gpuData = static_cast<const GPUAccelData*>(data); 865 REPORTER_ASSERT(reporter, 5 == gpuData->numSaveLayers()); 866 867 const GPUAccelData::SaveLayerInfo& info0 = gpuData->saveLayerInfo(0); 868 // The parent/child layer appear in reverse order 869 const GPUAccelData::SaveLayerInfo& info1 = gpuData->saveLayerInfo(2); 870 const GPUAccelData::SaveLayerInfo& info2 = gpuData->saveLayerInfo(1); 871 const GPUAccelData::SaveLayerInfo& info3 = gpuData->saveLayerInfo(3); 872 const GPUAccelData::SaveLayerInfo& info4 = gpuData->saveLayerInfo(4); 873 874 REPORTER_ASSERT(reporter, info0.fValid); 875 REPORTER_ASSERT(reporter, kWidth == info0.fSize.fWidth && kHeight == info0.fSize.fHeight); 876 REPORTER_ASSERT(reporter, info0.fCTM.isIdentity()); 877 REPORTER_ASSERT(reporter, 0 == info0.fOffset.fX && 0 == info0.fOffset.fY); 878 REPORTER_ASSERT(reporter, NULL != info0.fPaint); 879 REPORTER_ASSERT(reporter, !info0.fIsNested && !info0.fHasNestedLayers); 880 881 REPORTER_ASSERT(reporter, info1.fValid); 882 REPORTER_ASSERT(reporter, kWidth == info1.fSize.fWidth && kHeight == info1.fSize.fHeight); 883 REPORTER_ASSERT(reporter, info1.fCTM.isIdentity()); 884 REPORTER_ASSERT(reporter, 0 == info1.fOffset.fX && 0 == info1.fOffset.fY); 885 REPORTER_ASSERT(reporter, NULL != info1.fPaint); 886 REPORTER_ASSERT(reporter, !info1.fIsNested && info1.fHasNestedLayers); // has a nested SL 887 888 REPORTER_ASSERT(reporter, info2.fValid); 889 REPORTER_ASSERT(reporter, kWidth/2 == info2.fSize.fWidth && 890 kHeight/2 == info2.fSize.fHeight); // bound reduces size 891 REPORTER_ASSERT(reporter, info2.fCTM.isIdentity()); // translated 892 REPORTER_ASSERT(reporter, 0 == info2.fOffset.fX && 0 == info2.fOffset.fY); 893 REPORTER_ASSERT(reporter, NULL != info1.fPaint); 894 REPORTER_ASSERT(reporter, info2.fIsNested && !info2.fHasNestedLayers); // is nested 895 896 REPORTER_ASSERT(reporter, info3.fValid); 897 REPORTER_ASSERT(reporter, kWidth == info3.fSize.fWidth && kHeight == info3.fSize.fHeight); 898 REPORTER_ASSERT(reporter, info3.fCTM.isIdentity()); 899 REPORTER_ASSERT(reporter, 0 == info3.fOffset.fX && 0 == info3.fOffset.fY); 900 REPORTER_ASSERT(reporter, NULL != info3.fPaint); 901 REPORTER_ASSERT(reporter, !info3.fIsNested && !info3.fHasNestedLayers); 902 903 REPORTER_ASSERT(reporter, !info4.fValid); // paint is/was uncopyable 904 REPORTER_ASSERT(reporter, kWidth == info4.fSize.fWidth && kHeight == info4.fSize.fHeight); 905 REPORTER_ASSERT(reporter, 0 == info4.fOffset.fX && 0 == info4.fOffset.fY); 906 REPORTER_ASSERT(reporter, info4.fCTM.isIdentity()); 907 REPORTER_ASSERT(reporter, NULL == info4.fPaint); // paint is/was uncopyable 908 REPORTER_ASSERT(reporter, !info4.fIsNested && !info4.fHasNestedLayers); 909 } 910} 911 912#endif 913 914static void set_canvas_to_save_count_4(SkCanvas* canvas) { 915 canvas->restoreToCount(1); 916 canvas->save(); 917 canvas->save(); 918 canvas->save(); 919} 920 921static void test_unbalanced_save_restores(skiatest::Reporter* reporter) { 922 SkCanvas testCanvas(100, 100); 923 set_canvas_to_save_count_4(&testCanvas); 924 925 REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount()); 926 927 SkPaint paint; 928 SkRect rect = SkRect::MakeLTRB(-10000000, -10000000, 10000000, 10000000); 929 930 SkPictureRecorder recorder; 931 932 { 933 // Create picture with 2 unbalanced saves 934 SkCanvas* canvas = recorder.beginRecording(100, 100, NULL, 0); 935 canvas->save(); 936 canvas->translate(10, 10); 937 canvas->drawRect(rect, paint); 938 canvas->save(); 939 canvas->translate(10, 10); 940 canvas->drawRect(rect, paint); 941 SkAutoTUnref<SkPicture> extraSavePicture(recorder.endRecording()); 942 943 testCanvas.drawPicture(*extraSavePicture); 944 REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount()); 945 } 946 947 set_canvas_to_save_count_4(&testCanvas); 948 949 { 950 // Create picture with 2 unbalanced restores 951 SkCanvas* canvas = recorder.beginRecording(100, 100, NULL, 0); 952 canvas->save(); 953 canvas->translate(10, 10); 954 canvas->drawRect(rect, paint); 955 canvas->save(); 956 canvas->translate(10, 10); 957 canvas->drawRect(rect, paint); 958 canvas->restore(); 959 canvas->restore(); 960 canvas->restore(); 961 canvas->restore(); 962 SkAutoTUnref<SkPicture> extraRestorePicture(recorder.endRecording()); 963 964 testCanvas.drawPicture(*extraRestorePicture); 965 REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount()); 966 } 967 968 set_canvas_to_save_count_4(&testCanvas); 969 970 { 971 SkCanvas* canvas = recorder.beginRecording(100, 100, NULL, 0); 972 canvas->translate(10, 10); 973 canvas->drawRect(rect, paint); 974 SkAutoTUnref<SkPicture> noSavePicture(recorder.endRecording()); 975 976 testCanvas.drawPicture(*noSavePicture); 977 REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount()); 978 REPORTER_ASSERT(reporter, testCanvas.getTotalMatrix().isIdentity()); 979 } 980} 981 982static void test_peephole() { 983 SkRandom rand; 984 985 SkPictureRecorder recorder; 986 987 for (int j = 0; j < 100; j++) { 988 SkRandom rand2(rand); // remember the seed 989 990 SkCanvas* canvas = recorder.beginRecording(100, 100, NULL, 0); 991 992 for (int i = 0; i < 1000; ++i) { 993 rand_op(canvas, rand); 994 } 995 SkAutoTUnref<SkPicture> picture(recorder.endRecording()); 996 997 rand = rand2; 998 } 999 1000 { 1001 SkCanvas* canvas = recorder.beginRecording(100, 100, NULL, 0); 1002 SkRect rect = SkRect::MakeWH(50, 50); 1003 1004 for (int i = 0; i < 100; ++i) { 1005 canvas->save(); 1006 } 1007 while (canvas->getSaveCount() > 1) { 1008 canvas->clipRect(rect); 1009 canvas->restore(); 1010 } 1011 SkAutoTUnref<SkPicture> picture(recorder.endRecording()); 1012 } 1013} 1014 1015#ifndef SK_DEBUG 1016// Only test this is in release mode. We deliberately crash in debug mode, since a valid caller 1017// should never do this. 1018static void test_bad_bitmap() { 1019 // This bitmap has a width and height but no pixels. As a result, attempting to record it will 1020 // fail. 1021 SkBitmap bm; 1022 bm.setConfig(SkImageInfo::MakeN32Premul(100, 100)); 1023 SkPictureRecorder recorder; 1024 SkCanvas* recordingCanvas = recorder.beginRecording(100, 100, NULL, 0); 1025 recordingCanvas->drawBitmap(bm, 0, 0); 1026 SkAutoTUnref<SkPicture> picture(recorder.endRecording()); 1027 1028 SkCanvas canvas; 1029 canvas.drawPicture(*picture); 1030} 1031#endif 1032 1033static SkData* encode_bitmap_to_data(size_t*, const SkBitmap& bm) { 1034 return SkImageEncoder::EncodeData(bm, SkImageEncoder::kPNG_Type, 100); 1035} 1036 1037static SkData* serialized_picture_from_bitmap(const SkBitmap& bitmap) { 1038 SkPictureRecorder recorder; 1039 SkCanvas* canvas = recorder.beginRecording(bitmap.width(), bitmap.height(), NULL, 0); 1040 canvas->drawBitmap(bitmap, 0, 0); 1041 SkAutoTUnref<SkPicture> picture(recorder.endRecording()); 1042 1043 SkDynamicMemoryWStream wStream; 1044 picture->serialize(&wStream, &encode_bitmap_to_data); 1045 return wStream.copyToData(); 1046} 1047 1048struct ErrorContext { 1049 int fErrors; 1050 skiatest::Reporter* fReporter; 1051}; 1052 1053static void assert_one_parse_error_cb(SkError error, void* context) { 1054 ErrorContext* errorContext = static_cast<ErrorContext*>(context); 1055 errorContext->fErrors++; 1056 // This test only expects one error, and that is a kParseError. If there are others, 1057 // there is some unknown problem. 1058 REPORTER_ASSERT_MESSAGE(errorContext->fReporter, 1 == errorContext->fErrors, 1059 "This threw more errors than expected."); 1060 REPORTER_ASSERT_MESSAGE(errorContext->fReporter, kParseError_SkError == error, 1061 SkGetLastErrorString()); 1062} 1063 1064static void test_bitmap_with_encoded_data(skiatest::Reporter* reporter) { 1065 // Create a bitmap that will be encoded. 1066 SkBitmap original; 1067 make_bm(&original, 100, 100, SK_ColorBLUE, true); 1068 SkDynamicMemoryWStream wStream; 1069 if (!SkImageEncoder::EncodeStream(&wStream, original, SkImageEncoder::kPNG_Type, 100)) { 1070 return; 1071 } 1072 SkAutoDataUnref data(wStream.copyToData()); 1073 1074 SkBitmap bm; 1075 bool installSuccess = SkInstallDiscardablePixelRef( 1076 SkDecodingImageGenerator::Create(data, SkDecodingImageGenerator::Options()), &bm, NULL); 1077 REPORTER_ASSERT(reporter, installSuccess); 1078 1079 // Write both bitmaps to pictures, and ensure that the resulting data streams are the same. 1080 // Flattening original will follow the old path of performing an encode, while flattening bm 1081 // will use the already encoded data. 1082 SkAutoDataUnref picture1(serialized_picture_from_bitmap(original)); 1083 SkAutoDataUnref picture2(serialized_picture_from_bitmap(bm)); 1084 REPORTER_ASSERT(reporter, picture1->equals(picture2)); 1085 // Now test that a parse error was generated when trying to create a new SkPicture without 1086 // providing a function to decode the bitmap. 1087 ErrorContext context; 1088 context.fErrors = 0; 1089 context.fReporter = reporter; 1090 SkSetErrorCallback(assert_one_parse_error_cb, &context); 1091 SkMemoryStream pictureStream(picture1); 1092 SkClearLastError(); 1093 SkAutoUnref pictureFromStream(SkPicture::CreateFromStream(&pictureStream, NULL)); 1094 REPORTER_ASSERT(reporter, pictureFromStream.get() != NULL); 1095 SkClearLastError(); 1096 SkSetErrorCallback(NULL, NULL); 1097} 1098 1099static void test_clone_empty(skiatest::Reporter* reporter) { 1100 // This is a regression test for crbug.com/172062 1101 // Before the fix, we used to crash accessing a null pointer when we 1102 // had a picture with no paints. This test passes by not crashing. 1103 { 1104 SkPictureRecorder recorder; 1105 recorder.beginRecording(1, 1, NULL, 0); 1106 SkAutoTUnref<SkPicture> picture(recorder.endRecording()); 1107 SkAutoTUnref<SkPicture> destPicture(picture->clone()); 1108 REPORTER_ASSERT(reporter, NULL != destPicture); 1109 } 1110} 1111 1112static void test_draw_empty(skiatest::Reporter* reporter) { 1113 SkBitmap result; 1114 make_bm(&result, 2, 2, SK_ColorBLACK, false); 1115 1116 SkCanvas canvas(result); 1117 1118 { 1119 // stock SkPicture 1120 SkPictureRecorder recorder; 1121 recorder.beginRecording(1, 1, NULL, 0); 1122 SkAutoTUnref<SkPicture> picture(recorder.endRecording()); 1123 1124 canvas.drawPicture(*picture); 1125 } 1126 1127 { 1128 // tile grid 1129 SkTileGridFactory::TileGridInfo gridInfo; 1130 gridInfo.fMargin.setEmpty(); 1131 gridInfo.fOffset.setZero(); 1132 gridInfo.fTileInterval.set(1, 1); 1133 1134 SkTileGridFactory factory(gridInfo); 1135 SkPictureRecorder recorder; 1136 recorder.beginRecording(1, 1, &factory, 0); 1137 SkAutoTUnref<SkPicture> picture(recorder.endRecording()); 1138 1139 canvas.drawPicture(*picture); 1140 } 1141 1142 { 1143 // RTree 1144 SkRTreeFactory factory; 1145 SkPictureRecorder recorder; 1146 recorder.beginRecording(1, 1, &factory, 0); 1147 SkAutoTUnref<SkPicture> picture(recorder.endRecording()); 1148 1149 canvas.drawPicture(*picture); 1150 } 1151 1152 { 1153 // quad tree 1154 SkQuadTreeFactory factory; 1155 SkPictureRecorder recorder; 1156 recorder.beginRecording(1, 1, &factory, 0); 1157 SkAutoTUnref<SkPicture> picture(recorder.endRecording()); 1158 1159 canvas.drawPicture(*picture); 1160 } 1161} 1162 1163static void test_clip_bound_opt(skiatest::Reporter* reporter) { 1164 // Test for crbug.com/229011 1165 SkRect rect1 = SkRect::MakeXYWH(SkIntToScalar(4), SkIntToScalar(4), 1166 SkIntToScalar(2), SkIntToScalar(2)); 1167 SkRect rect2 = SkRect::MakeXYWH(SkIntToScalar(7), SkIntToScalar(7), 1168 SkIntToScalar(1), SkIntToScalar(1)); 1169 SkRect rect3 = SkRect::MakeXYWH(SkIntToScalar(6), SkIntToScalar(6), 1170 SkIntToScalar(1), SkIntToScalar(1)); 1171 1172 SkPath invPath; 1173 invPath.addOval(rect1); 1174 invPath.setFillType(SkPath::kInverseEvenOdd_FillType); 1175 SkPath path; 1176 path.addOval(rect2); 1177 SkPath path2; 1178 path2.addOval(rect3); 1179 SkIRect clipBounds; 1180 SkPictureRecorder recorder; 1181 // Minimalist test set for 100% code coverage of 1182 // SkPictureRecord::updateClipConservativelyUsingBounds 1183 { 1184 SkCanvas* canvas = recorder.beginRecording(10, 10, NULL, 1185 SkPicture::kUsePathBoundsForClip_RecordingFlag); 1186 canvas->clipPath(invPath, SkRegion::kIntersect_Op); 1187 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds); 1188 REPORTER_ASSERT(reporter, true == nonEmpty); 1189 REPORTER_ASSERT(reporter, 0 == clipBounds.fLeft); 1190 REPORTER_ASSERT(reporter, 0 == clipBounds.fTop); 1191 REPORTER_ASSERT(reporter, 10 == clipBounds.fBottom); 1192 REPORTER_ASSERT(reporter, 10 == clipBounds.fRight); 1193 } 1194 { 1195 SkCanvas* canvas = recorder.beginRecording(10, 10, NULL, 1196 SkPicture::kUsePathBoundsForClip_RecordingFlag); 1197 canvas->clipPath(path, SkRegion::kIntersect_Op); 1198 canvas->clipPath(invPath, SkRegion::kIntersect_Op); 1199 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds); 1200 REPORTER_ASSERT(reporter, true == nonEmpty); 1201 REPORTER_ASSERT(reporter, 7 == clipBounds.fLeft); 1202 REPORTER_ASSERT(reporter, 7 == clipBounds.fTop); 1203 REPORTER_ASSERT(reporter, 8 == clipBounds.fBottom); 1204 REPORTER_ASSERT(reporter, 8 == clipBounds.fRight); 1205 } 1206 { 1207 SkCanvas* canvas = recorder.beginRecording(10, 10, NULL, 1208 SkPicture::kUsePathBoundsForClip_RecordingFlag); 1209 canvas->clipPath(path, SkRegion::kIntersect_Op); 1210 canvas->clipPath(invPath, SkRegion::kUnion_Op); 1211 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds); 1212 REPORTER_ASSERT(reporter, true == nonEmpty); 1213 REPORTER_ASSERT(reporter, 0 == clipBounds.fLeft); 1214 REPORTER_ASSERT(reporter, 0 == clipBounds.fTop); 1215 REPORTER_ASSERT(reporter, 10 == clipBounds.fBottom); 1216 REPORTER_ASSERT(reporter, 10 == clipBounds.fRight); 1217 } 1218 { 1219 SkCanvas* canvas = recorder.beginRecording(10, 10, NULL, 1220 SkPicture::kUsePathBoundsForClip_RecordingFlag); 1221 canvas->clipPath(path, SkRegion::kDifference_Op); 1222 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds); 1223 REPORTER_ASSERT(reporter, true == nonEmpty); 1224 REPORTER_ASSERT(reporter, 0 == clipBounds.fLeft); 1225 REPORTER_ASSERT(reporter, 0 == clipBounds.fTop); 1226 REPORTER_ASSERT(reporter, 10 == clipBounds.fBottom); 1227 REPORTER_ASSERT(reporter, 10 == clipBounds.fRight); 1228 } 1229 { 1230 SkCanvas* canvas = recorder.beginRecording(10, 10, NULL, 1231 SkPicture::kUsePathBoundsForClip_RecordingFlag); 1232 canvas->clipPath(path, SkRegion::kReverseDifference_Op); 1233 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds); 1234 // True clip is actually empty in this case, but the best 1235 // determination we can make using only bounds as input is that the 1236 // clip is included in the bounds of 'path'. 1237 REPORTER_ASSERT(reporter, true == nonEmpty); 1238 REPORTER_ASSERT(reporter, 7 == clipBounds.fLeft); 1239 REPORTER_ASSERT(reporter, 7 == clipBounds.fTop); 1240 REPORTER_ASSERT(reporter, 8 == clipBounds.fBottom); 1241 REPORTER_ASSERT(reporter, 8 == clipBounds.fRight); 1242 } 1243 { 1244 SkCanvas* canvas = recorder.beginRecording(10, 10, NULL, 1245 SkPicture::kUsePathBoundsForClip_RecordingFlag); 1246 canvas->clipPath(path, SkRegion::kIntersect_Op); 1247 canvas->clipPath(path2, SkRegion::kXOR_Op); 1248 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds); 1249 REPORTER_ASSERT(reporter, true == nonEmpty); 1250 REPORTER_ASSERT(reporter, 6 == clipBounds.fLeft); 1251 REPORTER_ASSERT(reporter, 6 == clipBounds.fTop); 1252 REPORTER_ASSERT(reporter, 8 == clipBounds.fBottom); 1253 REPORTER_ASSERT(reporter, 8 == clipBounds.fRight); 1254 } 1255} 1256 1257/** 1258 * A canvas that records the number of clip commands. 1259 */ 1260class ClipCountingCanvas : public SkCanvas { 1261public: 1262 explicit ClipCountingCanvas(int width, int height) 1263 : INHERITED(width, height) 1264 , fClipCount(0){ 1265 } 1266 1267 virtual void onClipRect(const SkRect& r, 1268 SkRegion::Op op, 1269 ClipEdgeStyle edgeStyle) SK_OVERRIDE { 1270 fClipCount += 1; 1271 this->INHERITED::onClipRect(r, op, edgeStyle); 1272 } 1273 1274 virtual void onClipRRect(const SkRRect& rrect, 1275 SkRegion::Op op, 1276 ClipEdgeStyle edgeStyle)SK_OVERRIDE { 1277 fClipCount += 1; 1278 this->INHERITED::onClipRRect(rrect, op, edgeStyle); 1279 } 1280 1281 virtual void onClipPath(const SkPath& path, 1282 SkRegion::Op op, 1283 ClipEdgeStyle edgeStyle) SK_OVERRIDE { 1284 fClipCount += 1; 1285 this->INHERITED::onClipPath(path, op, edgeStyle); 1286 } 1287 1288 virtual void onClipRegion(const SkRegion& deviceRgn, SkRegion::Op op) SK_OVERRIDE { 1289 fClipCount += 1; 1290 this->INHERITED::onClipRegion(deviceRgn, op); 1291 } 1292 1293 unsigned getClipCount() const { return fClipCount; } 1294 1295private: 1296 unsigned fClipCount; 1297 1298 typedef SkCanvas INHERITED; 1299}; 1300 1301static void test_clip_expansion(skiatest::Reporter* reporter) { 1302 SkPictureRecorder recorder; 1303 SkCanvas* canvas = recorder.beginRecording(10, 10, NULL, 0); 1304 1305 canvas->clipRect(SkRect::MakeEmpty(), SkRegion::kReplace_Op); 1306 // The following expanding clip should not be skipped. 1307 canvas->clipRect(SkRect::MakeXYWH(4, 4, 3, 3), SkRegion::kUnion_Op); 1308 // Draw something so the optimizer doesn't just fold the world. 1309 SkPaint p; 1310 p.setColor(SK_ColorBLUE); 1311 canvas->drawPaint(p); 1312 SkAutoTUnref<SkPicture> picture(recorder.endRecording()); 1313 1314 ClipCountingCanvas testCanvas(10, 10); 1315 picture->draw(&testCanvas); 1316 1317 // Both clips should be present on playback. 1318 REPORTER_ASSERT(reporter, testCanvas.getClipCount() == 2); 1319} 1320 1321static void test_hierarchical(skiatest::Reporter* reporter) { 1322 SkBitmap bm; 1323 make_bm(&bm, 10, 10, SK_ColorRED, true); 1324 1325 SkPictureRecorder recorder; 1326 1327 recorder.beginRecording(10, 10, NULL, 0); 1328 SkAutoTUnref<SkPicture> childPlain(recorder.endRecording()); 1329 REPORTER_ASSERT(reporter, !childPlain->willPlayBackBitmaps()); // 0 1330 1331 recorder.beginRecording(10, 10, NULL, 0)->drawBitmap(bm, 0, 0); 1332 SkAutoTUnref<SkPicture> childWithBitmap(recorder.endRecording()); 1333 REPORTER_ASSERT(reporter, childWithBitmap->willPlayBackBitmaps()); // 1 1334 1335 { 1336 SkCanvas* canvas = recorder.beginRecording(10, 10, NULL, 0); 1337 canvas->drawPicture(*childPlain); 1338 SkAutoTUnref<SkPicture> parentPP(recorder.endRecording()); 1339 REPORTER_ASSERT(reporter, !parentPP->willPlayBackBitmaps()); // 0 1340 } 1341 { 1342 SkCanvas* canvas = recorder.beginRecording(10, 10, NULL, 0); 1343 canvas->drawPicture(*childWithBitmap); 1344 SkAutoTUnref<SkPicture> parentPWB(recorder.endRecording()); 1345 REPORTER_ASSERT(reporter, parentPWB->willPlayBackBitmaps()); // 1 1346 } 1347 { 1348 SkCanvas* canvas = recorder.beginRecording(10, 10, NULL, 0); 1349 canvas->drawBitmap(bm, 0, 0); 1350 canvas->drawPicture(*childPlain); 1351 SkAutoTUnref<SkPicture> parentWBP(recorder.endRecording()); 1352 REPORTER_ASSERT(reporter, parentWBP->willPlayBackBitmaps()); // 1 1353 } 1354 { 1355 SkCanvas* canvas = recorder.beginRecording(10, 10, NULL, 0); 1356 canvas->drawBitmap(bm, 0, 0); 1357 canvas->drawPicture(*childWithBitmap); 1358 SkAutoTUnref<SkPicture> parentWBWB(recorder.endRecording()); 1359 REPORTER_ASSERT(reporter, parentWBWB->willPlayBackBitmaps()); // 2 1360 } 1361} 1362 1363static void test_gen_id(skiatest::Reporter* reporter) { 1364 1365 SkPicture empty; 1366 1367 // Empty pictures should still have a valid ID 1368 REPORTER_ASSERT(reporter, empty.uniqueID() != SK_InvalidGenID); 1369 1370 SkPictureRecorder recorder; 1371 1372 SkCanvas* canvas = recorder.beginRecording(1, 1, NULL, 0); 1373 canvas->drawARGB(255, 255, 255, 255); 1374 SkAutoTUnref<SkPicture> hasData(recorder.endRecording()); 1375 // picture should have a non-zero id after recording 1376 REPORTER_ASSERT(reporter, hasData->uniqueID() != SK_InvalidGenID); 1377 1378 // both pictures should have different ids 1379 REPORTER_ASSERT(reporter, hasData->uniqueID() != empty.uniqueID()); 1380 1381 // test out copy constructor 1382 SkPicture copyWithData(*hasData); 1383 REPORTER_ASSERT(reporter, hasData->uniqueID() == copyWithData.uniqueID()); 1384 1385 SkPicture emptyCopy(empty); 1386 REPORTER_ASSERT(reporter, empty.uniqueID() != emptyCopy.uniqueID()); 1387 1388 // test out swap 1389 { 1390 SkPicture swapWithData; 1391 uint32_t beforeID1 = swapWithData.uniqueID(); 1392 uint32_t beforeID2 = copyWithData.uniqueID(); 1393 swapWithData.swap(copyWithData); 1394 REPORTER_ASSERT(reporter, copyWithData.uniqueID() == beforeID1); 1395 REPORTER_ASSERT(reporter, swapWithData.uniqueID() == beforeID2); 1396 } 1397 1398 // test out clone 1399 { 1400 SkAutoTUnref<SkPicture> cloneWithData(hasData->clone()); 1401 REPORTER_ASSERT(reporter, hasData->uniqueID() == cloneWithData->uniqueID()); 1402 1403 SkAutoTUnref<SkPicture> emptyClone(empty.clone()); 1404 REPORTER_ASSERT(reporter, empty.uniqueID() != emptyClone->uniqueID()); 1405 } 1406} 1407 1408DEF_TEST(Picture, reporter) { 1409#ifdef SK_DEBUG 1410 test_deleting_empty_playback(); 1411 test_serializing_empty_picture(); 1412#else 1413 test_bad_bitmap(); 1414#endif 1415 test_unbalanced_save_restores(reporter); 1416 test_peephole(); 1417#if SK_SUPPORT_GPU 1418 test_gpu_veto(reporter); 1419#endif 1420 test_gatherpixelrefs(reporter); 1421 test_gatherpixelrefsandrects(reporter); 1422 test_bitmap_with_encoded_data(reporter); 1423 test_clone_empty(reporter); 1424 test_draw_empty(reporter); 1425 test_clip_bound_opt(reporter); 1426 test_clip_expansion(reporter); 1427 test_hierarchical(reporter); 1428 test_gen_id(reporter); 1429} 1430 1431#if SK_SUPPORT_GPU 1432DEF_GPUTEST(GPUPicture, reporter, factory) { 1433 test_gpu_picture_optimization(reporter, factory); 1434} 1435#endif 1436 1437static void draw_bitmaps(const SkBitmap bitmap, SkCanvas* canvas) { 1438 const SkPaint paint; 1439 const SkRect rect = { 5.0f, 5.0f, 8.0f, 8.0f }; 1440 const SkIRect irect = { 2, 2, 3, 3 }; 1441 1442 // Don't care what these record, as long as they're legal. 1443 canvas->drawBitmap(bitmap, 0.0f, 0.0f, &paint); 1444 canvas->drawBitmapRectToRect(bitmap, &rect, rect, &paint, SkCanvas::kNone_DrawBitmapRectFlag); 1445 canvas->drawBitmapMatrix(bitmap, SkMatrix::I(), &paint); 1446 canvas->drawBitmapNine(bitmap, irect, rect, &paint); 1447 canvas->drawSprite(bitmap, 1, 1); 1448} 1449 1450static void test_draw_bitmaps(SkCanvas* canvas) { 1451 SkBitmap empty; 1452 draw_bitmaps(empty, canvas); 1453 empty.setConfig(SkImageInfo::MakeN32Premul(10, 10)); 1454 draw_bitmaps(empty, canvas); 1455} 1456 1457DEF_TEST(Picture_EmptyBitmap, r) { 1458 SkPictureRecorder recorder; 1459 test_draw_bitmaps(recorder.beginRecording(10, 10, NULL, 0)); 1460 SkAutoTUnref<SkPicture> picture(recorder.endRecording()); 1461} 1462 1463DEF_TEST(Canvas_EmptyBitmap, r) { 1464 SkBitmap dst; 1465 dst.allocN32Pixels(10, 10); 1466 SkCanvas canvas(dst); 1467 1468 test_draw_bitmaps(&canvas); 1469} 1470