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