PictureTest.cpp revision 533eb782edaa0b6fece6166d3001edf72ec39f11
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 "SkBBoxHierarchy.h" 9#include "SkBlurImageFilter.h" 10#include "SkCanvas.h" 11#include "SkColorPriv.h" 12#include "SkDashPathEffect.h" 13#include "SkData.h" 14#include "SkDecodingImageGenerator.h" 15#include "SkError.h" 16#include "SkImageEncoder.h" 17#include "SkImageGenerator.h" 18#include "SkPaint.h" 19#include "SkPicture.h" 20#include "SkPictureRecorder.h" 21#include "SkPictureUtils.h" 22#include "SkRRect.h" 23#include "SkRandom.h" 24#include "SkShader.h" 25#include "SkStream.h" 26 27#if SK_SUPPORT_GPU 28#include "SkSurface.h" 29#include "GrContextFactory.h" 30#include "GrPictureUtils.h" 31#endif 32#include "Test.h" 33 34#include "SkLumaColorFilter.h" 35#include "SkColorFilterImageFilter.h" 36 37static const int gColorScale = 30; 38static const int gColorOffset = 60; 39 40static void make_bm(SkBitmap* bm, int w, int h, SkColor color, bool immutable) { 41 bm->allocN32Pixels(w, h); 42 bm->eraseColor(color); 43 if (immutable) { 44 bm->setImmutable(); 45 } 46} 47 48static void make_checkerboard(SkBitmap* bm, int w, int h, bool immutable) { 49 SkASSERT(w % 2 == 0); 50 SkASSERT(h % 2 == 0); 51 bm->allocPixels(SkImageInfo::Make(w, h, kAlpha_8_SkColorType, 52 kPremul_SkAlphaType)); 53 SkAutoLockPixels lock(*bm); 54 for (int y = 0; y < h; y += 2) { 55 uint8_t* s = bm->getAddr8(0, y); 56 for (int x = 0; x < w; x += 2) { 57 *s++ = 0xFF; 58 *s++ = 0x00; 59 } 60 s = bm->getAddr8(0, y + 1); 61 for (int x = 0; x < w; x += 2) { 62 *s++ = 0x00; 63 *s++ = 0xFF; 64 } 65 } 66 if (immutable) { 67 bm->setImmutable(); 68 } 69} 70 71static void init_paint(SkPaint* paint, const SkBitmap &bm) { 72 SkShader* shader = SkShader::CreateBitmapShader(bm, 73 SkShader::kClamp_TileMode, 74 SkShader::kClamp_TileMode); 75 paint->setShader(shader)->unref(); 76} 77 78typedef void (*DrawBitmapProc)(SkCanvas*, const SkBitmap&, 79 const SkBitmap&, const SkPoint&, 80 SkTDArray<SkPixelRef*>* usedPixRefs); 81 82static void drawpaint_proc(SkCanvas* canvas, const SkBitmap& bm, 83 const SkBitmap& altBM, const SkPoint& pos, 84 SkTDArray<SkPixelRef*>* usedPixRefs) { 85 SkPaint paint; 86 init_paint(&paint, bm); 87 88 canvas->drawPaint(paint); 89 *usedPixRefs->append() = bm.pixelRef(); 90} 91 92static void drawpoints_proc(SkCanvas* canvas, const SkBitmap& bm, 93 const SkBitmap& altBM, const SkPoint& pos, 94 SkTDArray<SkPixelRef*>* usedPixRefs) { 95 SkPaint paint; 96 init_paint(&paint, bm); 97 98 // draw a rect 99 SkPoint points[5] = { 100 { pos.fX, pos.fY }, 101 { pos.fX + bm.width() - 1, pos.fY }, 102 { pos.fX + bm.width() - 1, pos.fY + bm.height() - 1 }, 103 { pos.fX, pos.fY + bm.height() - 1 }, 104 { pos.fX, pos.fY }, 105 }; 106 107 canvas->drawPoints(SkCanvas::kPolygon_PointMode, 5, points, paint); 108 *usedPixRefs->append() = bm.pixelRef(); 109} 110 111static void drawrect_proc(SkCanvas* canvas, const SkBitmap& bm, 112 const SkBitmap& altBM, const SkPoint& pos, 113 SkTDArray<SkPixelRef*>* usedPixRefs) { 114 SkPaint paint; 115 init_paint(&paint, bm); 116 117 SkRect r = { 0, 0, SkIntToScalar(bm.width()), SkIntToScalar(bm.height()) }; 118 r.offset(pos.fX, pos.fY); 119 120 canvas->drawRect(r, paint); 121 *usedPixRefs->append() = bm.pixelRef(); 122} 123 124static void drawoval_proc(SkCanvas* canvas, const SkBitmap& bm, 125 const SkBitmap& altBM, const SkPoint& pos, 126 SkTDArray<SkPixelRef*>* usedPixRefs) { 127 SkPaint paint; 128 init_paint(&paint, bm); 129 130 SkRect r = { 0, 0, SkIntToScalar(bm.width()), SkIntToScalar(bm.height()) }; 131 r.offset(pos.fX, pos.fY); 132 133 canvas->drawOval(r, paint); 134 *usedPixRefs->append() = bm.pixelRef(); 135} 136 137static void drawrrect_proc(SkCanvas* canvas, const SkBitmap& bm, 138 const SkBitmap& altBM, const SkPoint& pos, 139 SkTDArray<SkPixelRef*>* usedPixRefs) { 140 SkPaint paint; 141 init_paint(&paint, bm); 142 143 SkRect r = { 0, 0, SkIntToScalar(bm.width()), SkIntToScalar(bm.height()) }; 144 r.offset(pos.fX, pos.fY); 145 146 SkRRect rr; 147 rr.setRectXY(r, SkIntToScalar(bm.width())/4, SkIntToScalar(bm.height())/4); 148 canvas->drawRRect(rr, paint); 149 *usedPixRefs->append() = bm.pixelRef(); 150} 151 152static void drawpath_proc(SkCanvas* canvas, const SkBitmap& bm, 153 const SkBitmap& altBM, const SkPoint& pos, 154 SkTDArray<SkPixelRef*>* usedPixRefs) { 155 SkPaint paint; 156 init_paint(&paint, bm); 157 158 SkPath path; 159 path.lineTo(bm.width()/2.0f, SkIntToScalar(bm.height())); 160 path.lineTo(SkIntToScalar(bm.width()), 0); 161 path.close(); 162 path.offset(pos.fX, pos.fY); 163 164 canvas->drawPath(path, paint); 165 *usedPixRefs->append() = bm.pixelRef(); 166} 167 168static void drawbitmap_proc(SkCanvas* canvas, const SkBitmap& bm, 169 const SkBitmap& altBM, const SkPoint& pos, 170 SkTDArray<SkPixelRef*>* usedPixRefs) { 171 canvas->drawBitmap(bm, pos.fX, pos.fY, NULL); 172 *usedPixRefs->append() = bm.pixelRef(); 173} 174 175static void drawbitmap_withshader_proc(SkCanvas* canvas, const SkBitmap& bm, 176 const SkBitmap& altBM, const SkPoint& pos, 177 SkTDArray<SkPixelRef*>* usedPixRefs) { 178 SkPaint paint; 179 init_paint(&paint, bm); 180 181 // The bitmap in the paint is ignored unless we're drawing an A8 bitmap 182 canvas->drawBitmap(altBM, pos.fX, pos.fY, &paint); 183 *usedPixRefs->append() = bm.pixelRef(); 184 *usedPixRefs->append() = altBM.pixelRef(); 185} 186 187static void drawsprite_proc(SkCanvas* canvas, const SkBitmap& bm, 188 const SkBitmap& altBM, const SkPoint& pos, 189 SkTDArray<SkPixelRef*>* usedPixRefs) { 190 const SkMatrix& ctm = canvas->getTotalMatrix(); 191 192 SkPoint p(pos); 193 ctm.mapPoints(&p, 1); 194 195 canvas->drawSprite(bm, (int)p.fX, (int)p.fY, NULL); 196 *usedPixRefs->append() = bm.pixelRef(); 197} 198 199#if 0 200// Although specifiable, this case doesn't seem to make sense (i.e., the 201// bitmap in the shader is never used). 202static void drawsprite_withshader_proc(SkCanvas* canvas, const SkBitmap& bm, 203 const SkBitmap& altBM, const SkPoint& pos, 204 SkTDArray<SkPixelRef*>* usedPixRefs) { 205 SkPaint paint; 206 init_paint(&paint, bm); 207 208 const SkMatrix& ctm = canvas->getTotalMatrix(); 209 210 SkPoint p(pos); 211 ctm.mapPoints(&p, 1); 212 213 canvas->drawSprite(altBM, (int)p.fX, (int)p.fY, &paint); 214 *usedPixRefs->append() = bm.pixelRef(); 215 *usedPixRefs->append() = altBM.pixelRef(); 216} 217#endif 218 219static void drawbitmaprect_proc(SkCanvas* canvas, const SkBitmap& bm, 220 const SkBitmap& altBM, const SkPoint& pos, 221 SkTDArray<SkPixelRef*>* usedPixRefs) { 222 SkRect r = { 0, 0, SkIntToScalar(bm.width()), SkIntToScalar(bm.height()) }; 223 224 r.offset(pos.fX, pos.fY); 225 canvas->drawBitmapRectToRect(bm, NULL, r, NULL); 226 *usedPixRefs->append() = bm.pixelRef(); 227} 228 229static void drawbitmaprect_withshader_proc(SkCanvas* canvas, 230 const SkBitmap& bm, 231 const SkBitmap& altBM, 232 const SkPoint& pos, 233 SkTDArray<SkPixelRef*>* usedPixRefs) { 234 SkPaint paint; 235 init_paint(&paint, bm); 236 237 SkRect r = { 0, 0, SkIntToScalar(bm.width()), SkIntToScalar(bm.height()) }; 238 r.offset(pos.fX, pos.fY); 239 240 // The bitmap in the paint is ignored unless we're drawing an A8 bitmap 241 canvas->drawBitmapRectToRect(altBM, NULL, r, &paint); 242 *usedPixRefs->append() = bm.pixelRef(); 243 *usedPixRefs->append() = altBM.pixelRef(); 244} 245 246static void drawtext_proc(SkCanvas* canvas, const SkBitmap& bm, 247 const SkBitmap& altBM, const SkPoint& pos, 248 SkTDArray<SkPixelRef*>* usedPixRefs) { 249 SkPaint paint; 250 init_paint(&paint, bm); 251 paint.setTextSize(SkIntToScalar(1.5*bm.width())); 252 253 canvas->drawText("0", 1, pos.fX, pos.fY+bm.width(), paint); 254 *usedPixRefs->append() = bm.pixelRef(); 255} 256 257static void drawpostext_proc(SkCanvas* canvas, const SkBitmap& bm, 258 const SkBitmap& altBM, const SkPoint& pos, 259 SkTDArray<SkPixelRef*>* usedPixRefs) { 260 SkPaint paint; 261 init_paint(&paint, bm); 262 paint.setTextSize(SkIntToScalar(1.5*bm.width())); 263 264 SkPoint point = { pos.fX, pos.fY + bm.height() }; 265 canvas->drawPosText("O", 1, &point, paint); 266 *usedPixRefs->append() = bm.pixelRef(); 267} 268 269static void drawtextonpath_proc(SkCanvas* canvas, const SkBitmap& bm, 270 const SkBitmap& altBM, const SkPoint& pos, 271 SkTDArray<SkPixelRef*>* usedPixRefs) { 272 SkPaint paint; 273 274 init_paint(&paint, bm); 275 paint.setTextSize(SkIntToScalar(1.5*bm.width())); 276 277 SkPath path; 278 path.lineTo(SkIntToScalar(bm.width()), 0); 279 path.offset(pos.fX, pos.fY+bm.height()); 280 281 canvas->drawTextOnPath("O", 1, path, NULL, paint); 282 *usedPixRefs->append() = bm.pixelRef(); 283} 284 285static void drawverts_proc(SkCanvas* canvas, const SkBitmap& bm, 286 const SkBitmap& altBM, const SkPoint& pos, 287 SkTDArray<SkPixelRef*>* usedPixRefs) { 288 SkPaint paint; 289 init_paint(&paint, bm); 290 291 SkPoint verts[4] = { 292 { pos.fX, pos.fY }, 293 { pos.fX + bm.width(), pos.fY }, 294 { pos.fX + bm.width(), pos.fY + bm.height() }, 295 { pos.fX, pos.fY + bm.height() } 296 }; 297 SkPoint texs[4] = { { 0, 0 }, 298 { SkIntToScalar(bm.width()), 0 }, 299 { SkIntToScalar(bm.width()), SkIntToScalar(bm.height()) }, 300 { 0, SkIntToScalar(bm.height()) } }; 301 uint16_t indices[6] = { 0, 1, 2, 0, 2, 3 }; 302 303 canvas->drawVertices(SkCanvas::kTriangles_VertexMode, 4, verts, texs, NULL, NULL, 304 indices, 6, paint); 305 *usedPixRefs->append() = bm.pixelRef(); 306} 307 308// Return a picture with the bitmaps drawn at the specified positions. 309static SkPicture* record_bitmaps(const SkBitmap bm[], 310 const SkPoint pos[], 311 SkTDArray<SkPixelRef*> analytic[], 312 int count, 313 DrawBitmapProc proc) { 314 SkPictureRecorder recorder; 315 SkCanvas* canvas = recorder.beginRecording(1000, 1000); 316 for (int i = 0; i < count; ++i) { 317 analytic[i].rewind(); 318 canvas->save(); 319 SkRect clipRect = SkRect::MakeXYWH(pos[i].fX, pos[i].fY, 320 SkIntToScalar(bm[i].width()), 321 SkIntToScalar(bm[i].height())); 322 canvas->clipRect(clipRect, SkRegion::kIntersect_Op); 323 proc(canvas, bm[i], bm[count+i], pos[i], &analytic[i]); 324 canvas->restore(); 325 } 326 return recorder.endRecording(); 327} 328 329static void rand_rect(SkRect* rect, SkRandom& rand, SkScalar W, SkScalar H) { 330 rect->fLeft = rand.nextRangeScalar(-W, 2*W); 331 rect->fTop = rand.nextRangeScalar(-H, 2*H); 332 rect->fRight = rect->fLeft + rand.nextRangeScalar(0, W); 333 rect->fBottom = rect->fTop + rand.nextRangeScalar(0, H); 334 335 // we integralize rect to make our tests more predictable, since Gather is 336 // a little sloppy. 337 SkIRect ir; 338 rect->round(&ir); 339 rect->set(ir); 340} 341 342static void draw(SkPicture* pic, int width, int height, SkBitmap* result) { 343 make_bm(result, width, height, SK_ColorBLACK, false); 344 345 SkCanvas canvas(*result); 346 canvas.drawPicture(pic); 347} 348 349template <typename T> int find_index(const T* array, T elem, int count) { 350 for (int i = 0; i < count; ++i) { 351 if (array[i] == elem) { 352 return i; 353 } 354 } 355 return -1; 356} 357 358// Return true if 'ref' is found in array[] 359static bool find(SkPixelRef const * const * array, SkPixelRef const * ref, int count) { 360 return find_index<const SkPixelRef*>(array, ref, count) >= 0; 361} 362 363// Look at each pixel that is inside 'subset', and if its color appears in 364// colors[], find the corresponding value in refs[] and append that ref into 365// array, skipping duplicates of the same value. 366// Note that gathering pixelRefs from rendered colors suffers from the problem 367// that multiple simultaneous textures (e.g., A8 for alpha and 8888 for color) 368// isn't easy to reconstruct. 369static void gather_from_image(const SkBitmap& bm, SkPixelRef* const refs[], 370 int count, SkTDArray<SkPixelRef*>* array, 371 const SkRect& subset) { 372 SkIRect ir; 373 subset.roundOut(&ir); 374 375 if (!ir.intersect(0, 0, bm.width()-1, bm.height()-1)) { 376 return; 377 } 378 379 // Since we only want to return unique values in array, when we scan we just 380 // set a bit for each index'd color found. In practice we only have a few 381 // distinct colors, so we just use an int's bits as our array. Hence the 382 // assert that count <= number-of-bits-in-our-int. 383 SkASSERT((unsigned)count <= 32); 384 uint32_t bitarray = 0; 385 386 SkAutoLockPixels alp(bm); 387 388 for (int y = ir.fTop; y < ir.fBottom; ++y) { 389 for (int x = ir.fLeft; x < ir.fRight; ++x) { 390 SkPMColor pmc = *bm.getAddr32(x, y); 391 // the only good case where the color is not found would be if 392 // the color is transparent, meaning no bitmap was drawn in that 393 // pixel. 394 if (pmc) { 395 uint32_t index = SkGetPackedR32(pmc); 396 SkASSERT(SkGetPackedG32(pmc) == index); 397 SkASSERT(SkGetPackedB32(pmc) == index); 398 if (0 == index) { 399 continue; // background color 400 } 401 SkASSERT(0 == (index - gColorOffset) % gColorScale); 402 index = (index - gColorOffset) / gColorScale; 403 SkASSERT(static_cast<int>(index) < count); 404 bitarray |= 1 << index; 405 } 406 } 407 } 408 409 for (int i = 0; i < count; ++i) { 410 if (bitarray & (1 << i)) { 411 *array->append() = refs[i]; 412 } 413 } 414} 415 416static void gather_from_analytic(const SkPoint pos[], SkScalar w, SkScalar h, 417 const SkTDArray<SkPixelRef*> analytic[], 418 int count, 419 SkTDArray<SkPixelRef*>* result, 420 const SkRect& subset) { 421 for (int i = 0; i < count; ++i) { 422 SkRect rect = SkRect::MakeXYWH(pos[i].fX, pos[i].fY, w, h); 423 424 if (SkRect::Intersects(subset, rect)) { 425 result->append(analytic[i].count(), analytic[i].begin()); 426 } 427 } 428} 429 430 431static const struct { 432 const DrawBitmapProc proc; 433 const char* const desc; 434} gProcs[] = { 435 {drawpaint_proc, "drawpaint"}, 436 {drawpoints_proc, "drawpoints"}, 437 {drawrect_proc, "drawrect"}, 438 {drawoval_proc, "drawoval"}, 439 {drawrrect_proc, "drawrrect"}, 440 {drawpath_proc, "drawpath"}, 441 {drawbitmap_proc, "drawbitmap"}, 442 {drawbitmap_withshader_proc, "drawbitmap_withshader"}, 443 {drawsprite_proc, "drawsprite"}, 444#if 0 445 {drawsprite_withshader_proc, "drawsprite_withshader"}, 446#endif 447 {drawbitmaprect_proc, "drawbitmaprect"}, 448 {drawbitmaprect_withshader_proc, "drawbitmaprect_withshader"}, 449 {drawtext_proc, "drawtext"}, 450 {drawpostext_proc, "drawpostext"}, 451 {drawtextonpath_proc, "drawtextonpath"}, 452 {drawverts_proc, "drawverts"}, 453}; 454 455static void create_textures(SkBitmap* bm, SkPixelRef** refs, int num, int w, int h) { 456 // Our convention is that the color components contain an encoding of 457 // the index of their corresponding bitmap/pixelref. (0,0,0,0) is 458 // reserved for the background 459 for (int i = 0; i < num; ++i) { 460 make_bm(&bm[i], w, h, 461 SkColorSetARGB(0xFF, 462 gColorScale*i+gColorOffset, 463 gColorScale*i+gColorOffset, 464 gColorScale*i+gColorOffset), 465 true); 466 refs[i] = bm[i].pixelRef(); 467 } 468 469 // The A8 alternate bitmaps are all BW checkerboards 470 for (int i = 0; i < num; ++i) { 471 make_checkerboard(&bm[num+i], w, h, true); 472 refs[num+i] = bm[num+i].pixelRef(); 473 } 474} 475 476static void test_gatherpixelrefs(skiatest::Reporter* reporter) { 477 const int IW = 32; 478 const int IH = IW; 479 const SkScalar W = SkIntToScalar(IW); 480 const SkScalar H = W; 481 482 static const int N = 4; 483 SkBitmap bm[2*N]; 484 SkPixelRef* refs[2*N]; 485 SkTDArray<SkPixelRef*> analytic[N]; 486 487 const SkPoint pos[N] = { 488 { 0, 0 }, { W, 0 }, { 0, H }, { W, H } 489 }; 490 491 create_textures(bm, refs, N, IW, IH); 492 493 SkRandom rand; 494 for (size_t k = 0; k < SK_ARRAY_COUNT(gProcs); ++k) { 495 SkAutoTUnref<SkPicture> pic( 496 record_bitmaps(bm, pos, analytic, N, gProcs[k].proc)); 497 498 REPORTER_ASSERT(reporter, pic->willPlayBackBitmaps() || N == 0); 499 // quick check for a small piece of each quadrant, which should just 500 // contain 1 or 2 bitmaps. 501 for (size_t i = 0; i < SK_ARRAY_COUNT(pos); ++i) { 502 SkRect r; 503 r.set(2, 2, W - 2, H - 2); 504 r.offset(pos[i].fX, pos[i].fY); 505 SkAutoDataUnref data(SkPictureUtils::GatherPixelRefs(pic, r)); 506 if (!data) { 507 ERRORF(reporter, "SkPictureUtils::GatherPixelRefs returned " 508 "NULL for %s.", gProcs[k].desc); 509 continue; 510 } 511 SkPixelRef** gatheredRefs = (SkPixelRef**)data->data(); 512 int count = static_cast<int>(data->size() / sizeof(SkPixelRef*)); 513 REPORTER_ASSERT(reporter, 1 == count || 2 == count); 514 if (1 == count) { 515 REPORTER_ASSERT(reporter, gatheredRefs[0] == refs[i]); 516 } else if (2 == count) { 517 REPORTER_ASSERT(reporter, 518 (gatheredRefs[0] == refs[i] && gatheredRefs[1] == refs[i+N]) || 519 (gatheredRefs[1] == refs[i] && gatheredRefs[0] == refs[i+N])); 520 } 521 } 522 523 SkBitmap image; 524 draw(pic, 2*IW, 2*IH, &image); 525 526 // Test a bunch of random (mostly) rects, and compare the gather results 527 // with a deduced list of refs by looking at the colors drawn. 528 for (int j = 0; j < 100; ++j) { 529 SkRect r; 530 rand_rect(&r, rand, 2*W, 2*H); 531 532 SkTDArray<SkPixelRef*> fromImage; 533 gather_from_image(image, refs, N, &fromImage, r); 534 535 SkTDArray<SkPixelRef*> fromAnalytic; 536 gather_from_analytic(pos, W, H, analytic, N, &fromAnalytic, r); 537 538 SkData* data = SkPictureUtils::GatherPixelRefs(pic, r); 539 size_t dataSize = data ? data->size() : 0; 540 int gatherCount = static_cast<int>(dataSize / sizeof(SkPixelRef*)); 541 SkASSERT(gatherCount * sizeof(SkPixelRef*) == dataSize); 542 SkPixelRef** gatherRefs = data ? (SkPixelRef**)(data->data()) : NULL; 543 SkAutoDataUnref adu(data); 544 545 // Everything that we saw drawn should appear in the analytic list 546 // but the analytic list may contain some pixelRefs that were not 547 // seen in the image (e.g., A8 textures used as masks) 548 for (int i = 0; i < fromImage.count(); ++i) { 549 if (-1 == fromAnalytic.find(fromImage[i])) { 550 ERRORF(reporter, "PixelRef missing %d %s", 551 i, gProcs[k].desc); 552 } 553 } 554 555 /* 556 * GatherPixelRefs is conservative, so it can return more bitmaps 557 * than are strictly required. Thus our check here is only that 558 * Gather didn't miss any that we actually needed. Even that isn't 559 * a strict requirement on Gather, which is meant to be quick and 560 * only mostly-correct, but at the moment this test should work. 561 */ 562 for (int i = 0; i < fromAnalytic.count(); ++i) { 563 bool found = find(gatherRefs, fromAnalytic[i], gatherCount); 564 if (!found) { 565 ERRORF(reporter, "PixelRef missing %d %s", 566 i, gProcs[k].desc); 567 } 568#if 0 569 // enable this block of code to debug failures, as it will rerun 570 // the case that failed. 571 if (!found) { 572 SkData* data = SkPictureUtils::GatherPixelRefs(pic, r); 573 size_t dataSize = data ? data->size() : 0; 574 } 575#endif 576 } 577 } 578 } 579} 580 581#define GENERATE_CANVAS(recorder, x) \ 582 (x) ? recorder.EXPERIMENTAL_beginRecording(100, 100) \ 583 : recorder. DEPRECATED_beginRecording(100,100); 584 585/* Hit a few SkPicture::Analysis cases not handled elsewhere. */ 586static void test_analysis(skiatest::Reporter* reporter, bool useNewPath) { 587 SkPictureRecorder recorder; 588 589 SkCanvas* canvas = GENERATE_CANVAS(recorder, useNewPath); 590 { 591 canvas->drawRect(SkRect::MakeWH(10, 10), SkPaint ()); 592 } 593 SkAutoTUnref<SkPicture> picture(recorder.endRecording()); 594 REPORTER_ASSERT(reporter, !picture->willPlayBackBitmaps()); 595 596 canvas = GENERATE_CANVAS(recorder, useNewPath); 597 { 598 SkPaint paint; 599 // CreateBitmapShader is too smart for us; an empty (or 1x1) bitmap shader 600 // gets optimized into a non-bitmap form, so we create a 2x2 bitmap here. 601 SkBitmap bitmap; 602 bitmap.allocPixels(SkImageInfo::MakeN32Premul(2, 2)); 603 bitmap.eraseColor(SK_ColorBLUE); 604 *(bitmap.getAddr32(0, 0)) = SK_ColorGREEN; 605 SkShader* shader = SkShader::CreateBitmapShader(bitmap, SkShader::kClamp_TileMode, 606 SkShader::kClamp_TileMode); 607 paint.setShader(shader)->unref(); 608 REPORTER_ASSERT(reporter, 609 shader->asABitmap(NULL, NULL, NULL) == SkShader::kDefault_BitmapType); 610 611 canvas->drawRect(SkRect::MakeWH(10, 10), paint); 612 } 613 picture.reset(recorder.endRecording()); 614 REPORTER_ASSERT(reporter, picture->willPlayBackBitmaps()); 615} 616 617 618static void test_gatherpixelrefsandrects(skiatest::Reporter* reporter) { 619 const int IW = 32; 620 const int IH = IW; 621 const SkScalar W = SkIntToScalar(IW); 622 const SkScalar H = W; 623 624 static const int N = 4; 625 SkBitmap bm[2*N]; 626 SkPixelRef* refs[2*N]; 627 SkTDArray<SkPixelRef*> analytic[N]; 628 629 const SkPoint pos[N] = { 630 { 0, 0 }, { W, 0 }, { 0, H }, { W, H } 631 }; 632 633 create_textures(bm, refs, N, IW, IH); 634 635 SkRandom rand; 636 for (size_t k = 0; k < SK_ARRAY_COUNT(gProcs); ++k) { 637 SkAutoTUnref<SkPicture> pic( 638 record_bitmaps(bm, pos, analytic, N, gProcs[k].proc)); 639 640 REPORTER_ASSERT(reporter, pic->willPlayBackBitmaps() || N == 0); 641 642 SkAutoTUnref<SkPictureUtils::SkPixelRefContainer> prCont( 643 new SkPictureUtils::SkPixelRefsAndRectsList); 644 645 SkPictureUtils::GatherPixelRefsAndRects(pic, prCont); 646 647 // quick check for a small piece of each quadrant, which should just 648 // contain 1 or 2 bitmaps. 649 for (size_t i = 0; i < SK_ARRAY_COUNT(pos); ++i) { 650 SkRect r; 651 r.set(2, 2, W - 2, H - 2); 652 r.offset(pos[i].fX, pos[i].fY); 653 654 SkTDArray<SkPixelRef*> gatheredRefs; 655 prCont->query(r, &gatheredRefs); 656 657 int count = gatheredRefs.count(); 658 REPORTER_ASSERT(reporter, 1 == count || 2 == count); 659 if (1 == count) { 660 REPORTER_ASSERT(reporter, gatheredRefs[0] == refs[i]); 661 } else if (2 == count) { 662 REPORTER_ASSERT(reporter, 663 (gatheredRefs[0] == refs[i] && gatheredRefs[1] == refs[i+N]) || 664 (gatheredRefs[1] == refs[i] && gatheredRefs[0] == refs[i+N])); 665 } 666 } 667 668 SkBitmap image; 669 draw(pic, 2*IW, 2*IH, &image); 670 671 // Test a bunch of random (mostly) rects, and compare the gather results 672 // with the analytic results and the pixel refs seen in a rendering. 673 for (int j = 0; j < 100; ++j) { 674 SkRect r; 675 rand_rect(&r, rand, 2*W, 2*H); 676 677 SkTDArray<SkPixelRef*> fromImage; 678 gather_from_image(image, refs, N, &fromImage, r); 679 680 SkTDArray<SkPixelRef*> fromAnalytic; 681 gather_from_analytic(pos, W, H, analytic, N, &fromAnalytic, r); 682 683 SkTDArray<SkPixelRef*> gatheredRefs; 684 prCont->query(r, &gatheredRefs); 685 686 // Everything that we saw drawn should appear in the analytic list 687 // but the analytic list may contain some pixelRefs that were not 688 // seen in the image (e.g., A8 textures used as masks) 689 for (int i = 0; i < fromImage.count(); ++i) { 690 REPORTER_ASSERT(reporter, -1 != fromAnalytic.find(fromImage[i])); 691 } 692 693 // Everything in the analytic list should appear in the gathered 694 // list. 695 for (int i = 0; i < fromAnalytic.count(); ++i) { 696 REPORTER_ASSERT(reporter, -1 != gatheredRefs.find(fromAnalytic[i])); 697 } 698 } 699 } 700} 701 702#ifdef SK_DEBUG 703// Ensure that deleting an empty SkPicture does not assert. Asserts only fire 704// in debug mode, so only run in debug mode. 705static void test_deleting_empty_picture() { 706 SkPictureRecorder recorder; 707 // Creates an SkPictureRecord 708 recorder.beginRecording(0, 0); 709 // Turns that into an SkPicture 710 SkAutoTUnref<SkPicture> picture(recorder.endRecording()); 711 // Ceates a new SkPictureRecord 712 recorder.beginRecording(0, 0); 713} 714 715// Ensure that serializing an empty picture does not assert. Likewise only runs in debug mode. 716static void test_serializing_empty_picture() { 717 SkPictureRecorder recorder; 718 recorder.beginRecording(0, 0); 719 SkAutoTUnref<SkPicture> picture(recorder.endRecording()); 720 SkDynamicMemoryWStream stream; 721 picture->serialize(&stream); 722} 723#endif 724 725static void rand_op(SkCanvas* canvas, SkRandom& rand) { 726 SkPaint paint; 727 SkRect rect = SkRect::MakeWH(50, 50); 728 729 SkScalar unit = rand.nextUScalar1(); 730 if (unit <= 0.3) { 731// SkDebugf("save\n"); 732 canvas->save(); 733 } else if (unit <= 0.6) { 734// SkDebugf("restore\n"); 735 canvas->restore(); 736 } else if (unit <= 0.9) { 737// SkDebugf("clip\n"); 738 canvas->clipRect(rect); 739 } else { 740// SkDebugf("draw\n"); 741 canvas->drawPaint(paint); 742 } 743} 744 745#if SK_SUPPORT_GPU 746 747static void test_gpu_veto(skiatest::Reporter* reporter, 748 bool useNewPath) { 749 750 SkPictureRecorder recorder; 751 752 SkCanvas* canvas = GENERATE_CANVAS(recorder, useNewPath); 753 { 754 SkPath path; 755 path.moveTo(0, 0); 756 path.lineTo(50, 50); 757 758 SkScalar intervals[] = { 1.0f, 1.0f }; 759 SkAutoTUnref<SkDashPathEffect> dash(SkDashPathEffect::Create(intervals, 2, 0)); 760 761 SkPaint paint; 762 paint.setStyle(SkPaint::kStroke_Style); 763 paint.setPathEffect(dash); 764 765 canvas->drawPath(path, paint); 766 } 767 SkAutoTUnref<SkPicture> picture(recorder.endRecording()); 768 // path effects currently render an SkPicture undesireable for GPU rendering 769 770 const char *reason = NULL; 771 REPORTER_ASSERT(reporter, !picture->suitableForGpuRasterization(NULL, &reason)); 772 REPORTER_ASSERT(reporter, NULL != reason); 773 774 canvas = GENERATE_CANVAS(recorder, useNewPath); 775 { 776 SkPath path; 777 778 path.moveTo(0, 0); 779 path.lineTo(0, 50); 780 path.lineTo(25, 25); 781 path.lineTo(50, 50); 782 path.lineTo(50, 0); 783 path.close(); 784 REPORTER_ASSERT(reporter, !path.isConvex()); 785 786 SkPaint paint; 787 paint.setAntiAlias(true); 788 for (int i = 0; i < 50; ++i) { 789 canvas->drawPath(path, paint); 790 } 791 } 792 picture.reset(recorder.endRecording()); 793 // A lot of AA concave paths currently render an SkPicture undesireable for GPU rendering 794 REPORTER_ASSERT(reporter, !picture->suitableForGpuRasterization(NULL)); 795 796 canvas = GENERATE_CANVAS(recorder, useNewPath); 797 { 798 SkPath path; 799 800 path.moveTo(0, 0); 801 path.lineTo(0, 50); 802 path.lineTo(25, 25); 803 path.lineTo(50, 50); 804 path.lineTo(50, 0); 805 path.close(); 806 REPORTER_ASSERT(reporter, !path.isConvex()); 807 808 SkPaint paint; 809 paint.setAntiAlias(true); 810 paint.setStyle(SkPaint::kStroke_Style); 811 paint.setStrokeWidth(0); 812 for (int i = 0; i < 50; ++i) { 813 canvas->drawPath(path, paint); 814 } 815 } 816 picture.reset(recorder.endRecording()); 817 // hairline stroked AA concave paths are fine for GPU rendering 818 REPORTER_ASSERT(reporter, picture->suitableForGpuRasterization(NULL)); 819 820 canvas = GENERATE_CANVAS(recorder, useNewPath); 821 { 822 SkPaint paint; 823 SkScalar intervals [] = { 10, 20 }; 824 SkPathEffect* pe = SkDashPathEffect::Create(intervals, 2, 25); 825 paint.setPathEffect(pe)->unref(); 826 827 SkPoint points [2] = { { 0, 0 }, { 100, 0 } }; 828 canvas->drawPoints(SkCanvas::kLines_PointMode, 2, points, paint); 829 } 830 picture.reset(recorder.endRecording()); 831 // fast-path dashed effects are fine for GPU rendering ... 832 REPORTER_ASSERT(reporter, picture->suitableForGpuRasterization(NULL)); 833 834 canvas = GENERATE_CANVAS(recorder, useNewPath); 835 { 836 SkPaint paint; 837 SkScalar intervals [] = { 10, 20 }; 838 SkPathEffect* pe = SkDashPathEffect::Create(intervals, 2, 25); 839 paint.setPathEffect(pe)->unref(); 840 841 canvas->drawRect(SkRect::MakeWH(10, 10), paint); 842 } 843 picture.reset(recorder.endRecording()); 844 // ... but only when applied to drawPoint() calls 845 REPORTER_ASSERT(reporter, !picture->suitableForGpuRasterization(NULL)); 846 847 // Nest the previous picture inside a new one. 848 // This doesn't work in the old backend. 849 if (useNewPath) { 850 canvas = GENERATE_CANVAS(recorder, useNewPath); 851 { 852 canvas->drawPicture(picture.get()); 853 } 854 picture.reset(recorder.endRecording()); 855 REPORTER_ASSERT(reporter, !picture->suitableForGpuRasterization(NULL)); 856 } 857} 858 859#undef GENERATE_CANVAS 860 861static void test_gpu_picture_optimization(skiatest::Reporter* reporter, 862 GrContextFactory* factory) { 863 for (int i= 0; i < GrContextFactory::kGLContextTypeCnt; ++i) { 864 GrContextFactory::GLContextType glCtxType = (GrContextFactory::GLContextType) i; 865 866 if (!GrContextFactory::IsRenderingGLContext(glCtxType)) { 867 continue; 868 } 869 870 GrContext* context = factory->get(glCtxType); 871 872 if (NULL == context) { 873 continue; 874 } 875 876 static const int kWidth = 100; 877 static const int kHeight = 100; 878 879 SkAutoTUnref<SkPicture> pict; 880 881 // create a picture with the structure: 882 // 1) 883 // SaveLayer 884 // Restore 885 // 2) 886 // SaveLayer 887 // Translate 888 // SaveLayer w/ bound 889 // Restore 890 // Restore 891 // 3) 892 // SaveLayer w/ copyable paint 893 // Restore 894 // 4) 895 // SaveLayer w/ non-copyable paint 896 // Restore 897 { 898 SkPictureRecorder recorder; 899 900 SkCanvas* c = recorder.DEPRECATED_beginRecording(kWidth, kHeight); 901 // 1) 902 c->saveLayer(NULL, NULL); 903 c->restore(); 904 905 // 2) 906 c->saveLayer(NULL, NULL); 907 c->translate(kWidth/2, kHeight/2); 908 SkRect r = SkRect::MakeXYWH(0, 0, kWidth/2, kHeight/2); 909 c->saveLayer(&r, NULL); 910 c->restore(); 911 c->restore(); 912 913 // 3) 914 { 915 SkPaint p; 916 p.setColor(SK_ColorRED); 917 c->saveLayer(NULL, &p); 918 c->restore(); 919 } 920 // 4) 921 // TODO: this case will need to be removed once the paint's are immutable 922 { 923 SkPaint p; 924 SkAutoTUnref<SkColorFilter> cf(SkLumaColorFilter::Create()); 925 p.setImageFilter(SkColorFilterImageFilter::Create(cf.get()))->unref(); 926 c->saveLayer(NULL, &p); 927 c->restore(); 928 } 929 930 pict.reset(recorder.endRecording()); 931 } 932 933 // Now test out the SaveLayer extraction 934 { 935 SkImageInfo info = SkImageInfo::MakeN32Premul(kWidth, kHeight); 936 937 SkAutoTUnref<SkSurface> surface(SkSurface::NewScratchRenderTarget(context, info)); 938 939 SkCanvas* canvas = surface->getCanvas(); 940 941 canvas->EXPERIMENTAL_optimize(pict); 942 943 SkPicture::AccelData::Key key = GrAccelData::ComputeAccelDataKey(); 944 945 const SkPicture::AccelData* data = pict->EXPERIMENTAL_getAccelData(key); 946 REPORTER_ASSERT(reporter, NULL != data); 947 948 const GrAccelData *gpuData = static_cast<const GrAccelData*>(data); 949 REPORTER_ASSERT(reporter, 5 == gpuData->numSaveLayers()); 950 951 const GrAccelData::SaveLayerInfo& info0 = gpuData->saveLayerInfo(0); 952 // The parent/child layer appear in reverse order 953 const GrAccelData::SaveLayerInfo& info1 = gpuData->saveLayerInfo(2); 954 const GrAccelData::SaveLayerInfo& info2 = gpuData->saveLayerInfo(1); 955 const GrAccelData::SaveLayerInfo& info3 = gpuData->saveLayerInfo(3); 956// const GrAccelData::SaveLayerInfo& info4 = gpuData->saveLayerInfo(4); 957 958 REPORTER_ASSERT(reporter, info0.fValid); 959 REPORTER_ASSERT(reporter, kWidth == info0.fSize.fWidth && kHeight == info0.fSize.fHeight); 960 REPORTER_ASSERT(reporter, info0.fCTM.isIdentity()); 961 REPORTER_ASSERT(reporter, 0 == info0.fOffset.fX && 0 == info0.fOffset.fY); 962 REPORTER_ASSERT(reporter, NULL != info0.fPaint); 963 REPORTER_ASSERT(reporter, !info0.fIsNested && !info0.fHasNestedLayers); 964 965 REPORTER_ASSERT(reporter, info1.fValid); 966 REPORTER_ASSERT(reporter, kWidth == info1.fSize.fWidth && kHeight == info1.fSize.fHeight); 967 REPORTER_ASSERT(reporter, info1.fCTM.isIdentity()); 968 REPORTER_ASSERT(reporter, 0 == info1.fOffset.fX && 0 == info1.fOffset.fY); 969 REPORTER_ASSERT(reporter, NULL != info1.fPaint); 970 REPORTER_ASSERT(reporter, !info1.fIsNested && info1.fHasNestedLayers); // has a nested SL 971 972 REPORTER_ASSERT(reporter, info2.fValid); 973 REPORTER_ASSERT(reporter, kWidth/2 == info2.fSize.fWidth && 974 kHeight/2 == info2.fSize.fHeight); // bound reduces size 975 REPORTER_ASSERT(reporter, info2.fCTM.isIdentity()); // translated 976 REPORTER_ASSERT(reporter, kWidth/2 == info2.fOffset.fX && 977 kHeight/2 == info2.fOffset.fY); 978 REPORTER_ASSERT(reporter, NULL != info1.fPaint); 979 REPORTER_ASSERT(reporter, info2.fIsNested && !info2.fHasNestedLayers); // is nested 980 981 REPORTER_ASSERT(reporter, info3.fValid); 982 REPORTER_ASSERT(reporter, kWidth == info3.fSize.fWidth && kHeight == info3.fSize.fHeight); 983 REPORTER_ASSERT(reporter, info3.fCTM.isIdentity()); 984 REPORTER_ASSERT(reporter, 0 == info3.fOffset.fX && 0 == info3.fOffset.fY); 985 REPORTER_ASSERT(reporter, NULL != info3.fPaint); 986 REPORTER_ASSERT(reporter, !info3.fIsNested && !info3.fHasNestedLayers); 987 988 #if 0 // needs more though for GrGatherCanvas 989 REPORTER_ASSERT(reporter, !info4.fValid); // paint is/was uncopyable 990 REPORTER_ASSERT(reporter, kWidth == info4.fSize.fWidth && kHeight == info4.fSize.fHeight); 991 REPORTER_ASSERT(reporter, 0 == info4.fOffset.fX && 0 == info4.fOffset.fY); 992 REPORTER_ASSERT(reporter, info4.fCTM.isIdentity()); 993 REPORTER_ASSERT(reporter, NULL == info4.fPaint); // paint is/was uncopyable 994 REPORTER_ASSERT(reporter, !info4.fIsNested && !info4.fHasNestedLayers); 995 #endif 996 } 997 } 998} 999 1000#endif 1001 1002static void test_has_text(skiatest::Reporter* reporter, bool useNewPath) { 1003 SkPictureRecorder recorder; 1004#define BEGIN_RECORDING useNewPath ? recorder.EXPERIMENTAL_beginRecording(100, 100) \ 1005 : recorder. DEPRECATED_beginRecording(100, 100) 1006 1007 SkCanvas* canvas = BEGIN_RECORDING; 1008 { 1009 canvas->drawRect(SkRect::MakeWH(20, 20), SkPaint()); 1010 } 1011 SkAutoTUnref<SkPicture> picture(recorder.endRecording()); 1012 REPORTER_ASSERT(reporter, !picture->hasText()); 1013 1014 SkPoint point = SkPoint::Make(10, 10); 1015 canvas = BEGIN_RECORDING; 1016 { 1017 canvas->drawText("Q", 1, point.fX, point.fY, SkPaint()); 1018 } 1019 picture.reset(recorder.endRecording()); 1020 REPORTER_ASSERT(reporter, picture->hasText()); 1021 1022 canvas = BEGIN_RECORDING; 1023 { 1024 canvas->drawPosText("Q", 1, &point, SkPaint()); 1025 } 1026 picture.reset(recorder.endRecording()); 1027 REPORTER_ASSERT(reporter, picture->hasText()); 1028 1029 canvas = BEGIN_RECORDING; 1030 { 1031 canvas->drawPosTextH("Q", 1, &point.fX, point.fY, SkPaint()); 1032 } 1033 picture.reset(recorder.endRecording()); 1034 REPORTER_ASSERT(reporter, picture->hasText()); 1035 1036 canvas = BEGIN_RECORDING; 1037 { 1038 SkPath path; 1039 path.moveTo(0, 0); 1040 path.lineTo(50, 50); 1041 1042 canvas->drawTextOnPathHV("Q", 1, path, point.fX, point.fY, SkPaint()); 1043 } 1044 picture.reset(recorder.endRecording()); 1045 REPORTER_ASSERT(reporter, picture->hasText()); 1046 1047 canvas = BEGIN_RECORDING; 1048 { 1049 SkPath path; 1050 path.moveTo(0, 0); 1051 path.lineTo(50, 50); 1052 1053 canvas->drawTextOnPath("Q", 1, path, NULL, SkPaint()); 1054 } 1055 picture.reset(recorder.endRecording()); 1056 REPORTER_ASSERT(reporter, picture->hasText()); 1057 1058 // Nest the previous picture inside a new one. 1059 // This doesn't work in the old backend. 1060 if (useNewPath) { 1061 canvas = BEGIN_RECORDING; 1062 { 1063 canvas->drawPicture(picture.get()); 1064 } 1065 picture.reset(recorder.endRecording()); 1066 REPORTER_ASSERT(reporter, picture->hasText()); 1067 } 1068#undef BEGIN_RECORDING 1069} 1070 1071static void set_canvas_to_save_count_4(SkCanvas* canvas) { 1072 canvas->restoreToCount(1); 1073 canvas->save(); 1074 canvas->save(); 1075 canvas->save(); 1076} 1077 1078/** 1079 * A canvas that records the number of saves, saveLayers and restores. 1080 */ 1081class SaveCountingCanvas : public SkCanvas { 1082public: 1083 SaveCountingCanvas(int width, int height) 1084 : INHERITED(width, height) 1085 , fSaveCount(0) 1086 , fSaveLayerCount(0) 1087 , fRestoreCount(0){ 1088 } 1089 1090 virtual SaveLayerStrategy willSaveLayer(const SkRect* bounds, const SkPaint* paint, 1091 SaveFlags flags) SK_OVERRIDE { 1092 ++fSaveLayerCount; 1093 return this->INHERITED::willSaveLayer(bounds, paint, flags); 1094 } 1095 1096 virtual void willSave() SK_OVERRIDE { 1097 ++fSaveCount; 1098 this->INHERITED::willSave(); 1099 } 1100 1101 virtual void willRestore() SK_OVERRIDE { 1102 ++fRestoreCount; 1103 this->INHERITED::willRestore(); 1104 } 1105 1106 unsigned int getSaveCount() const { return fSaveCount; } 1107 unsigned int getSaveLayerCount() const { return fSaveLayerCount; } 1108 unsigned int getRestoreCount() const { return fRestoreCount; } 1109 1110private: 1111 unsigned int fSaveCount; 1112 unsigned int fSaveLayerCount; 1113 unsigned int fRestoreCount; 1114 1115 typedef SkCanvas INHERITED; 1116}; 1117 1118void check_save_state(skiatest::Reporter* reporter, SkPicture* picture, 1119 unsigned int numSaves, unsigned int numSaveLayers, 1120 unsigned int numRestores) { 1121 SaveCountingCanvas canvas(picture->width(), picture->height()); 1122 1123 picture->draw(&canvas); 1124 1125 REPORTER_ASSERT(reporter, numSaves == canvas.getSaveCount()); 1126 REPORTER_ASSERT(reporter, numSaveLayers == canvas.getSaveLayerCount()); 1127 REPORTER_ASSERT(reporter, numRestores == canvas.getRestoreCount()); 1128} 1129 1130// This class exists so SkPicture can friend it and give it access to 1131// the 'partialReplay' method. 1132class SkPictureRecorderReplayTester { 1133public: 1134 static SkPicture* Copy(SkPictureRecorder* recorder) { 1135 SkPictureRecorder recorder2; 1136 1137 SkCanvas* canvas = recorder2.beginRecording(10, 10); 1138 1139 recorder->partialReplay(canvas); 1140 1141 return recorder2.endRecording(); 1142 } 1143}; 1144 1145static void create_imbalance(SkCanvas* canvas) { 1146 SkRect clipRect = SkRect::MakeWH(2, 2); 1147 SkRect drawRect = SkRect::MakeWH(10, 10); 1148 canvas->save(); 1149 canvas->clipRect(clipRect, SkRegion::kReplace_Op); 1150 canvas->translate(1.0f, 1.0f); 1151 SkPaint p; 1152 p.setColor(SK_ColorGREEN); 1153 canvas->drawRect(drawRect, p); 1154 // no restore 1155} 1156 1157// This tests that replaying a potentially unbalanced picture into a canvas 1158// doesn't affect the canvas' save count or matrix/clip state. 1159static void check_balance(skiatest::Reporter* reporter, SkPicture* picture) { 1160 SkBitmap bm; 1161 bm.allocN32Pixels(4, 3); 1162 SkCanvas canvas(bm); 1163 1164 int beforeSaveCount = canvas.getSaveCount(); 1165 1166 SkMatrix beforeMatrix = canvas.getTotalMatrix(); 1167 1168 SkRect beforeClip; 1169 1170 canvas.getClipBounds(&beforeClip); 1171 1172 canvas.drawPicture(picture); 1173 1174 REPORTER_ASSERT(reporter, beforeSaveCount == canvas.getSaveCount()); 1175 REPORTER_ASSERT(reporter, beforeMatrix == canvas.getTotalMatrix()); 1176 1177 SkRect afterClip; 1178 1179 canvas.getClipBounds(&afterClip); 1180 1181 REPORTER_ASSERT(reporter, afterClip == beforeClip); 1182} 1183 1184// Test out SkPictureRecorder::partialReplay 1185DEF_TEST(PictureRecorder_replay, reporter) { 1186 // check save/saveLayer state 1187 { 1188 SkPictureRecorder recorder; 1189 1190 SkCanvas* canvas = recorder.beginRecording(10, 10); 1191 1192 canvas->saveLayer(NULL, NULL); 1193 1194 SkAutoTUnref<SkPicture> copy(SkPictureRecorderReplayTester::Copy(&recorder)); 1195 1196 // The extra save and restore comes from the Copy process. 1197 check_save_state(reporter, copy, 2, 1, 3); 1198 1199 canvas->saveLayer(NULL, NULL); 1200 1201 SkAutoTUnref<SkPicture> final(recorder.endRecording()); 1202 1203 check_save_state(reporter, final, 1, 2, 3); 1204 1205 // The copy shouldn't pick up any operations added after it was made 1206 check_save_state(reporter, copy, 2, 1, 3); 1207 } 1208 1209 // (partially) check leakage of draw ops 1210 { 1211 SkPictureRecorder recorder; 1212 1213 SkCanvas* canvas = recorder.beginRecording(10, 10); 1214 1215 SkRect r = SkRect::MakeWH(5, 5); 1216 SkPaint p; 1217 1218 canvas->drawRect(r, p); 1219 1220 SkAutoTUnref<SkPicture> copy(SkPictureRecorderReplayTester::Copy(&recorder)); 1221 1222 REPORTER_ASSERT(reporter, !copy->willPlayBackBitmaps()); 1223 1224 SkBitmap bm; 1225 make_bm(&bm, 10, 10, SK_ColorRED, true); 1226 1227 r.offset(5.0f, 5.0f); 1228 canvas->drawBitmapRectToRect(bm, NULL, r); 1229 1230 SkAutoTUnref<SkPicture> final(recorder.endRecording()); 1231 REPORTER_ASSERT(reporter, final->willPlayBackBitmaps()); 1232 1233 REPORTER_ASSERT(reporter, copy->uniqueID() != final->uniqueID()); 1234 1235 // The snapshot shouldn't pick up any operations added after it was made 1236 REPORTER_ASSERT(reporter, !copy->willPlayBackBitmaps()); 1237 } 1238 1239 // Recreate the Android partialReplay test case 1240 { 1241 SkPictureRecorder recorder; 1242 1243 SkCanvas* canvas = recorder.beginRecording(4, 3, NULL, 0); 1244 create_imbalance(canvas); 1245 1246 int expectedSaveCount = canvas->getSaveCount(); 1247 1248 SkAutoTUnref<SkPicture> copy(SkPictureRecorderReplayTester::Copy(&recorder)); 1249 check_balance(reporter, copy); 1250 1251 REPORTER_ASSERT(reporter, expectedSaveCount = canvas->getSaveCount()); 1252 1253 // End the recording of source to test the picture finalization 1254 // process isn't complicated by the partialReplay step 1255 SkAutoTUnref<SkPicture> final(recorder.endRecording()); 1256 } 1257} 1258 1259static void test_unbalanced_save_restores(skiatest::Reporter* reporter) { 1260 SkCanvas testCanvas(100, 100); 1261 set_canvas_to_save_count_4(&testCanvas); 1262 1263 REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount()); 1264 1265 SkPaint paint; 1266 SkRect rect = SkRect::MakeLTRB(-10000000, -10000000, 10000000, 10000000); 1267 1268 SkPictureRecorder recorder; 1269 1270 { 1271 // Create picture with 2 unbalanced saves 1272 SkCanvas* canvas = recorder.beginRecording(100, 100); 1273 canvas->save(); 1274 canvas->translate(10, 10); 1275 canvas->drawRect(rect, paint); 1276 canvas->save(); 1277 canvas->translate(10, 10); 1278 canvas->drawRect(rect, paint); 1279 SkAutoTUnref<SkPicture> extraSavePicture(recorder.endRecording()); 1280 1281 testCanvas.drawPicture(extraSavePicture); 1282 REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount()); 1283 } 1284 1285 set_canvas_to_save_count_4(&testCanvas); 1286 1287 { 1288 // Create picture with 2 unbalanced restores 1289 SkCanvas* canvas = recorder.beginRecording(100, 100); 1290 canvas->save(); 1291 canvas->translate(10, 10); 1292 canvas->drawRect(rect, paint); 1293 canvas->save(); 1294 canvas->translate(10, 10); 1295 canvas->drawRect(rect, paint); 1296 canvas->restore(); 1297 canvas->restore(); 1298 canvas->restore(); 1299 canvas->restore(); 1300 SkAutoTUnref<SkPicture> extraRestorePicture(recorder.endRecording()); 1301 1302 testCanvas.drawPicture(extraRestorePicture); 1303 REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount()); 1304 } 1305 1306 set_canvas_to_save_count_4(&testCanvas); 1307 1308 { 1309 SkCanvas* canvas = recorder.beginRecording(100, 100); 1310 canvas->translate(10, 10); 1311 canvas->drawRect(rect, paint); 1312 SkAutoTUnref<SkPicture> noSavePicture(recorder.endRecording()); 1313 1314 testCanvas.drawPicture(noSavePicture); 1315 REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount()); 1316 REPORTER_ASSERT(reporter, testCanvas.getTotalMatrix().isIdentity()); 1317 } 1318} 1319 1320static void test_peephole() { 1321 SkRandom rand; 1322 1323 SkPictureRecorder recorder; 1324 1325 for (int j = 0; j < 100; j++) { 1326 SkRandom rand2(rand); // remember the seed 1327 1328 SkCanvas* canvas = recorder.beginRecording(100, 100); 1329 1330 for (int i = 0; i < 1000; ++i) { 1331 rand_op(canvas, rand); 1332 } 1333 SkAutoTUnref<SkPicture> picture(recorder.endRecording()); 1334 1335 rand = rand2; 1336 } 1337 1338 { 1339 SkCanvas* canvas = recorder.beginRecording(100, 100); 1340 SkRect rect = SkRect::MakeWH(50, 50); 1341 1342 for (int i = 0; i < 100; ++i) { 1343 canvas->save(); 1344 } 1345 while (canvas->getSaveCount() > 1) { 1346 canvas->clipRect(rect); 1347 canvas->restore(); 1348 } 1349 SkAutoTUnref<SkPicture> picture(recorder.endRecording()); 1350 } 1351} 1352 1353#ifndef SK_DEBUG 1354// Only test this is in release mode. We deliberately crash in debug mode, since a valid caller 1355// should never do this. 1356static void test_bad_bitmap() { 1357 // This bitmap has a width and height but no pixels. As a result, attempting to record it will 1358 // fail. 1359 SkBitmap bm; 1360 bm.setInfo(SkImageInfo::MakeN32Premul(100, 100)); 1361 SkPictureRecorder recorder; 1362 SkCanvas* recordingCanvas = recorder.beginRecording(100, 100); 1363 recordingCanvas->drawBitmap(bm, 0, 0); 1364 SkAutoTUnref<SkPicture> picture(recorder.endRecording()); 1365 1366 SkCanvas canvas; 1367 canvas.drawPicture(picture); 1368} 1369#endif 1370 1371static SkData* encode_bitmap_to_data(size_t*, const SkBitmap& bm) { 1372 return SkImageEncoder::EncodeData(bm, SkImageEncoder::kPNG_Type, 100); 1373} 1374 1375static SkData* serialized_picture_from_bitmap(const SkBitmap& bitmap) { 1376 SkPictureRecorder recorder; 1377 SkCanvas* canvas = recorder.beginRecording(bitmap.width(), bitmap.height()); 1378 canvas->drawBitmap(bitmap, 0, 0); 1379 SkAutoTUnref<SkPicture> picture(recorder.endRecording()); 1380 1381 SkDynamicMemoryWStream wStream; 1382 picture->serialize(&wStream, &encode_bitmap_to_data); 1383 return wStream.copyToData(); 1384} 1385 1386struct ErrorContext { 1387 int fErrors; 1388 skiatest::Reporter* fReporter; 1389}; 1390 1391static void assert_one_parse_error_cb(SkError error, void* context) { 1392 ErrorContext* errorContext = static_cast<ErrorContext*>(context); 1393 errorContext->fErrors++; 1394 // This test only expects one error, and that is a kParseError. If there are others, 1395 // there is some unknown problem. 1396 REPORTER_ASSERT_MESSAGE(errorContext->fReporter, 1 == errorContext->fErrors, 1397 "This threw more errors than expected."); 1398 REPORTER_ASSERT_MESSAGE(errorContext->fReporter, kParseError_SkError == error, 1399 SkGetLastErrorString()); 1400} 1401 1402static void test_bitmap_with_encoded_data(skiatest::Reporter* reporter) { 1403 // Create a bitmap that will be encoded. 1404 SkBitmap original; 1405 make_bm(&original, 100, 100, SK_ColorBLUE, true); 1406 SkDynamicMemoryWStream wStream; 1407 if (!SkImageEncoder::EncodeStream(&wStream, original, SkImageEncoder::kPNG_Type, 100)) { 1408 return; 1409 } 1410 SkAutoDataUnref data(wStream.copyToData()); 1411 1412 SkBitmap bm; 1413 bool installSuccess = SkInstallDiscardablePixelRef( 1414 SkDecodingImageGenerator::Create(data, SkDecodingImageGenerator::Options()), &bm); 1415 REPORTER_ASSERT(reporter, installSuccess); 1416 1417 // Write both bitmaps to pictures, and ensure that the resulting data streams are the same. 1418 // Flattening original will follow the old path of performing an encode, while flattening bm 1419 // will use the already encoded data. 1420 SkAutoDataUnref picture1(serialized_picture_from_bitmap(original)); 1421 SkAutoDataUnref picture2(serialized_picture_from_bitmap(bm)); 1422 REPORTER_ASSERT(reporter, picture1->equals(picture2)); 1423 // Now test that a parse error was generated when trying to create a new SkPicture without 1424 // providing a function to decode the bitmap. 1425 ErrorContext context; 1426 context.fErrors = 0; 1427 context.fReporter = reporter; 1428 SkSetErrorCallback(assert_one_parse_error_cb, &context); 1429 SkMemoryStream pictureStream(picture1); 1430 SkClearLastError(); 1431 SkAutoUnref pictureFromStream(SkPicture::CreateFromStream(&pictureStream, NULL)); 1432 REPORTER_ASSERT(reporter, pictureFromStream.get() != NULL); 1433 SkClearLastError(); 1434 SkSetErrorCallback(NULL, NULL); 1435} 1436 1437static void test_draw_empty(skiatest::Reporter* reporter) { 1438 SkBitmap result; 1439 make_bm(&result, 2, 2, SK_ColorBLACK, false); 1440 1441 SkCanvas canvas(result); 1442 1443 { 1444 // stock SkPicture 1445 SkPictureRecorder recorder; 1446 recorder.beginRecording(1, 1); 1447 SkAutoTUnref<SkPicture> picture(recorder.endRecording()); 1448 1449 canvas.drawPicture(picture); 1450 } 1451 1452 { 1453 // tile grid 1454 SkTileGridFactory::TileGridInfo gridInfo; 1455 gridInfo.fMargin.setEmpty(); 1456 gridInfo.fOffset.setZero(); 1457 gridInfo.fTileInterval.set(1, 1); 1458 1459 SkTileGridFactory factory(gridInfo); 1460 SkPictureRecorder recorder; 1461 recorder.beginRecording(1, 1, &factory); 1462 SkAutoTUnref<SkPicture> picture(recorder.endRecording()); 1463 1464 canvas.drawPicture(picture); 1465 } 1466 1467 { 1468 // RTree 1469 SkRTreeFactory factory; 1470 SkPictureRecorder recorder; 1471 recorder.beginRecording(1, 1, &factory); 1472 SkAutoTUnref<SkPicture> picture(recorder.endRecording()); 1473 1474 canvas.drawPicture(picture); 1475 } 1476} 1477 1478static void test_clip_bound_opt(skiatest::Reporter* reporter) { 1479 // Test for crbug.com/229011 1480 SkRect rect1 = SkRect::MakeXYWH(SkIntToScalar(4), SkIntToScalar(4), 1481 SkIntToScalar(2), SkIntToScalar(2)); 1482 SkRect rect2 = SkRect::MakeXYWH(SkIntToScalar(7), SkIntToScalar(7), 1483 SkIntToScalar(1), SkIntToScalar(1)); 1484 SkRect rect3 = SkRect::MakeXYWH(SkIntToScalar(6), SkIntToScalar(6), 1485 SkIntToScalar(1), SkIntToScalar(1)); 1486 1487 SkPath invPath; 1488 invPath.addOval(rect1); 1489 invPath.setFillType(SkPath::kInverseEvenOdd_FillType); 1490 SkPath path; 1491 path.addOval(rect2); 1492 SkPath path2; 1493 path2.addOval(rect3); 1494 SkIRect clipBounds; 1495 SkPictureRecorder recorder; 1496 // Minimalist test set for 100% code coverage of 1497 // SkPictureRecord::updateClipConservativelyUsingBounds 1498 { 1499 SkCanvas* canvas = recorder.beginRecording(10, 10); 1500 canvas->clipPath(invPath, SkRegion::kIntersect_Op); 1501 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds); 1502 REPORTER_ASSERT(reporter, true == nonEmpty); 1503 REPORTER_ASSERT(reporter, 0 == clipBounds.fLeft); 1504 REPORTER_ASSERT(reporter, 0 == clipBounds.fTop); 1505 REPORTER_ASSERT(reporter, 10 == clipBounds.fBottom); 1506 REPORTER_ASSERT(reporter, 10 == clipBounds.fRight); 1507 } 1508 { 1509 SkCanvas* canvas = recorder.beginRecording(10, 10); 1510 canvas->clipPath(path, SkRegion::kIntersect_Op); 1511 canvas->clipPath(invPath, SkRegion::kIntersect_Op); 1512 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds); 1513 REPORTER_ASSERT(reporter, true == nonEmpty); 1514 REPORTER_ASSERT(reporter, 7 == clipBounds.fLeft); 1515 REPORTER_ASSERT(reporter, 7 == clipBounds.fTop); 1516 REPORTER_ASSERT(reporter, 8 == clipBounds.fBottom); 1517 REPORTER_ASSERT(reporter, 8 == clipBounds.fRight); 1518 } 1519 { 1520 SkCanvas* canvas = recorder.beginRecording(10, 10); 1521 canvas->clipPath(path, SkRegion::kIntersect_Op); 1522 canvas->clipPath(invPath, SkRegion::kUnion_Op); 1523 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds); 1524 REPORTER_ASSERT(reporter, true == nonEmpty); 1525 REPORTER_ASSERT(reporter, 0 == clipBounds.fLeft); 1526 REPORTER_ASSERT(reporter, 0 == clipBounds.fTop); 1527 REPORTER_ASSERT(reporter, 10 == clipBounds.fBottom); 1528 REPORTER_ASSERT(reporter, 10 == clipBounds.fRight); 1529 } 1530 { 1531 SkCanvas* canvas = recorder.beginRecording(10, 10); 1532 canvas->clipPath(path, SkRegion::kDifference_Op); 1533 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds); 1534 REPORTER_ASSERT(reporter, true == nonEmpty); 1535 REPORTER_ASSERT(reporter, 0 == clipBounds.fLeft); 1536 REPORTER_ASSERT(reporter, 0 == clipBounds.fTop); 1537 REPORTER_ASSERT(reporter, 10 == clipBounds.fBottom); 1538 REPORTER_ASSERT(reporter, 10 == clipBounds.fRight); 1539 } 1540 { 1541 SkCanvas* canvas = recorder.beginRecording(10, 10); 1542 canvas->clipPath(path, SkRegion::kReverseDifference_Op); 1543 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds); 1544 // True clip is actually empty in this case, but the best 1545 // determination we can make using only bounds as input is that the 1546 // clip is included in the bounds of 'path'. 1547 REPORTER_ASSERT(reporter, true == nonEmpty); 1548 REPORTER_ASSERT(reporter, 7 == clipBounds.fLeft); 1549 REPORTER_ASSERT(reporter, 7 == clipBounds.fTop); 1550 REPORTER_ASSERT(reporter, 8 == clipBounds.fBottom); 1551 REPORTER_ASSERT(reporter, 8 == clipBounds.fRight); 1552 } 1553 { 1554 SkCanvas* canvas = recorder.beginRecording(10, 10); 1555 canvas->clipPath(path, SkRegion::kIntersect_Op); 1556 canvas->clipPath(path2, SkRegion::kXOR_Op); 1557 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds); 1558 REPORTER_ASSERT(reporter, true == nonEmpty); 1559 REPORTER_ASSERT(reporter, 6 == clipBounds.fLeft); 1560 REPORTER_ASSERT(reporter, 6 == clipBounds.fTop); 1561 REPORTER_ASSERT(reporter, 8 == clipBounds.fBottom); 1562 REPORTER_ASSERT(reporter, 8 == clipBounds.fRight); 1563 } 1564} 1565 1566/** 1567 * A canvas that records the number of clip commands. 1568 */ 1569class ClipCountingCanvas : public SkCanvas { 1570public: 1571 ClipCountingCanvas(int width, int height) 1572 : INHERITED(width, height) 1573 , fClipCount(0){ 1574 } 1575 1576 virtual void onClipRect(const SkRect& r, 1577 SkRegion::Op op, 1578 ClipEdgeStyle edgeStyle) SK_OVERRIDE { 1579 fClipCount += 1; 1580 this->INHERITED::onClipRect(r, op, edgeStyle); 1581 } 1582 1583 virtual void onClipRRect(const SkRRect& rrect, 1584 SkRegion::Op op, 1585 ClipEdgeStyle edgeStyle)SK_OVERRIDE { 1586 fClipCount += 1; 1587 this->INHERITED::onClipRRect(rrect, op, edgeStyle); 1588 } 1589 1590 virtual void onClipPath(const SkPath& path, 1591 SkRegion::Op op, 1592 ClipEdgeStyle edgeStyle) SK_OVERRIDE { 1593 fClipCount += 1; 1594 this->INHERITED::onClipPath(path, op, edgeStyle); 1595 } 1596 1597 virtual void onClipRegion(const SkRegion& deviceRgn, SkRegion::Op op) SK_OVERRIDE { 1598 fClipCount += 1; 1599 this->INHERITED::onClipRegion(deviceRgn, op); 1600 } 1601 1602 unsigned getClipCount() const { return fClipCount; } 1603 1604private: 1605 unsigned fClipCount; 1606 1607 typedef SkCanvas INHERITED; 1608}; 1609 1610static void test_clip_expansion(skiatest::Reporter* reporter) { 1611 SkPictureRecorder recorder; 1612 SkCanvas* canvas = recorder.beginRecording(10, 10); 1613 1614 canvas->clipRect(SkRect::MakeEmpty(), SkRegion::kReplace_Op); 1615 // The following expanding clip should not be skipped. 1616 canvas->clipRect(SkRect::MakeXYWH(4, 4, 3, 3), SkRegion::kUnion_Op); 1617 // Draw something so the optimizer doesn't just fold the world. 1618 SkPaint p; 1619 p.setColor(SK_ColorBLUE); 1620 canvas->drawPaint(p); 1621 SkAutoTUnref<SkPicture> picture(recorder.endRecording()); 1622 1623 ClipCountingCanvas testCanvas(10, 10); 1624 picture->draw(&testCanvas); 1625 1626 // Both clips should be present on playback. 1627 REPORTER_ASSERT(reporter, testCanvas.getClipCount() == 2); 1628} 1629 1630static void test_hierarchical(skiatest::Reporter* reporter) { 1631 SkBitmap bm; 1632 make_bm(&bm, 10, 10, SK_ColorRED, true); 1633 1634 SkPictureRecorder recorder; 1635 1636 recorder.beginRecording(10, 10); 1637 SkAutoTUnref<SkPicture> childPlain(recorder.endRecording()); 1638 REPORTER_ASSERT(reporter, !childPlain->willPlayBackBitmaps()); // 0 1639 1640 recorder.beginRecording(10, 10)->drawBitmap(bm, 0, 0); 1641 SkAutoTUnref<SkPicture> childWithBitmap(recorder.endRecording()); 1642 REPORTER_ASSERT(reporter, childWithBitmap->willPlayBackBitmaps()); // 1 1643 1644 { 1645 SkCanvas* canvas = recorder.beginRecording(10, 10); 1646 canvas->drawPicture(childPlain); 1647 SkAutoTUnref<SkPicture> parentPP(recorder.endRecording()); 1648 REPORTER_ASSERT(reporter, !parentPP->willPlayBackBitmaps()); // 0 1649 } 1650 { 1651 SkCanvas* canvas = recorder.beginRecording(10, 10); 1652 canvas->drawPicture(childWithBitmap); 1653 SkAutoTUnref<SkPicture> parentPWB(recorder.endRecording()); 1654 REPORTER_ASSERT(reporter, parentPWB->willPlayBackBitmaps()); // 1 1655 } 1656 { 1657 SkCanvas* canvas = recorder.beginRecording(10, 10); 1658 canvas->drawBitmap(bm, 0, 0); 1659 canvas->drawPicture(childPlain); 1660 SkAutoTUnref<SkPicture> parentWBP(recorder.endRecording()); 1661 REPORTER_ASSERT(reporter, parentWBP->willPlayBackBitmaps()); // 1 1662 } 1663 { 1664 SkCanvas* canvas = recorder.beginRecording(10, 10); 1665 canvas->drawBitmap(bm, 0, 0); 1666 canvas->drawPicture(childWithBitmap); 1667 SkAutoTUnref<SkPicture> parentWBWB(recorder.endRecording()); 1668 REPORTER_ASSERT(reporter, parentWBWB->willPlayBackBitmaps()); // 2 1669 } 1670} 1671 1672static void test_gen_id(skiatest::Reporter* reporter) { 1673 1674 SkPictureRecorder recorder; 1675 recorder.beginRecording(0, 0); 1676 SkAutoTUnref<SkPicture> empty(recorder.endRecording()); 1677 1678 // Empty pictures should still have a valid ID 1679 REPORTER_ASSERT(reporter, empty->uniqueID() != SK_InvalidGenID); 1680 1681 SkCanvas* canvas = recorder.beginRecording(1, 1); 1682 canvas->drawARGB(255, 255, 255, 255); 1683 SkAutoTUnref<SkPicture> hasData(recorder.endRecording()); 1684 // picture should have a non-zero id after recording 1685 REPORTER_ASSERT(reporter, hasData->uniqueID() != SK_InvalidGenID); 1686 1687 // both pictures should have different ids 1688 REPORTER_ASSERT(reporter, hasData->uniqueID() != empty->uniqueID()); 1689} 1690 1691DEF_TEST(Picture, reporter) { 1692#ifdef SK_DEBUG 1693 test_deleting_empty_picture(); 1694 test_serializing_empty_picture(); 1695#else 1696 test_bad_bitmap(); 1697#endif 1698 test_unbalanced_save_restores(reporter); 1699 test_peephole(); 1700#if SK_SUPPORT_GPU 1701 test_gpu_veto(reporter, false); 1702 test_gpu_veto(reporter, true); 1703#endif 1704 test_has_text(reporter, false); 1705 test_has_text(reporter, true); 1706 test_analysis(reporter, false); 1707 test_analysis(reporter, true); 1708 test_gatherpixelrefs(reporter); 1709 test_gatherpixelrefsandrects(reporter); 1710 test_bitmap_with_encoded_data(reporter); 1711 test_draw_empty(reporter); 1712 test_clip_bound_opt(reporter); 1713 test_clip_expansion(reporter); 1714 test_hierarchical(reporter); 1715 test_gen_id(reporter); 1716} 1717 1718#if SK_SUPPORT_GPU 1719DEF_GPUTEST(GPUPicture, reporter, factory) { 1720 test_gpu_picture_optimization(reporter, factory); 1721} 1722#endif 1723 1724static void draw_bitmaps(const SkBitmap bitmap, SkCanvas* canvas) { 1725 const SkPaint paint; 1726 const SkRect rect = { 5.0f, 5.0f, 8.0f, 8.0f }; 1727 const SkIRect irect = { 2, 2, 3, 3 }; 1728 1729 // Don't care what these record, as long as they're legal. 1730 canvas->drawBitmap(bitmap, 0.0f, 0.0f, &paint); 1731 canvas->drawBitmapRectToRect(bitmap, &rect, rect, &paint, SkCanvas::kNone_DrawBitmapRectFlag); 1732 canvas->drawBitmapMatrix(bitmap, SkMatrix::I(), &paint); 1733 canvas->drawBitmapNine(bitmap, irect, rect, &paint); 1734 canvas->drawSprite(bitmap, 1, 1); 1735} 1736 1737static void test_draw_bitmaps(SkCanvas* canvas) { 1738 SkBitmap empty; 1739 draw_bitmaps(empty, canvas); 1740 empty.setInfo(SkImageInfo::MakeN32Premul(10, 10)); 1741 draw_bitmaps(empty, canvas); 1742} 1743 1744DEF_TEST(Picture_EmptyBitmap, r) { 1745 SkPictureRecorder recorder; 1746 test_draw_bitmaps(recorder.beginRecording(10, 10)); 1747 SkAutoTUnref<SkPicture> picture(recorder.endRecording()); 1748} 1749 1750DEF_TEST(Canvas_EmptyBitmap, r) { 1751 SkBitmap dst; 1752 dst.allocN32Pixels(10, 10); 1753 SkCanvas canvas(dst); 1754 1755 test_draw_bitmaps(&canvas); 1756} 1757 1758DEF_TEST(DontOptimizeSaveLayerDrawDrawRestore, reporter) { 1759 // This test is from crbug.com/344987. 1760 // The commands are: 1761 // saveLayer with paint that modifies alpha 1762 // drawBitmapRectToRect 1763 // drawBitmapRectToRect 1764 // restore 1765 // The bug was that this structure was modified so that: 1766 // - The saveLayer and restore were eliminated 1767 // - The alpha was only applied to the first drawBitmapRectToRect 1768 1769 // This test draws blue and red squares inside a 50% transparent 1770 // layer. Both colours should show up muted. 1771 // When the bug is present, the red square (the second bitmap) 1772 // shows upwith full opacity. 1773 1774 SkBitmap blueBM; 1775 make_bm(&blueBM, 100, 100, SkColorSetARGB(255, 0, 0, 255), true); 1776 SkBitmap redBM; 1777 make_bm(&redBM, 100, 100, SkColorSetARGB(255, 255, 0, 0), true); 1778 SkPaint semiTransparent; 1779 semiTransparent.setAlpha(0x80); 1780 1781 SkPictureRecorder recorder; 1782 SkCanvas* canvas = recorder.beginRecording(100, 100); 1783 canvas->drawARGB(0, 0, 0, 0); 1784 1785 canvas->saveLayer(0, &semiTransparent); 1786 canvas->drawBitmap(blueBM, 25, 25); 1787 canvas->drawBitmap(redBM, 50, 50); 1788 canvas->restore(); 1789 1790 SkAutoTUnref<SkPicture> picture(recorder.endRecording()); 1791 1792 // Now replay the picture back on another canvas 1793 // and check a couple of its pixels. 1794 SkBitmap replayBM; 1795 make_bm(&replayBM, 100, 100, SK_ColorBLACK, false); 1796 SkCanvas replayCanvas(replayBM); 1797 picture->draw(&replayCanvas); 1798 replayCanvas.flush(); 1799 1800 // With the bug present, at (55, 55) we would get a fully opaque red 1801 // intead of a dark red. 1802 REPORTER_ASSERT(reporter, replayBM.getColor(30, 30) == 0xff000080); 1803 REPORTER_ASSERT(reporter, replayBM.getColor(55, 55) == 0xff800000); 1804} 1805 1806struct CountingBBH : public SkBBoxHierarchy { 1807 mutable int searchCalls; 1808 1809 CountingBBH() : searchCalls(0) {} 1810 1811 virtual void search(const SkRect& query, SkTDArray<void*>* results) const { 1812 this->searchCalls++; 1813 } 1814 1815 // All other methods unimplemented. 1816 virtual void insert(void* data, const SkRect& bounds, bool defer) {} 1817 virtual void flushDeferredInserts() {} 1818 virtual void clear() {} 1819 virtual int getCount() const { return 0; } 1820 virtual int getDepth() const { return 0; } 1821 virtual void rewindInserts() {} 1822}; 1823 1824class SpoonFedBBHFactory : public SkBBHFactory { 1825public: 1826 explicit SpoonFedBBHFactory(SkBBoxHierarchy* bbh) : fBBH(bbh) {} 1827 virtual SkBBoxHierarchy* operator()(int width, int height) const { 1828 return SkRef(fBBH); 1829 } 1830private: 1831 SkBBoxHierarchy* fBBH; 1832}; 1833 1834// When the canvas clip covers the full picture, we don't need to call the BBH. 1835DEF_TEST(Picture_SkipBBH, r) { 1836 CountingBBH bbh; 1837 SpoonFedBBHFactory factory(&bbh); 1838 1839 SkPictureRecorder recorder; 1840 recorder.beginRecording(320, 240, &factory); 1841 SkAutoTUnref<const SkPicture> picture(recorder.endRecording()); 1842 1843 SkCanvas big(640, 480), small(300, 200); 1844 1845 picture->draw(&big); 1846 REPORTER_ASSERT(r, bbh.searchCalls == 0); 1847 1848 picture->draw(&small); 1849 REPORTER_ASSERT(r, bbh.searchCalls == 1); 1850} 1851