PictureTest.cpp revision 3a0f27916712bb3226874aeaa268e30f565880de
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.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 848#undef GENERATE_CANVAS 849 850static void test_gpu_picture_optimization(skiatest::Reporter* reporter, 851 GrContextFactory* factory) { 852 for (int i= 0; i < GrContextFactory::kGLContextTypeCnt; ++i) { 853 GrContextFactory::GLContextType glCtxType = (GrContextFactory::GLContextType) i; 854 855 if (!GrContextFactory::IsRenderingGLContext(glCtxType)) { 856 continue; 857 } 858 859 GrContext* context = factory->get(glCtxType); 860 861 if (NULL == context) { 862 continue; 863 } 864 865 static const int kWidth = 100; 866 static const int kHeight = 100; 867 868 SkAutoTUnref<SkPicture> pict; 869 870 // create a picture with the structure: 871 // 1) 872 // SaveLayer 873 // Restore 874 // 2) 875 // SaveLayer 876 // Translate 877 // SaveLayer w/ bound 878 // Restore 879 // Restore 880 // 3) 881 // SaveLayer w/ copyable paint 882 // Restore 883 // 4) 884 // SaveLayer w/ non-copyable paint 885 // Restore 886 { 887 SkPictureRecorder recorder; 888 889 SkCanvas* c = recorder.beginRecording(kWidth, kHeight); 890 // 1) 891 c->saveLayer(NULL, NULL); 892 c->restore(); 893 894 // 2) 895 c->saveLayer(NULL, NULL); 896 c->translate(kWidth/2, kHeight/2); 897 SkRect r = SkRect::MakeXYWH(0, 0, kWidth/2, kHeight/2); 898 c->saveLayer(&r, NULL); 899 c->restore(); 900 c->restore(); 901 902 // 3) 903 { 904 SkPaint p; 905 p.setColor(SK_ColorRED); 906 c->saveLayer(NULL, &p); 907 c->restore(); 908 } 909 // 4) 910 // TODO: this case will need to be removed once the paint's are immutable 911 { 912 SkPaint p; 913 SkAutoTUnref<SkColorFilter> cf(SkLumaColorFilter::Create()); 914 p.setImageFilter(SkColorFilterImageFilter::Create(cf.get()))->unref(); 915 c->saveLayer(NULL, &p); 916 c->restore(); 917 } 918 919 pict.reset(recorder.endRecording()); 920 } 921 922 // Now test out the SaveLayer extraction 923 { 924 SkImageInfo info = SkImageInfo::MakeN32Premul(kWidth, kHeight); 925 926 SkAutoTUnref<SkSurface> surface(SkSurface::NewScratchRenderTarget(context, info)); 927 928 SkCanvas* canvas = surface->getCanvas(); 929 930 canvas->EXPERIMENTAL_optimize(pict); 931 932 SkPicture::AccelData::Key key = GrAccelData::ComputeAccelDataKey(); 933 934 const SkPicture::AccelData* data = pict->EXPERIMENTAL_getAccelData(key); 935 REPORTER_ASSERT(reporter, NULL != data); 936 937 const GrAccelData *gpuData = static_cast<const GrAccelData*>(data); 938 REPORTER_ASSERT(reporter, 5 == gpuData->numSaveLayers()); 939 940 const GrAccelData::SaveLayerInfo& info0 = gpuData->saveLayerInfo(0); 941 // The parent/child layer appear in reverse order 942 const GrAccelData::SaveLayerInfo& info1 = gpuData->saveLayerInfo(2); 943 const GrAccelData::SaveLayerInfo& info2 = gpuData->saveLayerInfo(1); 944 const GrAccelData::SaveLayerInfo& info3 = gpuData->saveLayerInfo(3); 945// const GrAccelData::SaveLayerInfo& info4 = gpuData->saveLayerInfo(4); 946 947 REPORTER_ASSERT(reporter, info0.fValid); 948 REPORTER_ASSERT(reporter, kWidth == info0.fSize.fWidth && kHeight == info0.fSize.fHeight); 949 REPORTER_ASSERT(reporter, info0.fCTM.isIdentity()); 950 REPORTER_ASSERT(reporter, 0 == info0.fOffset.fX && 0 == info0.fOffset.fY); 951 REPORTER_ASSERT(reporter, NULL != info0.fPaint); 952 REPORTER_ASSERT(reporter, !info0.fIsNested && !info0.fHasNestedLayers); 953 954 REPORTER_ASSERT(reporter, info1.fValid); 955 REPORTER_ASSERT(reporter, kWidth == info1.fSize.fWidth && kHeight == info1.fSize.fHeight); 956 REPORTER_ASSERT(reporter, info1.fCTM.isIdentity()); 957 REPORTER_ASSERT(reporter, 0 == info1.fOffset.fX && 0 == info1.fOffset.fY); 958 REPORTER_ASSERT(reporter, NULL != info1.fPaint); 959 REPORTER_ASSERT(reporter, !info1.fIsNested && info1.fHasNestedLayers); // has a nested SL 960 961 REPORTER_ASSERT(reporter, info2.fValid); 962 REPORTER_ASSERT(reporter, kWidth/2 == info2.fSize.fWidth && 963 kHeight/2 == info2.fSize.fHeight); // bound reduces size 964 REPORTER_ASSERT(reporter, info2.fCTM.isIdentity()); // translated 965 REPORTER_ASSERT(reporter, kWidth/2 == info2.fOffset.fX && 966 kHeight/2 == info2.fOffset.fY); 967 REPORTER_ASSERT(reporter, NULL != info1.fPaint); 968 REPORTER_ASSERT(reporter, info2.fIsNested && !info2.fHasNestedLayers); // is nested 969 970 REPORTER_ASSERT(reporter, info3.fValid); 971 REPORTER_ASSERT(reporter, kWidth == info3.fSize.fWidth && kHeight == info3.fSize.fHeight); 972 REPORTER_ASSERT(reporter, info3.fCTM.isIdentity()); 973 REPORTER_ASSERT(reporter, 0 == info3.fOffset.fX && 0 == info3.fOffset.fY); 974 REPORTER_ASSERT(reporter, NULL != info3.fPaint); 975 REPORTER_ASSERT(reporter, !info3.fIsNested && !info3.fHasNestedLayers); 976 977 #if 0 // needs more though for GrGatherCanvas 978 REPORTER_ASSERT(reporter, !info4.fValid); // paint is/was uncopyable 979 REPORTER_ASSERT(reporter, kWidth == info4.fSize.fWidth && kHeight == info4.fSize.fHeight); 980 REPORTER_ASSERT(reporter, 0 == info4.fOffset.fX && 0 == info4.fOffset.fY); 981 REPORTER_ASSERT(reporter, info4.fCTM.isIdentity()); 982 REPORTER_ASSERT(reporter, NULL == info4.fPaint); // paint is/was uncopyable 983 REPORTER_ASSERT(reporter, !info4.fIsNested && !info4.fHasNestedLayers); 984 #endif 985 } 986 } 987} 988 989#endif 990 991static void test_has_text(skiatest::Reporter* reporter) { 992 SkPictureRecorder recorder; 993 SkPaint paint; 994 paint.setColor(SK_ColorBLUE); 995 SkPoint point = SkPoint::Make(10, 10); 996 997 SkCanvas* canvas = recorder.beginRecording(100, 100); 998 { 999 canvas->drawRect(SkRect::MakeWH(20, 20), paint); 1000 } 1001 SkAutoTUnref<SkPicture> picture(recorder.endRecording()); 1002 REPORTER_ASSERT(reporter, !picture->hasText()); 1003 1004 canvas = recorder.beginRecording(100, 100); 1005 { 1006 canvas->drawText("Q", 1, point.fX, point.fY, paint); 1007 } 1008 picture.reset(recorder.endRecording()); 1009 REPORTER_ASSERT(reporter, picture->hasText()); 1010 1011 canvas = recorder.beginRecording(100, 100); 1012 { 1013 canvas->drawPosText("Q", 1, &point, paint); 1014 } 1015 picture.reset(recorder.endRecording()); 1016 REPORTER_ASSERT(reporter, picture->hasText()); 1017 1018 canvas = recorder.beginRecording(100, 100); 1019 { 1020 canvas->drawPosTextH("Q", 1, &point.fX, point.fY, paint); 1021 } 1022 picture.reset(recorder.endRecording()); 1023 REPORTER_ASSERT(reporter, picture->hasText()); 1024 1025 canvas = recorder.beginRecording(100, 100); 1026 { 1027 SkPath path; 1028 path.moveTo(0, 0); 1029 path.lineTo(50, 50); 1030 1031 canvas->drawTextOnPathHV("Q", 1, path, point.fX, point.fY, paint); 1032 } 1033 picture.reset(recorder.endRecording()); 1034 REPORTER_ASSERT(reporter, picture->hasText()); 1035 1036 canvas = recorder.beginRecording(100, 100); 1037 { 1038 SkPath path; 1039 path.moveTo(0, 0); 1040 path.lineTo(50, 50); 1041 1042 canvas->drawTextOnPath("Q", 1, path, NULL, paint); 1043 } 1044 picture.reset(recorder.endRecording()); 1045 REPORTER_ASSERT(reporter, picture->hasText()); 1046} 1047 1048static void set_canvas_to_save_count_4(SkCanvas* canvas) { 1049 canvas->restoreToCount(1); 1050 canvas->save(); 1051 canvas->save(); 1052 canvas->save(); 1053} 1054 1055/** 1056 * A canvas that records the number of saves, saveLayers and restores. 1057 */ 1058class SaveCountingCanvas : public SkCanvas { 1059public: 1060 SaveCountingCanvas(int width, int height) 1061 : INHERITED(width, height) 1062 , fSaveCount(0) 1063 , fSaveLayerCount(0) 1064 , fRestoreCount(0){ 1065 } 1066 1067 virtual SaveLayerStrategy willSaveLayer(const SkRect* bounds, const SkPaint* paint, 1068 SaveFlags flags) SK_OVERRIDE { 1069 ++fSaveLayerCount; 1070 return this->INHERITED::willSaveLayer(bounds, paint, flags); 1071 } 1072 1073 virtual void willSave() SK_OVERRIDE { 1074 ++fSaveCount; 1075 this->INHERITED::willSave(); 1076 } 1077 1078 virtual void willRestore() SK_OVERRIDE { 1079 ++fRestoreCount; 1080 this->INHERITED::willRestore(); 1081 } 1082 1083 unsigned int getSaveCount() const { return fSaveCount; } 1084 unsigned int getSaveLayerCount() const { return fSaveLayerCount; } 1085 unsigned int getRestoreCount() const { return fRestoreCount; } 1086 1087private: 1088 unsigned int fSaveCount; 1089 unsigned int fSaveLayerCount; 1090 unsigned int fRestoreCount; 1091 1092 typedef SkCanvas INHERITED; 1093}; 1094 1095void check_save_state(skiatest::Reporter* reporter, SkPicture* picture, 1096 unsigned int numSaves, unsigned int numSaveLayers, 1097 unsigned int numRestores) { 1098 SaveCountingCanvas canvas(picture->width(), picture->height()); 1099 1100 picture->draw(&canvas); 1101 1102 REPORTER_ASSERT(reporter, numSaves == canvas.getSaveCount()); 1103 REPORTER_ASSERT(reporter, numSaveLayers == canvas.getSaveLayerCount()); 1104 REPORTER_ASSERT(reporter, numRestores == canvas.getRestoreCount()); 1105} 1106 1107// This class exists so SkPicture can friend it and give it access to 1108// the 'partialReplay' method. 1109class SkPictureRecorderReplayTester { 1110public: 1111 static SkPicture* Copy(SkPictureRecorder* recorder) { 1112 SkPictureRecorder recorder2; 1113 1114 SkCanvas* canvas = recorder2.beginRecording(10, 10); 1115 1116 recorder->partialReplay(canvas); 1117 1118 return recorder2.endRecording(); 1119 } 1120}; 1121 1122static void create_imbalance(SkCanvas* canvas) { 1123 SkRect clipRect = SkRect::MakeWH(2, 2); 1124 SkRect drawRect = SkRect::MakeWH(10, 10); 1125 canvas->save(); 1126 canvas->clipRect(clipRect, SkRegion::kReplace_Op); 1127 canvas->translate(1.0f, 1.0f); 1128 SkPaint p; 1129 p.setColor(SK_ColorGREEN); 1130 canvas->drawRect(drawRect, p); 1131 // no restore 1132} 1133 1134// This tests that replaying a potentially unbalanced picture into a canvas 1135// doesn't affect the canvas' save count or matrix/clip state. 1136static void check_balance(skiatest::Reporter* reporter, SkPicture* picture) { 1137 SkBitmap bm; 1138 bm.allocN32Pixels(4, 3); 1139 SkCanvas canvas(bm); 1140 1141 int beforeSaveCount = canvas.getSaveCount(); 1142 1143 SkMatrix beforeMatrix = canvas.getTotalMatrix(); 1144 1145 SkRect beforeClip; 1146 1147 canvas.getClipBounds(&beforeClip); 1148 1149 canvas.drawPicture(picture); 1150 1151 REPORTER_ASSERT(reporter, beforeSaveCount == canvas.getSaveCount()); 1152 REPORTER_ASSERT(reporter, beforeMatrix == canvas.getTotalMatrix()); 1153 1154 SkRect afterClip; 1155 1156 canvas.getClipBounds(&afterClip); 1157 1158 REPORTER_ASSERT(reporter, afterClip == beforeClip); 1159} 1160 1161// Test out SkPictureRecorder::partialReplay 1162DEF_TEST(PictureRecorder_replay, reporter) { 1163 // check save/saveLayer state 1164 { 1165 SkPictureRecorder recorder; 1166 1167 SkCanvas* canvas = recorder.beginRecording(10, 10); 1168 1169 canvas->saveLayer(NULL, NULL); 1170 1171 SkAutoTUnref<SkPicture> copy(SkPictureRecorderReplayTester::Copy(&recorder)); 1172 1173 // The extra save and restore comes from the Copy process. 1174 check_save_state(reporter, copy, 2, 1, 3); 1175 1176 canvas->saveLayer(NULL, NULL); 1177 1178 SkAutoTUnref<SkPicture> final(recorder.endRecording()); 1179 1180 check_save_state(reporter, final, 1, 2, 3); 1181 1182 // The copy shouldn't pick up any operations added after it was made 1183 check_save_state(reporter, copy, 2, 1, 3); 1184 } 1185 1186 // (partially) check leakage of draw ops 1187 { 1188 SkPictureRecorder recorder; 1189 1190 SkCanvas* canvas = recorder.beginRecording(10, 10); 1191 1192 SkRect r = SkRect::MakeWH(5, 5); 1193 SkPaint p; 1194 1195 canvas->drawRect(r, p); 1196 1197 SkAutoTUnref<SkPicture> copy(SkPictureRecorderReplayTester::Copy(&recorder)); 1198 1199 REPORTER_ASSERT(reporter, !copy->willPlayBackBitmaps()); 1200 1201 SkBitmap bm; 1202 make_bm(&bm, 10, 10, SK_ColorRED, true); 1203 1204 r.offset(5.0f, 5.0f); 1205 canvas->drawBitmapRectToRect(bm, NULL, r); 1206 1207 SkAutoTUnref<SkPicture> final(recorder.endRecording()); 1208 REPORTER_ASSERT(reporter, final->willPlayBackBitmaps()); 1209 1210 REPORTER_ASSERT(reporter, copy->uniqueID() != final->uniqueID()); 1211 1212 // The snapshot shouldn't pick up any operations added after it was made 1213 REPORTER_ASSERT(reporter, !copy->willPlayBackBitmaps()); 1214 } 1215 1216 // Recreate the Android partialReplay test case 1217 { 1218 SkPictureRecorder recorder; 1219 1220 SkCanvas* canvas = recorder.beginRecording(4, 3, NULL, 0); 1221 create_imbalance(canvas); 1222 1223 int expectedSaveCount = canvas->getSaveCount(); 1224 1225 SkAutoTUnref<SkPicture> copy(SkPictureRecorderReplayTester::Copy(&recorder)); 1226 check_balance(reporter, copy); 1227 1228 REPORTER_ASSERT(reporter, expectedSaveCount = canvas->getSaveCount()); 1229 1230 // End the recording of source to test the picture finalization 1231 // process isn't complicated by the partialReplay step 1232 SkAutoTUnref<SkPicture> final(recorder.endRecording()); 1233 } 1234} 1235 1236static void test_unbalanced_save_restores(skiatest::Reporter* reporter) { 1237 SkCanvas testCanvas(100, 100); 1238 set_canvas_to_save_count_4(&testCanvas); 1239 1240 REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount()); 1241 1242 SkPaint paint; 1243 SkRect rect = SkRect::MakeLTRB(-10000000, -10000000, 10000000, 10000000); 1244 1245 SkPictureRecorder recorder; 1246 1247 { 1248 // Create picture with 2 unbalanced saves 1249 SkCanvas* canvas = recorder.beginRecording(100, 100); 1250 canvas->save(); 1251 canvas->translate(10, 10); 1252 canvas->drawRect(rect, paint); 1253 canvas->save(); 1254 canvas->translate(10, 10); 1255 canvas->drawRect(rect, paint); 1256 SkAutoTUnref<SkPicture> extraSavePicture(recorder.endRecording()); 1257 1258 testCanvas.drawPicture(extraSavePicture); 1259 REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount()); 1260 } 1261 1262 set_canvas_to_save_count_4(&testCanvas); 1263 1264 { 1265 // Create picture with 2 unbalanced restores 1266 SkCanvas* canvas = recorder.beginRecording(100, 100); 1267 canvas->save(); 1268 canvas->translate(10, 10); 1269 canvas->drawRect(rect, paint); 1270 canvas->save(); 1271 canvas->translate(10, 10); 1272 canvas->drawRect(rect, paint); 1273 canvas->restore(); 1274 canvas->restore(); 1275 canvas->restore(); 1276 canvas->restore(); 1277 SkAutoTUnref<SkPicture> extraRestorePicture(recorder.endRecording()); 1278 1279 testCanvas.drawPicture(extraRestorePicture); 1280 REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount()); 1281 } 1282 1283 set_canvas_to_save_count_4(&testCanvas); 1284 1285 { 1286 SkCanvas* canvas = recorder.beginRecording(100, 100); 1287 canvas->translate(10, 10); 1288 canvas->drawRect(rect, paint); 1289 SkAutoTUnref<SkPicture> noSavePicture(recorder.endRecording()); 1290 1291 testCanvas.drawPicture(noSavePicture); 1292 REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount()); 1293 REPORTER_ASSERT(reporter, testCanvas.getTotalMatrix().isIdentity()); 1294 } 1295} 1296 1297static void test_peephole() { 1298 SkRandom rand; 1299 1300 SkPictureRecorder recorder; 1301 1302 for (int j = 0; j < 100; j++) { 1303 SkRandom rand2(rand); // remember the seed 1304 1305 SkCanvas* canvas = recorder.beginRecording(100, 100); 1306 1307 for (int i = 0; i < 1000; ++i) { 1308 rand_op(canvas, rand); 1309 } 1310 SkAutoTUnref<SkPicture> picture(recorder.endRecording()); 1311 1312 rand = rand2; 1313 } 1314 1315 { 1316 SkCanvas* canvas = recorder.beginRecording(100, 100); 1317 SkRect rect = SkRect::MakeWH(50, 50); 1318 1319 for (int i = 0; i < 100; ++i) { 1320 canvas->save(); 1321 } 1322 while (canvas->getSaveCount() > 1) { 1323 canvas->clipRect(rect); 1324 canvas->restore(); 1325 } 1326 SkAutoTUnref<SkPicture> picture(recorder.endRecording()); 1327 } 1328} 1329 1330#ifndef SK_DEBUG 1331// Only test this is in release mode. We deliberately crash in debug mode, since a valid caller 1332// should never do this. 1333static void test_bad_bitmap() { 1334 // This bitmap has a width and height but no pixels. As a result, attempting to record it will 1335 // fail. 1336 SkBitmap bm; 1337 bm.setInfo(SkImageInfo::MakeN32Premul(100, 100)); 1338 SkPictureRecorder recorder; 1339 SkCanvas* recordingCanvas = recorder.beginRecording(100, 100); 1340 recordingCanvas->drawBitmap(bm, 0, 0); 1341 SkAutoTUnref<SkPicture> picture(recorder.endRecording()); 1342 1343 SkCanvas canvas; 1344 canvas.drawPicture(picture); 1345} 1346#endif 1347 1348static SkData* encode_bitmap_to_data(size_t*, const SkBitmap& bm) { 1349 return SkImageEncoder::EncodeData(bm, SkImageEncoder::kPNG_Type, 100); 1350} 1351 1352static SkData* serialized_picture_from_bitmap(const SkBitmap& bitmap) { 1353 SkPictureRecorder recorder; 1354 SkCanvas* canvas = recorder.beginRecording(bitmap.width(), bitmap.height()); 1355 canvas->drawBitmap(bitmap, 0, 0); 1356 SkAutoTUnref<SkPicture> picture(recorder.endRecording()); 1357 1358 SkDynamicMemoryWStream wStream; 1359 picture->serialize(&wStream, &encode_bitmap_to_data); 1360 return wStream.copyToData(); 1361} 1362 1363struct ErrorContext { 1364 int fErrors; 1365 skiatest::Reporter* fReporter; 1366}; 1367 1368static void assert_one_parse_error_cb(SkError error, void* context) { 1369 ErrorContext* errorContext = static_cast<ErrorContext*>(context); 1370 errorContext->fErrors++; 1371 // This test only expects one error, and that is a kParseError. If there are others, 1372 // there is some unknown problem. 1373 REPORTER_ASSERT_MESSAGE(errorContext->fReporter, 1 == errorContext->fErrors, 1374 "This threw more errors than expected."); 1375 REPORTER_ASSERT_MESSAGE(errorContext->fReporter, kParseError_SkError == error, 1376 SkGetLastErrorString()); 1377} 1378 1379static void test_bitmap_with_encoded_data(skiatest::Reporter* reporter) { 1380 // Create a bitmap that will be encoded. 1381 SkBitmap original; 1382 make_bm(&original, 100, 100, SK_ColorBLUE, true); 1383 SkDynamicMemoryWStream wStream; 1384 if (!SkImageEncoder::EncodeStream(&wStream, original, SkImageEncoder::kPNG_Type, 100)) { 1385 return; 1386 } 1387 SkAutoDataUnref data(wStream.copyToData()); 1388 1389 SkBitmap bm; 1390 bool installSuccess = SkInstallDiscardablePixelRef( 1391 SkDecodingImageGenerator::Create(data, SkDecodingImageGenerator::Options()), &bm); 1392 REPORTER_ASSERT(reporter, installSuccess); 1393 1394 // Write both bitmaps to pictures, and ensure that the resulting data streams are the same. 1395 // Flattening original will follow the old path of performing an encode, while flattening bm 1396 // will use the already encoded data. 1397 SkAutoDataUnref picture1(serialized_picture_from_bitmap(original)); 1398 SkAutoDataUnref picture2(serialized_picture_from_bitmap(bm)); 1399 REPORTER_ASSERT(reporter, picture1->equals(picture2)); 1400 // Now test that a parse error was generated when trying to create a new SkPicture without 1401 // providing a function to decode the bitmap. 1402 ErrorContext context; 1403 context.fErrors = 0; 1404 context.fReporter = reporter; 1405 SkSetErrorCallback(assert_one_parse_error_cb, &context); 1406 SkMemoryStream pictureStream(picture1); 1407 SkClearLastError(); 1408 SkAutoUnref pictureFromStream(SkPicture::CreateFromStream(&pictureStream, NULL)); 1409 REPORTER_ASSERT(reporter, pictureFromStream.get() != NULL); 1410 SkClearLastError(); 1411 SkSetErrorCallback(NULL, NULL); 1412} 1413 1414static void test_draw_empty(skiatest::Reporter* reporter) { 1415 SkBitmap result; 1416 make_bm(&result, 2, 2, SK_ColorBLACK, false); 1417 1418 SkCanvas canvas(result); 1419 1420 { 1421 // stock SkPicture 1422 SkPictureRecorder recorder; 1423 recorder.beginRecording(1, 1); 1424 SkAutoTUnref<SkPicture> picture(recorder.endRecording()); 1425 1426 canvas.drawPicture(picture); 1427 } 1428 1429 { 1430 // tile grid 1431 SkTileGridFactory::TileGridInfo gridInfo; 1432 gridInfo.fMargin.setEmpty(); 1433 gridInfo.fOffset.setZero(); 1434 gridInfo.fTileInterval.set(1, 1); 1435 1436 SkTileGridFactory factory(gridInfo); 1437 SkPictureRecorder recorder; 1438 recorder.beginRecording(1, 1, &factory); 1439 SkAutoTUnref<SkPicture> picture(recorder.endRecording()); 1440 1441 canvas.drawPicture(picture); 1442 } 1443 1444 { 1445 // RTree 1446 SkRTreeFactory factory; 1447 SkPictureRecorder recorder; 1448 recorder.beginRecording(1, 1, &factory); 1449 SkAutoTUnref<SkPicture> picture(recorder.endRecording()); 1450 1451 canvas.drawPicture(picture); 1452 } 1453 1454 { 1455 // quad tree 1456 SkQuadTreeFactory factory; 1457 SkPictureRecorder recorder; 1458 recorder.beginRecording(1, 1, &factory); 1459 SkAutoTUnref<SkPicture> picture(recorder.endRecording()); 1460 1461 canvas.drawPicture(picture); 1462 } 1463} 1464 1465static void test_clip_bound_opt(skiatest::Reporter* reporter) { 1466 // Test for crbug.com/229011 1467 SkRect rect1 = SkRect::MakeXYWH(SkIntToScalar(4), SkIntToScalar(4), 1468 SkIntToScalar(2), SkIntToScalar(2)); 1469 SkRect rect2 = SkRect::MakeXYWH(SkIntToScalar(7), SkIntToScalar(7), 1470 SkIntToScalar(1), SkIntToScalar(1)); 1471 SkRect rect3 = SkRect::MakeXYWH(SkIntToScalar(6), SkIntToScalar(6), 1472 SkIntToScalar(1), SkIntToScalar(1)); 1473 1474 SkPath invPath; 1475 invPath.addOval(rect1); 1476 invPath.setFillType(SkPath::kInverseEvenOdd_FillType); 1477 SkPath path; 1478 path.addOval(rect2); 1479 SkPath path2; 1480 path2.addOval(rect3); 1481 SkIRect clipBounds; 1482 SkPictureRecorder recorder; 1483 // Minimalist test set for 100% code coverage of 1484 // SkPictureRecord::updateClipConservativelyUsingBounds 1485 { 1486 SkCanvas* canvas = recorder.beginRecording(10, 10); 1487 canvas->clipPath(invPath, SkRegion::kIntersect_Op); 1488 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds); 1489 REPORTER_ASSERT(reporter, true == nonEmpty); 1490 REPORTER_ASSERT(reporter, 0 == clipBounds.fLeft); 1491 REPORTER_ASSERT(reporter, 0 == clipBounds.fTop); 1492 REPORTER_ASSERT(reporter, 10 == clipBounds.fBottom); 1493 REPORTER_ASSERT(reporter, 10 == clipBounds.fRight); 1494 } 1495 { 1496 SkCanvas* canvas = recorder.beginRecording(10, 10); 1497 canvas->clipPath(path, SkRegion::kIntersect_Op); 1498 canvas->clipPath(invPath, SkRegion::kIntersect_Op); 1499 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds); 1500 REPORTER_ASSERT(reporter, true == nonEmpty); 1501 REPORTER_ASSERT(reporter, 7 == clipBounds.fLeft); 1502 REPORTER_ASSERT(reporter, 7 == clipBounds.fTop); 1503 REPORTER_ASSERT(reporter, 8 == clipBounds.fBottom); 1504 REPORTER_ASSERT(reporter, 8 == clipBounds.fRight); 1505 } 1506 { 1507 SkCanvas* canvas = recorder.beginRecording(10, 10); 1508 canvas->clipPath(path, SkRegion::kIntersect_Op); 1509 canvas->clipPath(invPath, SkRegion::kUnion_Op); 1510 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds); 1511 REPORTER_ASSERT(reporter, true == nonEmpty); 1512 REPORTER_ASSERT(reporter, 0 == clipBounds.fLeft); 1513 REPORTER_ASSERT(reporter, 0 == clipBounds.fTop); 1514 REPORTER_ASSERT(reporter, 10 == clipBounds.fBottom); 1515 REPORTER_ASSERT(reporter, 10 == clipBounds.fRight); 1516 } 1517 { 1518 SkCanvas* canvas = recorder.beginRecording(10, 10); 1519 canvas->clipPath(path, SkRegion::kDifference_Op); 1520 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds); 1521 REPORTER_ASSERT(reporter, true == nonEmpty); 1522 REPORTER_ASSERT(reporter, 0 == clipBounds.fLeft); 1523 REPORTER_ASSERT(reporter, 0 == clipBounds.fTop); 1524 REPORTER_ASSERT(reporter, 10 == clipBounds.fBottom); 1525 REPORTER_ASSERT(reporter, 10 == clipBounds.fRight); 1526 } 1527 { 1528 SkCanvas* canvas = recorder.beginRecording(10, 10); 1529 canvas->clipPath(path, SkRegion::kReverseDifference_Op); 1530 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds); 1531 // True clip is actually empty in this case, but the best 1532 // determination we can make using only bounds as input is that the 1533 // clip is included in the bounds of 'path'. 1534 REPORTER_ASSERT(reporter, true == nonEmpty); 1535 REPORTER_ASSERT(reporter, 7 == clipBounds.fLeft); 1536 REPORTER_ASSERT(reporter, 7 == clipBounds.fTop); 1537 REPORTER_ASSERT(reporter, 8 == clipBounds.fBottom); 1538 REPORTER_ASSERT(reporter, 8 == clipBounds.fRight); 1539 } 1540 { 1541 SkCanvas* canvas = recorder.beginRecording(10, 10); 1542 canvas->clipPath(path, SkRegion::kIntersect_Op); 1543 canvas->clipPath(path2, SkRegion::kXOR_Op); 1544 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds); 1545 REPORTER_ASSERT(reporter, true == nonEmpty); 1546 REPORTER_ASSERT(reporter, 6 == clipBounds.fLeft); 1547 REPORTER_ASSERT(reporter, 6 == clipBounds.fTop); 1548 REPORTER_ASSERT(reporter, 8 == clipBounds.fBottom); 1549 REPORTER_ASSERT(reporter, 8 == clipBounds.fRight); 1550 } 1551} 1552 1553/** 1554 * A canvas that records the number of clip commands. 1555 */ 1556class ClipCountingCanvas : public SkCanvas { 1557public: 1558 ClipCountingCanvas(int width, int height) 1559 : INHERITED(width, height) 1560 , fClipCount(0){ 1561 } 1562 1563 virtual void onClipRect(const SkRect& r, 1564 SkRegion::Op op, 1565 ClipEdgeStyle edgeStyle) SK_OVERRIDE { 1566 fClipCount += 1; 1567 this->INHERITED::onClipRect(r, op, edgeStyle); 1568 } 1569 1570 virtual void onClipRRect(const SkRRect& rrect, 1571 SkRegion::Op op, 1572 ClipEdgeStyle edgeStyle)SK_OVERRIDE { 1573 fClipCount += 1; 1574 this->INHERITED::onClipRRect(rrect, op, edgeStyle); 1575 } 1576 1577 virtual void onClipPath(const SkPath& path, 1578 SkRegion::Op op, 1579 ClipEdgeStyle edgeStyle) SK_OVERRIDE { 1580 fClipCount += 1; 1581 this->INHERITED::onClipPath(path, op, edgeStyle); 1582 } 1583 1584 virtual void onClipRegion(const SkRegion& deviceRgn, SkRegion::Op op) SK_OVERRIDE { 1585 fClipCount += 1; 1586 this->INHERITED::onClipRegion(deviceRgn, op); 1587 } 1588 1589 unsigned getClipCount() const { return fClipCount; } 1590 1591private: 1592 unsigned fClipCount; 1593 1594 typedef SkCanvas INHERITED; 1595}; 1596 1597static void test_clip_expansion(skiatest::Reporter* reporter) { 1598 SkPictureRecorder recorder; 1599 SkCanvas* canvas = recorder.beginRecording(10, 10); 1600 1601 canvas->clipRect(SkRect::MakeEmpty(), SkRegion::kReplace_Op); 1602 // The following expanding clip should not be skipped. 1603 canvas->clipRect(SkRect::MakeXYWH(4, 4, 3, 3), SkRegion::kUnion_Op); 1604 // Draw something so the optimizer doesn't just fold the world. 1605 SkPaint p; 1606 p.setColor(SK_ColorBLUE); 1607 canvas->drawPaint(p); 1608 SkAutoTUnref<SkPicture> picture(recorder.endRecording()); 1609 1610 ClipCountingCanvas testCanvas(10, 10); 1611 picture->draw(&testCanvas); 1612 1613 // Both clips should be present on playback. 1614 REPORTER_ASSERT(reporter, testCanvas.getClipCount() == 2); 1615} 1616 1617static void test_hierarchical(skiatest::Reporter* reporter) { 1618 SkBitmap bm; 1619 make_bm(&bm, 10, 10, SK_ColorRED, true); 1620 1621 SkPictureRecorder recorder; 1622 1623 recorder.beginRecording(10, 10); 1624 SkAutoTUnref<SkPicture> childPlain(recorder.endRecording()); 1625 REPORTER_ASSERT(reporter, !childPlain->willPlayBackBitmaps()); // 0 1626 1627 recorder.beginRecording(10, 10)->drawBitmap(bm, 0, 0); 1628 SkAutoTUnref<SkPicture> childWithBitmap(recorder.endRecording()); 1629 REPORTER_ASSERT(reporter, childWithBitmap->willPlayBackBitmaps()); // 1 1630 1631 { 1632 SkCanvas* canvas = recorder.beginRecording(10, 10); 1633 canvas->drawPicture(childPlain); 1634 SkAutoTUnref<SkPicture> parentPP(recorder.endRecording()); 1635 REPORTER_ASSERT(reporter, !parentPP->willPlayBackBitmaps()); // 0 1636 } 1637 { 1638 SkCanvas* canvas = recorder.beginRecording(10, 10); 1639 canvas->drawPicture(childWithBitmap); 1640 SkAutoTUnref<SkPicture> parentPWB(recorder.endRecording()); 1641 REPORTER_ASSERT(reporter, parentPWB->willPlayBackBitmaps()); // 1 1642 } 1643 { 1644 SkCanvas* canvas = recorder.beginRecording(10, 10); 1645 canvas->drawBitmap(bm, 0, 0); 1646 canvas->drawPicture(childPlain); 1647 SkAutoTUnref<SkPicture> parentWBP(recorder.endRecording()); 1648 REPORTER_ASSERT(reporter, parentWBP->willPlayBackBitmaps()); // 1 1649 } 1650 { 1651 SkCanvas* canvas = recorder.beginRecording(10, 10); 1652 canvas->drawBitmap(bm, 0, 0); 1653 canvas->drawPicture(childWithBitmap); 1654 SkAutoTUnref<SkPicture> parentWBWB(recorder.endRecording()); 1655 REPORTER_ASSERT(reporter, parentWBWB->willPlayBackBitmaps()); // 2 1656 } 1657} 1658 1659static void test_gen_id(skiatest::Reporter* reporter) { 1660 1661 SkPictureRecorder recorder; 1662 recorder.beginRecording(0, 0); 1663 SkAutoTUnref<SkPicture> empty(recorder.endRecording()); 1664 1665 // Empty pictures should still have a valid ID 1666 REPORTER_ASSERT(reporter, empty->uniqueID() != SK_InvalidGenID); 1667 1668 SkCanvas* canvas = recorder.beginRecording(1, 1); 1669 canvas->drawARGB(255, 255, 255, 255); 1670 SkAutoTUnref<SkPicture> hasData(recorder.endRecording()); 1671 // picture should have a non-zero id after recording 1672 REPORTER_ASSERT(reporter, hasData->uniqueID() != SK_InvalidGenID); 1673 1674 // both pictures should have different ids 1675 REPORTER_ASSERT(reporter, hasData->uniqueID() != empty->uniqueID()); 1676} 1677 1678DEF_TEST(Picture, reporter) { 1679#ifdef SK_DEBUG 1680 test_deleting_empty_picture(); 1681 test_serializing_empty_picture(); 1682#else 1683 test_bad_bitmap(); 1684#endif 1685 test_unbalanced_save_restores(reporter); 1686 test_peephole(); 1687#if SK_SUPPORT_GPU 1688 test_gpu_veto(reporter, false); 1689 test_gpu_veto(reporter, true); 1690#endif 1691 test_has_text(reporter); 1692 test_analysis(reporter, false); 1693 test_analysis(reporter, true); 1694 test_gatherpixelrefs(reporter); 1695 test_gatherpixelrefsandrects(reporter); 1696 test_bitmap_with_encoded_data(reporter); 1697 test_draw_empty(reporter); 1698 test_clip_bound_opt(reporter); 1699 test_clip_expansion(reporter); 1700 test_hierarchical(reporter); 1701 test_gen_id(reporter); 1702} 1703 1704#if SK_SUPPORT_GPU 1705DEF_GPUTEST(GPUPicture, reporter, factory) { 1706 test_gpu_picture_optimization(reporter, factory); 1707} 1708#endif 1709 1710static void draw_bitmaps(const SkBitmap bitmap, SkCanvas* canvas) { 1711 const SkPaint paint; 1712 const SkRect rect = { 5.0f, 5.0f, 8.0f, 8.0f }; 1713 const SkIRect irect = { 2, 2, 3, 3 }; 1714 1715 // Don't care what these record, as long as they're legal. 1716 canvas->drawBitmap(bitmap, 0.0f, 0.0f, &paint); 1717 canvas->drawBitmapRectToRect(bitmap, &rect, rect, &paint, SkCanvas::kNone_DrawBitmapRectFlag); 1718 canvas->drawBitmapMatrix(bitmap, SkMatrix::I(), &paint); 1719 canvas->drawBitmapNine(bitmap, irect, rect, &paint); 1720 canvas->drawSprite(bitmap, 1, 1); 1721} 1722 1723static void test_draw_bitmaps(SkCanvas* canvas) { 1724 SkBitmap empty; 1725 draw_bitmaps(empty, canvas); 1726 empty.setInfo(SkImageInfo::MakeN32Premul(10, 10)); 1727 draw_bitmaps(empty, canvas); 1728} 1729 1730DEF_TEST(Picture_EmptyBitmap, r) { 1731 SkPictureRecorder recorder; 1732 test_draw_bitmaps(recorder.beginRecording(10, 10)); 1733 SkAutoTUnref<SkPicture> picture(recorder.endRecording()); 1734} 1735 1736DEF_TEST(Canvas_EmptyBitmap, r) { 1737 SkBitmap dst; 1738 dst.allocN32Pixels(10, 10); 1739 SkCanvas canvas(dst); 1740 1741 test_draw_bitmaps(&canvas); 1742} 1743 1744DEF_TEST(DontOptimizeSaveLayerDrawDrawRestore, reporter) { 1745 // This test is from crbug.com/344987. 1746 // The commands are: 1747 // saveLayer with paint that modifies alpha 1748 // drawBitmapRectToRect 1749 // drawBitmapRectToRect 1750 // restore 1751 // The bug was that this structure was modified so that: 1752 // - The saveLayer and restore were eliminated 1753 // - The alpha was only applied to the first drawBitmapRectToRect 1754 1755 // This test draws blue and red squares inside a 50% transparent 1756 // layer. Both colours should show up muted. 1757 // When the bug is present, the red square (the second bitmap) 1758 // shows upwith full opacity. 1759 1760 SkBitmap blueBM; 1761 make_bm(&blueBM, 100, 100, SkColorSetARGB(255, 0, 0, 255), true); 1762 SkBitmap redBM; 1763 make_bm(&redBM, 100, 100, SkColorSetARGB(255, 255, 0, 0), true); 1764 SkPaint semiTransparent; 1765 semiTransparent.setAlpha(0x80); 1766 1767 SkPictureRecorder recorder; 1768 SkCanvas* canvas = recorder.beginRecording(100, 100); 1769 canvas->drawARGB(0, 0, 0, 0); 1770 1771 canvas->saveLayer(0, &semiTransparent); 1772 canvas->drawBitmap(blueBM, 25, 25); 1773 canvas->drawBitmap(redBM, 50, 50); 1774 canvas->restore(); 1775 1776 SkAutoTUnref<SkPicture> picture(recorder.endRecording()); 1777 1778 // Now replay the picture back on another canvas 1779 // and check a couple of its pixels. 1780 SkBitmap replayBM; 1781 make_bm(&replayBM, 100, 100, SK_ColorBLACK, false); 1782 SkCanvas replayCanvas(replayBM); 1783 picture->draw(&replayCanvas); 1784 replayCanvas.flush(); 1785 1786 // With the bug present, at (55, 55) we would get a fully opaque red 1787 // intead of a dark red. 1788 REPORTER_ASSERT(reporter, replayBM.getColor(30, 30) == 0xff000080); 1789 REPORTER_ASSERT(reporter, replayBM.getColor(55, 55) == 0xff800000); 1790} 1791 1792struct CountingBBH : public SkBBoxHierarchy { 1793 mutable int searchCalls; 1794 1795 CountingBBH() : searchCalls(0) {} 1796 1797 virtual void search(const SkIRect& query, SkTDArray<void*>* results) const { 1798 this->searchCalls++; 1799 } 1800 1801 // All other methods unimplemented. 1802 virtual void insert(void* data, const SkIRect& bounds, bool defer) {} 1803 virtual void flushDeferredInserts() {} 1804 virtual void clear() {} 1805 virtual int getCount() const { return 0; } 1806 virtual int getDepth() const { return 0; } 1807 virtual void rewindInserts() {} 1808}; 1809 1810class SpoonFedBBHFactory : public SkBBHFactory { 1811public: 1812 explicit SpoonFedBBHFactory(SkBBoxHierarchy* bbh) : fBBH(bbh) {} 1813 virtual SkBBoxHierarchy* operator()(int width, int height) const { 1814 return SkRef(fBBH); 1815 } 1816private: 1817 SkBBoxHierarchy* fBBH; 1818}; 1819 1820// When the canvas clip covers the full picture, we don't need to call the BBH. 1821DEF_TEST(Picture_SkipBBH, r) { 1822 CountingBBH bbh; 1823 SpoonFedBBHFactory factory(&bbh); 1824 1825 SkPictureRecorder recorder; 1826 recorder.beginRecording(320, 240, &factory); 1827 SkAutoTUnref<const SkPicture> picture(recorder.endRecording()); 1828 1829 SkCanvas big(640, 480), small(300, 200); 1830 1831 picture->draw(&big); 1832 REPORTER_ASSERT(r, bbh.searchCalls == 0); 1833 1834 picture->draw(&small); 1835 REPORTER_ASSERT(r, bbh.searchCalls == 1); 1836} 1837