PictureTest.cpp revision d990e2f14f14c36c3d0beb303dd0953c7aa1fcfa
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 "SkDecodingImageGenerator.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 virtual void willSave() SK_OVERRIDE { 1169 ++fSaveCount; 1170 this->INHERITED::willSave(); 1171 } 1172 1173 virtual 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( 1499 SkDecodingImageGenerator::Create(data, SkDecodingImageGenerator::Options()), &bm); 1500 REPORTER_ASSERT(reporter, installSuccess); 1501 1502 // Write both bitmaps to pictures, and ensure that the resulting data streams are the same. 1503 // Flattening original will follow the old path of performing an encode, while flattening bm 1504 // will use the already encoded data. 1505 SkAutoDataUnref picture1(serialized_picture_from_bitmap(original)); 1506 SkAutoDataUnref picture2(serialized_picture_from_bitmap(bm)); 1507 REPORTER_ASSERT(reporter, picture1->equals(picture2)); 1508 // Now test that a parse error was generated when trying to create a new SkPicture without 1509 // providing a function to decode the bitmap. 1510 ErrorContext context; 1511 context.fErrors = 0; 1512 context.fReporter = reporter; 1513 SkSetErrorCallback(assert_one_parse_error_cb, &context); 1514 SkMemoryStream pictureStream(picture1); 1515 SkClearLastError(); 1516 SkAutoTUnref<SkPicture> pictureFromStream(SkPicture::CreateFromStream(&pictureStream, NULL)); 1517 REPORTER_ASSERT(reporter, pictureFromStream.get() != NULL); 1518 SkClearLastError(); 1519 SkSetErrorCallback(NULL, NULL); 1520} 1521 1522static void test_clip_bound_opt(skiatest::Reporter* reporter) { 1523 // Test for crbug.com/229011 1524 SkRect rect1 = SkRect::MakeXYWH(SkIntToScalar(4), SkIntToScalar(4), 1525 SkIntToScalar(2), SkIntToScalar(2)); 1526 SkRect rect2 = SkRect::MakeXYWH(SkIntToScalar(7), SkIntToScalar(7), 1527 SkIntToScalar(1), SkIntToScalar(1)); 1528 SkRect rect3 = SkRect::MakeXYWH(SkIntToScalar(6), SkIntToScalar(6), 1529 SkIntToScalar(1), SkIntToScalar(1)); 1530 1531 SkPath invPath; 1532 invPath.addOval(rect1); 1533 invPath.setFillType(SkPath::kInverseEvenOdd_FillType); 1534 SkPath path; 1535 path.addOval(rect2); 1536 SkPath path2; 1537 path2.addOval(rect3); 1538 SkIRect clipBounds; 1539 SkPictureRecorder recorder; 1540 1541 // Testing conservative-raster-clip that is enabled by PictureRecord 1542 { 1543 SkCanvas* canvas = recorder.beginRecording(10, 10); 1544 canvas->clipPath(invPath, SkRegion::kIntersect_Op); 1545 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds); 1546 REPORTER_ASSERT(reporter, true == nonEmpty); 1547 REPORTER_ASSERT(reporter, 0 == clipBounds.fLeft); 1548 REPORTER_ASSERT(reporter, 0 == clipBounds.fTop); 1549 REPORTER_ASSERT(reporter, 10 == clipBounds.fBottom); 1550 REPORTER_ASSERT(reporter, 10 == clipBounds.fRight); 1551 } 1552 { 1553 SkCanvas* canvas = recorder.beginRecording(10, 10); 1554 canvas->clipPath(path, SkRegion::kIntersect_Op); 1555 canvas->clipPath(invPath, SkRegion::kIntersect_Op); 1556 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds); 1557 REPORTER_ASSERT(reporter, true == nonEmpty); 1558 REPORTER_ASSERT(reporter, 7 == clipBounds.fLeft); 1559 REPORTER_ASSERT(reporter, 7 == clipBounds.fTop); 1560 REPORTER_ASSERT(reporter, 8 == clipBounds.fBottom); 1561 REPORTER_ASSERT(reporter, 8 == clipBounds.fRight); 1562 } 1563 { 1564 SkCanvas* canvas = recorder.beginRecording(10, 10); 1565 canvas->clipPath(path, SkRegion::kIntersect_Op); 1566 canvas->clipPath(invPath, SkRegion::kUnion_Op); 1567 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds); 1568 REPORTER_ASSERT(reporter, true == nonEmpty); 1569 REPORTER_ASSERT(reporter, 0 == clipBounds.fLeft); 1570 REPORTER_ASSERT(reporter, 0 == clipBounds.fTop); 1571 REPORTER_ASSERT(reporter, 10 == clipBounds.fBottom); 1572 REPORTER_ASSERT(reporter, 10 == clipBounds.fRight); 1573 } 1574 { 1575 SkCanvas* canvas = recorder.beginRecording(10, 10); 1576 canvas->clipPath(path, SkRegion::kDifference_Op); 1577 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds); 1578 REPORTER_ASSERT(reporter, true == nonEmpty); 1579 REPORTER_ASSERT(reporter, 0 == clipBounds.fLeft); 1580 REPORTER_ASSERT(reporter, 0 == clipBounds.fTop); 1581 REPORTER_ASSERT(reporter, 10 == clipBounds.fBottom); 1582 REPORTER_ASSERT(reporter, 10 == clipBounds.fRight); 1583 } 1584 { 1585 SkCanvas* canvas = recorder.beginRecording(10, 10); 1586 canvas->clipPath(path, SkRegion::kReverseDifference_Op); 1587 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds); 1588 // True clip is actually empty in this case, but the best 1589 // determination we can make using only bounds as input is that the 1590 // clip is included in the bounds of 'path'. 1591 REPORTER_ASSERT(reporter, true == nonEmpty); 1592 REPORTER_ASSERT(reporter, 7 == clipBounds.fLeft); 1593 REPORTER_ASSERT(reporter, 7 == clipBounds.fTop); 1594 REPORTER_ASSERT(reporter, 8 == clipBounds.fBottom); 1595 REPORTER_ASSERT(reporter, 8 == clipBounds.fRight); 1596 } 1597 { 1598 SkCanvas* canvas = recorder.beginRecording(10, 10); 1599 canvas->clipPath(path, SkRegion::kIntersect_Op); 1600 canvas->clipPath(path2, SkRegion::kXOR_Op); 1601 bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds); 1602 REPORTER_ASSERT(reporter, true == nonEmpty); 1603 REPORTER_ASSERT(reporter, 6 == clipBounds.fLeft); 1604 REPORTER_ASSERT(reporter, 6 == clipBounds.fTop); 1605 REPORTER_ASSERT(reporter, 8 == clipBounds.fBottom); 1606 REPORTER_ASSERT(reporter, 8 == clipBounds.fRight); 1607 } 1608} 1609 1610/** 1611 * A canvas that records the number of clip commands. 1612 */ 1613class ClipCountingCanvas : public SkCanvas { 1614public: 1615 ClipCountingCanvas(int width, int height) 1616 : INHERITED(width, height) 1617 , fClipCount(0){ 1618 } 1619 1620 virtual void onClipRect(const SkRect& r, 1621 SkRegion::Op op, 1622 ClipEdgeStyle edgeStyle) SK_OVERRIDE { 1623 fClipCount += 1; 1624 this->INHERITED::onClipRect(r, op, edgeStyle); 1625 } 1626 1627 virtual void onClipRRect(const SkRRect& rrect, 1628 SkRegion::Op op, 1629 ClipEdgeStyle edgeStyle)SK_OVERRIDE { 1630 fClipCount += 1; 1631 this->INHERITED::onClipRRect(rrect, op, edgeStyle); 1632 } 1633 1634 virtual void onClipPath(const SkPath& path, 1635 SkRegion::Op op, 1636 ClipEdgeStyle edgeStyle) SK_OVERRIDE { 1637 fClipCount += 1; 1638 this->INHERITED::onClipPath(path, op, edgeStyle); 1639 } 1640 1641 virtual void onClipRegion(const SkRegion& deviceRgn, SkRegion::Op op) SK_OVERRIDE { 1642 fClipCount += 1; 1643 this->INHERITED::onClipRegion(deviceRgn, op); 1644 } 1645 1646 unsigned getClipCount() const { return fClipCount; } 1647 1648private: 1649 unsigned fClipCount; 1650 1651 typedef SkCanvas INHERITED; 1652}; 1653 1654static void test_clip_expansion(skiatest::Reporter* reporter) { 1655 SkPictureRecorder recorder; 1656 SkCanvas* canvas = recorder.beginRecording(10, 10); 1657 1658 canvas->clipRect(SkRect::MakeEmpty(), SkRegion::kReplace_Op); 1659 // The following expanding clip should not be skipped. 1660 canvas->clipRect(SkRect::MakeXYWH(4, 4, 3, 3), SkRegion::kUnion_Op); 1661 // Draw something so the optimizer doesn't just fold the world. 1662 SkPaint p; 1663 p.setColor(SK_ColorBLUE); 1664 canvas->drawPaint(p); 1665 SkAutoTUnref<SkPicture> picture(recorder.endRecording()); 1666 1667 ClipCountingCanvas testCanvas(10, 10); 1668 picture->playback(&testCanvas); 1669 1670 // Both clips should be present on playback. 1671 REPORTER_ASSERT(reporter, testCanvas.getClipCount() == 2); 1672} 1673 1674static void test_hierarchical(skiatest::Reporter* reporter) { 1675 SkBitmap bm; 1676 make_bm(&bm, 10, 10, SK_ColorRED, true); 1677 1678 SkPictureRecorder recorder; 1679 1680 recorder.beginRecording(10, 10); 1681 SkAutoTUnref<SkPicture> childPlain(recorder.endRecording()); 1682 REPORTER_ASSERT(reporter, !childPlain->willPlayBackBitmaps()); // 0 1683 1684 recorder.beginRecording(10, 10)->drawBitmap(bm, 0, 0); 1685 SkAutoTUnref<SkPicture> childWithBitmap(recorder.endRecording()); 1686 REPORTER_ASSERT(reporter, childWithBitmap->willPlayBackBitmaps()); // 1 1687 1688 { 1689 SkCanvas* canvas = recorder.beginRecording(10, 10); 1690 canvas->drawPicture(childPlain); 1691 SkAutoTUnref<SkPicture> parentPP(recorder.endRecording()); 1692 REPORTER_ASSERT(reporter, !parentPP->willPlayBackBitmaps()); // 0 1693 } 1694 { 1695 SkCanvas* canvas = recorder.beginRecording(10, 10); 1696 canvas->drawPicture(childWithBitmap); 1697 SkAutoTUnref<SkPicture> parentPWB(recorder.endRecording()); 1698 REPORTER_ASSERT(reporter, parentPWB->willPlayBackBitmaps()); // 1 1699 } 1700 { 1701 SkCanvas* canvas = recorder.beginRecording(10, 10); 1702 canvas->drawBitmap(bm, 0, 0); 1703 canvas->drawPicture(childPlain); 1704 SkAutoTUnref<SkPicture> parentWBP(recorder.endRecording()); 1705 REPORTER_ASSERT(reporter, parentWBP->willPlayBackBitmaps()); // 1 1706 } 1707 { 1708 SkCanvas* canvas = recorder.beginRecording(10, 10); 1709 canvas->drawBitmap(bm, 0, 0); 1710 canvas->drawPicture(childWithBitmap); 1711 SkAutoTUnref<SkPicture> parentWBWB(recorder.endRecording()); 1712 REPORTER_ASSERT(reporter, parentWBWB->willPlayBackBitmaps()); // 2 1713 } 1714} 1715 1716static void test_gen_id(skiatest::Reporter* reporter) { 1717 1718 SkPictureRecorder recorder; 1719 recorder.beginRecording(0, 0); 1720 SkAutoTUnref<SkPicture> empty(recorder.endRecording()); 1721 1722 // Empty pictures should still have a valid ID 1723 REPORTER_ASSERT(reporter, empty->uniqueID() != SK_InvalidGenID); 1724 1725 SkCanvas* canvas = recorder.beginRecording(1, 1); 1726 canvas->drawARGB(255, 255, 255, 255); 1727 SkAutoTUnref<SkPicture> hasData(recorder.endRecording()); 1728 // picture should have a non-zero id after recording 1729 REPORTER_ASSERT(reporter, hasData->uniqueID() != SK_InvalidGenID); 1730 1731 // both pictures should have different ids 1732 REPORTER_ASSERT(reporter, hasData->uniqueID() != empty->uniqueID()); 1733} 1734 1735static void test_bytes_used(skiatest::Reporter* reporter) { 1736 SkPictureRecorder recorder; 1737 1738 recorder.beginRecording(0, 0); 1739 SkAutoTUnref<SkPicture> empty(recorder.endRecording()); 1740 1741 // Sanity check to make sure we aren't under-measuring. 1742 REPORTER_ASSERT(reporter, SkPictureUtils::ApproximateBytesUsed(empty.get()) >= 1743 sizeof(SkPicture) + sizeof(SkRecord)); 1744 1745 // Protect against any unintentional bloat. 1746 size_t approxUsed = SkPictureUtils::ApproximateBytesUsed(empty.get()); 1747 REPORTER_ASSERT(reporter, approxUsed <= 136); 1748 1749 // Sanity check of nested SkPictures. 1750 SkPictureRecorder r2; 1751 r2.beginRecording(0, 0); 1752 r2.getRecordingCanvas()->drawPicture(empty.get()); 1753 SkAutoTUnref<SkPicture> nested(r2.endRecording()); 1754 1755 REPORTER_ASSERT(reporter, SkPictureUtils::ApproximateBytesUsed(nested.get()) > 1756 SkPictureUtils::ApproximateBytesUsed(empty.get())); 1757} 1758 1759DEF_TEST(Picture, reporter) { 1760#ifdef SK_DEBUG 1761 test_deleting_empty_picture(); 1762 test_serializing_empty_picture(); 1763#else 1764 test_bad_bitmap(); 1765#endif 1766 test_unbalanced_save_restores(reporter); 1767 test_peephole(); 1768#if SK_SUPPORT_GPU 1769 test_gpu_veto(reporter); 1770#endif 1771 test_has_text(reporter); 1772 test_analysis(reporter); 1773 test_gatherpixelrefs(reporter); 1774 test_gatherpixelrefsandrects(reporter); 1775 test_bitmap_with_encoded_data(reporter); 1776 test_clip_bound_opt(reporter); 1777 test_clip_expansion(reporter); 1778 test_hierarchical(reporter); 1779 test_gen_id(reporter); 1780 test_savelayer_extraction(reporter); 1781 test_bytes_used(reporter); 1782} 1783 1784static void draw_bitmaps(const SkBitmap bitmap, SkCanvas* canvas) { 1785 const SkPaint paint; 1786 const SkRect rect = { 5.0f, 5.0f, 8.0f, 8.0f }; 1787 const SkIRect irect = { 2, 2, 3, 3 }; 1788 1789 // Don't care what these record, as long as they're legal. 1790 canvas->drawBitmap(bitmap, 0.0f, 0.0f, &paint); 1791 canvas->drawBitmapRectToRect(bitmap, &rect, rect, &paint, SkCanvas::kNone_DrawBitmapRectFlag); 1792 canvas->drawBitmapNine(bitmap, irect, rect, &paint); 1793 canvas->drawSprite(bitmap, 1, 1); 1794} 1795 1796static void test_draw_bitmaps(SkCanvas* canvas) { 1797 SkBitmap empty; 1798 draw_bitmaps(empty, canvas); 1799 empty.setInfo(SkImageInfo::MakeN32Premul(10, 10)); 1800 draw_bitmaps(empty, canvas); 1801} 1802 1803DEF_TEST(Picture_EmptyBitmap, r) { 1804 SkPictureRecorder recorder; 1805 test_draw_bitmaps(recorder.beginRecording(10, 10)); 1806 SkAutoTUnref<SkPicture> picture(recorder.endRecording()); 1807} 1808 1809DEF_TEST(Canvas_EmptyBitmap, r) { 1810 SkBitmap dst; 1811 dst.allocN32Pixels(10, 10); 1812 SkCanvas canvas(dst); 1813 1814 test_draw_bitmaps(&canvas); 1815} 1816 1817DEF_TEST(DontOptimizeSaveLayerDrawDrawRestore, reporter) { 1818 // This test is from crbug.com/344987. 1819 // The commands are: 1820 // saveLayer with paint that modifies alpha 1821 // drawBitmapRectToRect 1822 // drawBitmapRectToRect 1823 // restore 1824 // The bug was that this structure was modified so that: 1825 // - The saveLayer and restore were eliminated 1826 // - The alpha was only applied to the first drawBitmapRectToRect 1827 1828 // This test draws blue and red squares inside a 50% transparent 1829 // layer. Both colours should show up muted. 1830 // When the bug is present, the red square (the second bitmap) 1831 // shows upwith full opacity. 1832 1833 SkBitmap blueBM; 1834 make_bm(&blueBM, 100, 100, SkColorSetARGB(255, 0, 0, 255), true); 1835 SkBitmap redBM; 1836 make_bm(&redBM, 100, 100, SkColorSetARGB(255, 255, 0, 0), true); 1837 SkPaint semiTransparent; 1838 semiTransparent.setAlpha(0x80); 1839 1840 SkPictureRecorder recorder; 1841 SkCanvas* canvas = recorder.beginRecording(100, 100); 1842 canvas->drawARGB(0, 0, 0, 0); 1843 1844 canvas->saveLayer(0, &semiTransparent); 1845 canvas->drawBitmap(blueBM, 25, 25); 1846 canvas->drawBitmap(redBM, 50, 50); 1847 canvas->restore(); 1848 1849 SkAutoTUnref<SkPicture> picture(recorder.endRecording()); 1850 1851 // Now replay the picture back on another canvas 1852 // and check a couple of its pixels. 1853 SkBitmap replayBM; 1854 make_bm(&replayBM, 100, 100, SK_ColorBLACK, false); 1855 SkCanvas replayCanvas(replayBM); 1856 picture->playback(&replayCanvas); 1857 replayCanvas.flush(); 1858 1859 // With the bug present, at (55, 55) we would get a fully opaque red 1860 // intead of a dark red. 1861 REPORTER_ASSERT(reporter, replayBM.getColor(30, 30) == 0xff000080); 1862 REPORTER_ASSERT(reporter, replayBM.getColor(55, 55) == 0xff800000); 1863} 1864 1865struct CountingBBH : public SkBBoxHierarchy { 1866 mutable int searchCalls; 1867 1868 CountingBBH() : searchCalls(0) {} 1869 1870 virtual void search(const SkRect& query, SkTDArray<unsigned>* results) const SK_OVERRIDE { 1871 this->searchCalls++; 1872 } 1873 1874 virtual void insert(SkAutoTMalloc<SkRect>*, int) SK_OVERRIDE {} 1875 virtual size_t bytesUsed() const { return 0; } 1876}; 1877 1878class SpoonFedBBHFactory : public SkBBHFactory { 1879public: 1880 explicit SpoonFedBBHFactory(SkBBoxHierarchy* bbh) : fBBH(bbh) {} 1881 SkBBoxHierarchy* operator()(const SkRect&) const SK_OVERRIDE { 1882 return SkRef(fBBH); 1883 } 1884private: 1885 SkBBoxHierarchy* fBBH; 1886}; 1887 1888// When the canvas clip covers the full picture, we don't need to call the BBH. 1889DEF_TEST(Picture_SkipBBH, r) { 1890 CountingBBH bbh; 1891 SpoonFedBBHFactory factory(&bbh); 1892 1893 SkPictureRecorder recorder; 1894 recorder.beginRecording(320, 240, &factory); 1895 SkAutoTUnref<const SkPicture> picture(recorder.endRecording()); 1896 1897 SkCanvas big(640, 480), small(300, 200); 1898 1899 picture->playback(&big); 1900 REPORTER_ASSERT(r, bbh.searchCalls == 0); 1901 1902 picture->playback(&small); 1903 REPORTER_ASSERT(r, bbh.searchCalls == 1); 1904} 1905 1906DEF_TEST(Picture_BitmapLeak, r) { 1907 SkBitmap mut, immut; 1908 mut.allocN32Pixels(300, 200); 1909 immut.allocN32Pixels(300, 200); 1910 immut.setImmutable(); 1911 SkASSERT(!mut.isImmutable()); 1912 SkASSERT(immut.isImmutable()); 1913 1914 // No one can hold a ref on our pixels yet. 1915 REPORTER_ASSERT(r, mut.pixelRef()->unique()); 1916 REPORTER_ASSERT(r, immut.pixelRef()->unique()); 1917 1918 SkAutoTUnref<const SkPicture> pic; 1919 { 1920 // we want the recorder to go out of scope before our subsequent checks, so we 1921 // place it inside local braces. 1922 SkPictureRecorder rec; 1923 SkCanvas* canvas = rec.beginRecording(1920, 1200); 1924 canvas->drawBitmap(mut, 0, 0); 1925 canvas->drawBitmap(immut, 800, 600); 1926 pic.reset(rec.endRecording()); 1927 } 1928 1929 // The picture shares the immutable pixels but copies the mutable ones. 1930 REPORTER_ASSERT(r, mut.pixelRef()->unique()); 1931 REPORTER_ASSERT(r, !immut.pixelRef()->unique()); 1932 1933 // When the picture goes away, it's just our bitmaps holding the refs. 1934 pic.reset(NULL); 1935 REPORTER_ASSERT(r, mut.pixelRef()->unique()); 1936 REPORTER_ASSERT(r, immut.pixelRef()->unique()); 1937} 1938