PictureRenderer.cpp revision 363e546ed626b6dbbc42f5db87b3594bc0b5944b
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 "PictureRenderer.h" 9#include "picture_utils.h" 10#include "SamplePipeControllers.h" 11#include "SkCanvas.h" 12#include "SkDevice.h" 13#include "SkGPipe.h" 14#if SK_SUPPORT_GPU 15#include "SkGpuDevice.h" 16#endif 17#include "SkGraphics.h" 18#include "SkImageEncoder.h" 19#include "SkMaskFilter.h" 20#include "SkMatrix.h" 21#include "SkPicture.h" 22#include "SkRTree.h" 23#include "SkScalar.h" 24#include "SkStream.h" 25#include "SkString.h" 26#include "SkTemplates.h" 27#include "SkTileGridPicture.h" 28#include "SkTDArray.h" 29#include "SkThreadUtils.h" 30#include "SkTypes.h" 31#include "SkData.h" 32#include "SkPictureUtils.h" 33 34namespace sk_tools { 35 36enum { 37 kDefaultTileWidth = 256, 38 kDefaultTileHeight = 256 39}; 40 41void PictureRenderer::init(SkPicture* pict) { 42 SkASSERT(NULL == fPicture); 43 SkASSERT(NULL == fCanvas.get()); 44 if (fPicture != NULL || NULL != fCanvas.get()) { 45 return; 46 } 47 48 SkASSERT(pict != NULL); 49 if (NULL == pict) { 50 return; 51 } 52 53 fPicture = pict; 54 fPicture->ref(); 55 fCanvas.reset(this->setupCanvas()); 56} 57 58class FlagsDrawFilter : public SkDrawFilter { 59public: 60 FlagsDrawFilter(PictureRenderer::DrawFilterFlags* flags) : 61 fFlags(flags) {} 62 63 virtual bool filter(SkPaint* paint, Type t) { 64 paint->setFlags(paint->getFlags() & ~fFlags[t] & SkPaint::kAllFlags); 65 if (PictureRenderer::kBlur_DrawFilterFlag & fFlags[t]) { 66 SkMaskFilter* maskFilter = paint->getMaskFilter(); 67 SkMaskFilter::BlurInfo blurInfo; 68 if (maskFilter && maskFilter->asABlur(&blurInfo)) { 69 paint->setMaskFilter(NULL); 70 } 71 } 72 if (PictureRenderer::kHinting_DrawFilterFlag & fFlags[t]) { 73 paint->setHinting(SkPaint::kNo_Hinting); 74 } else if (PictureRenderer::kSlightHinting_DrawFilterFlag & fFlags[t]) { 75 paint->setHinting(SkPaint::kSlight_Hinting); 76 } 77 return true; 78 } 79 80private: 81 PictureRenderer::DrawFilterFlags* fFlags; 82}; 83 84static void setUpFilter(SkCanvas* canvas, PictureRenderer::DrawFilterFlags* drawFilters) { 85 if (drawFilters && !canvas->getDrawFilter()) { 86 canvas->setDrawFilter(SkNEW_ARGS(FlagsDrawFilter, (drawFilters)))->unref(); 87 if (drawFilters[0] & PictureRenderer::kAAClip_DrawFilterFlag) { 88 canvas->setAllowSoftClip(false); 89 } 90 } 91} 92 93SkCanvas* PictureRenderer::setupCanvas() { 94 const int width = this->getViewWidth(); 95 const int height = this->getViewHeight(); 96 return this->setupCanvas(width, height); 97} 98 99SkCanvas* PictureRenderer::setupCanvas(int width, int height) { 100 SkCanvas* canvas; 101 switch(fDeviceType) { 102 case kBitmap_DeviceType: { 103 SkBitmap bitmap; 104 sk_tools::setup_bitmap(&bitmap, width, height); 105 canvas = SkNEW_ARGS(SkCanvas, (bitmap)); 106 } 107 break; 108#if SK_SUPPORT_GPU 109 case kGPU_DeviceType: { 110 SkAutoTUnref<SkGpuDevice> device(SkNEW_ARGS(SkGpuDevice, 111 (fGrContext, SkBitmap::kARGB_8888_Config, 112 width, height))); 113 canvas = SkNEW_ARGS(SkCanvas, (device.get())); 114 } 115 break; 116#endif 117 default: 118 SkASSERT(0); 119 return NULL; 120 } 121 setUpFilter(canvas, fDrawFilters); 122 this->scaleToScaleFactor(canvas); 123 return canvas; 124} 125 126void PictureRenderer::scaleToScaleFactor(SkCanvas* canvas) { 127 SkASSERT(canvas != NULL); 128 if (fScaleFactor != SK_Scalar1) { 129 canvas->scale(fScaleFactor, fScaleFactor); 130 } 131} 132 133void PictureRenderer::end() { 134 this->resetState(); 135 SkSafeUnref(fPicture); 136 fPicture = NULL; 137 fCanvas.reset(NULL); 138} 139 140int PictureRenderer::getViewWidth() { 141 SkASSERT(fPicture != NULL); 142 int width = fPicture->width(); 143 if (fViewport.width() > 0) { 144 width = SkMin32(width, fViewport.width()); 145 } 146 return width; 147} 148 149int PictureRenderer::getViewHeight() { 150 SkASSERT(fPicture != NULL); 151 int height = fPicture->height(); 152 if (fViewport.height() > 0) { 153 height = SkMin32(height, fViewport.height()); 154 } 155 return height; 156} 157 158/** Converts fPicture to a picture that uses a BBoxHierarchy. 159 * PictureRenderer subclasses that are used to test picture playback 160 * should call this method during init. 161 */ 162void PictureRenderer::buildBBoxHierarchy() { 163 SkASSERT(NULL != fPicture); 164 if (kNone_BBoxHierarchyType != fBBoxHierarchyType && NULL != fPicture) { 165 SkPicture* newPicture = this->createPicture(); 166 SkCanvas* recorder = newPicture->beginRecording(fPicture->width(), fPicture->height(), 167 this->recordFlags()); 168 fPicture->draw(recorder); 169 newPicture->endRecording(); 170 fPicture->unref(); 171 fPicture = newPicture; 172 } 173} 174 175void PictureRenderer::resetState() { 176#if SK_SUPPORT_GPU 177 if (this->isUsingGpuDevice()) { 178 SkGLContext* glContext = fGrContextFactory.getGLContext( 179 GrContextFactory::kNative_GLContextType); 180 181 SkASSERT(glContext != NULL); 182 if (NULL == glContext) { 183 return; 184 } 185 186 fGrContext->flush(); 187 SK_GL(*glContext, Finish()); 188 } 189#endif 190} 191 192uint32_t PictureRenderer::recordFlags() { 193 return kNone_BBoxHierarchyType == fBBoxHierarchyType ? 0 : 194 SkPicture::kOptimizeForClippedPlayback_RecordingFlag; 195} 196 197/** 198 * Write the canvas to the specified path. 199 * @param canvas Must be non-null. Canvas to be written to a file. 200 * @param path Path for the file to be written. Should have no extension; write() will append 201 * an appropriate one. Passed in by value so it can be modified. 202 * @return bool True if the Canvas is written to a file. 203 */ 204static bool write(SkCanvas* canvas, SkString path) { 205 SkASSERT(canvas != NULL); 206 if (NULL == canvas) { 207 return false; 208 } 209 210 SkBitmap bitmap; 211 SkISize size = canvas->getDeviceSize(); 212 sk_tools::setup_bitmap(&bitmap, size.width(), size.height()); 213 214 canvas->readPixels(&bitmap, 0, 0); 215 sk_tools::force_all_opaque(bitmap); 216 217 // Since path is passed in by value, it is okay to modify it. 218 path.append(".png"); 219 return SkImageEncoder::EncodeFile(path.c_str(), bitmap, SkImageEncoder::kPNG_Type, 100); 220} 221 222/** 223 * If path is non NULL, append number to it, and call write(SkCanvas*, SkString) to write the 224 * provided canvas to a file. Returns true if path is NULL or if write() succeeds. 225 */ 226static bool writeAppendNumber(SkCanvas* canvas, const SkString* path, int number) { 227 if (NULL == path) { 228 return true; 229 } 230 SkString pathWithNumber(*path); 231 pathWithNumber.appendf("%i", number); 232 return write(canvas, pathWithNumber); 233} 234 235/////////////////////////////////////////////////////////////////////////////////////////////// 236 237SkCanvas* RecordPictureRenderer::setupCanvas(int width, int height) { 238 // defer the canvas setup until the render step 239 return NULL; 240} 241 242static bool PNGEncodeBitmapToStream(SkWStream* wStream, const SkBitmap& bm) { 243 return SkImageEncoder::EncodeStream(wStream, bm, SkImageEncoder::kPNG_Type, 100); 244} 245 246bool RecordPictureRenderer::render(const SkString* path, SkBitmap** out) { 247 SkAutoTUnref<SkPicture> replayer(this->createPicture()); 248 SkCanvas* recorder = replayer->beginRecording(this->getViewWidth(), this->getViewHeight(), 249 this->recordFlags()); 250 this->scaleToScaleFactor(recorder); 251 fPicture->draw(recorder); 252 replayer->endRecording(); 253 if (path != NULL) { 254 // Record the new picture as a new SKP with PNG encoded bitmaps. 255 SkString skpPath(*path); 256 // ".skp" was removed from 'path' before being passed in here. 257 skpPath.append(".skp"); 258 SkFILEWStream stream(skpPath.c_str()); 259 replayer->serialize(&stream, &PNGEncodeBitmapToStream); 260 return true; 261 } 262 return false; 263} 264 265SkString RecordPictureRenderer::getConfigNameInternal() { 266 return SkString("record"); 267} 268 269/////////////////////////////////////////////////////////////////////////////////////////////// 270 271bool PipePictureRenderer::render(const SkString* path, SkBitmap** out) { 272 SkASSERT(fCanvas.get() != NULL); 273 SkASSERT(fPicture != NULL); 274 if (NULL == fCanvas.get() || NULL == fPicture) { 275 return false; 276 } 277 278 PipeController pipeController(fCanvas.get()); 279 SkGPipeWriter writer; 280 SkCanvas* pipeCanvas = writer.startRecording(&pipeController); 281 pipeCanvas->drawPicture(*fPicture); 282 writer.endRecording(); 283 fCanvas->flush(); 284 if (NULL != path) { 285 return write(fCanvas, *path); 286 } 287 if (NULL != out) { 288 *out = SkNEW(SkBitmap); 289 setup_bitmap(*out, fPicture->width(), fPicture->height()); 290 fCanvas->readPixels(*out, 0, 0); 291 } 292 return true; 293} 294 295SkString PipePictureRenderer::getConfigNameInternal() { 296 return SkString("pipe"); 297} 298 299/////////////////////////////////////////////////////////////////////////////////////////////// 300 301void SimplePictureRenderer::init(SkPicture* picture) { 302 INHERITED::init(picture); 303 this->buildBBoxHierarchy(); 304} 305 306bool SimplePictureRenderer::render(const SkString* path, SkBitmap** out) { 307 SkASSERT(fCanvas.get() != NULL); 308 SkASSERT(fPicture != NULL); 309 if (NULL == fCanvas.get() || NULL == fPicture) { 310 return false; 311 } 312 313 fCanvas->drawPicture(*fPicture); 314 fCanvas->flush(); 315 if (NULL != path) { 316 return write(fCanvas, *path); 317 } 318 319 if (NULL != out) { 320 *out = SkNEW(SkBitmap); 321 setup_bitmap(*out, fPicture->width(), fPicture->height()); 322 fCanvas->readPixels(*out, 0, 0); 323 } 324 325 return true; 326} 327 328SkString SimplePictureRenderer::getConfigNameInternal() { 329 return SkString("simple"); 330} 331 332/////////////////////////////////////////////////////////////////////////////////////////////// 333 334TiledPictureRenderer::TiledPictureRenderer() 335 : fTileWidth(kDefaultTileWidth) 336 , fTileHeight(kDefaultTileHeight) 337 , fTileWidthPercentage(0.0) 338 , fTileHeightPercentage(0.0) 339 , fTileMinPowerOf2Width(0) 340 , fCurrentTileOffset(-1) 341 , fTilesX(0) 342 , fTilesY(0) { } 343 344void TiledPictureRenderer::init(SkPicture* pict) { 345 SkASSERT(pict != NULL); 346 SkASSERT(0 == fTileRects.count()); 347 if (NULL == pict || fTileRects.count() != 0) { 348 return; 349 } 350 351 // Do not call INHERITED::init(), which would create a (potentially large) canvas which is not 352 // used by bench_pictures. 353 fPicture = pict; 354 fPicture->ref(); 355 this->buildBBoxHierarchy(); 356 357 if (fTileWidthPercentage > 0) { 358 fTileWidth = sk_float_ceil2int(float(fTileWidthPercentage * fPicture->width() / 100)); 359 } 360 if (fTileHeightPercentage > 0) { 361 fTileHeight = sk_float_ceil2int(float(fTileHeightPercentage * fPicture->height() / 100)); 362 } 363 364 if (fTileMinPowerOf2Width > 0) { 365 this->setupPowerOf2Tiles(); 366 } else { 367 this->setupTiles(); 368 } 369 fCanvas.reset(this->setupCanvas(fTileWidth, fTileHeight)); 370 // Initialize to -1 so that the first call to nextTile will set this up to draw tile 0 on the 371 // first call to drawCurrentTile. 372 fCurrentTileOffset = -1; 373} 374 375void TiledPictureRenderer::end() { 376 fTileRects.reset(); 377 this->INHERITED::end(); 378} 379 380void TiledPictureRenderer::setupTiles() { 381 // Only use enough tiles to cover the viewport 382 const int width = this->getViewWidth(); 383 const int height = this->getViewHeight(); 384 385 fTilesX = fTilesY = 0; 386 for (int tile_y_start = 0; tile_y_start < height; tile_y_start += fTileHeight) { 387 fTilesY++; 388 for (int tile_x_start = 0; tile_x_start < width; tile_x_start += fTileWidth) { 389 if (0 == tile_y_start) { 390 // Only count tiles in the X direction on the first pass. 391 fTilesX++; 392 } 393 *fTileRects.append() = SkRect::MakeXYWH(SkIntToScalar(tile_x_start), 394 SkIntToScalar(tile_y_start), 395 SkIntToScalar(fTileWidth), 396 SkIntToScalar(fTileHeight)); 397 } 398 } 399} 400 401bool TiledPictureRenderer::tileDimensions(int &x, int &y) { 402 if (fTileRects.count() == 0 || NULL == fPicture) { 403 return false; 404 } 405 x = fTilesX; 406 y = fTilesY; 407 return true; 408} 409 410// The goal of the powers of two tiles is to minimize the amount of wasted tile 411// space in the width-wise direction and then minimize the number of tiles. The 412// constraints are that every tile must have a pixel width that is a power of 413// two and also be of some minimal width (that is also a power of two). 414// 415// This is solved by first taking our picture size and rounding it up to the 416// multiple of the minimal width. The binary representation of this rounded 417// value gives us the tiles we need: a bit of value one means we need a tile of 418// that size. 419void TiledPictureRenderer::setupPowerOf2Tiles() { 420 // Only use enough tiles to cover the viewport 421 const int width = this->getViewWidth(); 422 const int height = this->getViewHeight(); 423 424 int rounded_value = width; 425 if (width % fTileMinPowerOf2Width != 0) { 426 rounded_value = width - (width % fTileMinPowerOf2Width) + fTileMinPowerOf2Width; 427 } 428 429 int num_bits = SkScalarCeilToInt(SkScalarLog2(SkIntToScalar(width))); 430 int largest_possible_tile_size = 1 << num_bits; 431 432 fTilesX = fTilesY = 0; 433 // The tile height is constant for a particular picture. 434 for (int tile_y_start = 0; tile_y_start < height; tile_y_start += fTileHeight) { 435 fTilesY++; 436 int tile_x_start = 0; 437 int current_width = largest_possible_tile_size; 438 // Set fTileWidth to be the width of the widest tile, so that each canvas is large enough 439 // to draw each tile. 440 fTileWidth = current_width; 441 442 while (current_width >= fTileMinPowerOf2Width) { 443 // It is very important this is a bitwise AND. 444 if (current_width & rounded_value) { 445 if (0 == tile_y_start) { 446 // Only count tiles in the X direction on the first pass. 447 fTilesX++; 448 } 449 *fTileRects.append() = SkRect::MakeXYWH(SkIntToScalar(tile_x_start), 450 SkIntToScalar(tile_y_start), 451 SkIntToScalar(current_width), 452 SkIntToScalar(fTileHeight)); 453 tile_x_start += current_width; 454 } 455 456 current_width >>= 1; 457 } 458 } 459} 460 461/** 462 * Draw the specified playback to the canvas translated to rectangle provided, so that this mini 463 * canvas represents the rectangle's portion of the overall picture. 464 * Saves and restores so that the initial clip and matrix return to their state before this function 465 * is called. 466 */ 467template<class T> 468static void DrawTileToCanvas(SkCanvas* canvas, const SkRect& tileRect, T* playback) { 469 int saveCount = canvas->save(); 470 // Translate so that we draw the correct portion of the picture. 471 // Perform a postTranslate so that the scaleFactor does not interfere with the positioning. 472 SkMatrix mat(canvas->getTotalMatrix()); 473 mat.postTranslate(-tileRect.fLeft, -tileRect.fTop); 474 canvas->setMatrix(mat); 475 playback->draw(canvas); 476 canvas->restoreToCount(saveCount); 477 canvas->flush(); 478} 479 480/////////////////////////////////////////////////////////////////////////////////////////////// 481 482static void bitmapCopySubset(const SkBitmap& src, SkBitmap* dst, int xDst, 483 int yDst) { 484 for (int y = 0; y <src.height() && y + yDst < dst->height() ; y++) { 485 for (int x = 0; x < src.width() && x + xDst < dst->width() ; x++) { 486 *dst->getAddr32(xDst + x, yDst + y) = *src.getAddr32(x, y); 487 } 488 } 489} 490 491bool TiledPictureRenderer::nextTile(int &i, int &j) { 492 if (++fCurrentTileOffset < fTileRects.count()) { 493 i = fCurrentTileOffset % fTilesX; 494 j = fCurrentTileOffset / fTilesX; 495 return true; 496 } 497 return false; 498} 499 500void TiledPictureRenderer::drawCurrentTile() { 501 SkASSERT(fCurrentTileOffset >= 0 && fCurrentTileOffset < fTileRects.count()); 502 DrawTileToCanvas(fCanvas, fTileRects[fCurrentTileOffset], fPicture); 503} 504 505bool TiledPictureRenderer::render(const SkString* path, SkBitmap** out) { 506 SkASSERT(fPicture != NULL); 507 if (NULL == fPicture) { 508 return false; 509 } 510 511 SkBitmap bitmap; 512 if (out){ 513 *out = SkNEW(SkBitmap); 514 setup_bitmap(*out, fPicture->width(), fPicture->height()); 515 setup_bitmap(&bitmap, fTileWidth, fTileHeight); 516 } 517 bool success = true; 518 for (int i = 0; i < fTileRects.count(); ++i) { 519 DrawTileToCanvas(fCanvas, fTileRects[i], fPicture); 520 if (NULL != path) { 521 success &= writeAppendNumber(fCanvas, path, i); 522 } 523 if (NULL != out) { 524 if (fCanvas->readPixels(&bitmap, 0, 0)) { 525 bitmapCopySubset(bitmap, *out, fTileRects[i].left(), 526 fTileRects[i].top()); 527 } else { 528 success = false; 529 } 530 } 531 } 532 return success; 533} 534 535SkCanvas* TiledPictureRenderer::setupCanvas(int width, int height) { 536 SkCanvas* canvas = this->INHERITED::setupCanvas(width, height); 537 SkASSERT(fPicture != NULL); 538 // Clip the tile to an area that is completely inside both the SkPicture and the viewport. This 539 // is mostly important for tiles on the right and bottom edges as they may go over this area and 540 // the picture may have some commands that draw outside of this area and so should not actually 541 // be written. 542 // Uses a clipRegion so that it will be unaffected by the scale factor, which may have been set 543 // by INHERITED::setupCanvas. 544 SkRegion clipRegion; 545 clipRegion.setRect(0, 0, this->getViewWidth(), this->getViewHeight()); 546 canvas->clipRegion(clipRegion); 547 return canvas; 548} 549 550SkString TiledPictureRenderer::getConfigNameInternal() { 551 SkString name; 552 if (fTileMinPowerOf2Width > 0) { 553 name.append("pow2tile_"); 554 name.appendf("%i", fTileMinPowerOf2Width); 555 } else { 556 name.append("tile_"); 557 if (fTileWidthPercentage > 0) { 558 name.appendf("%.f%%", fTileWidthPercentage); 559 } else { 560 name.appendf("%i", fTileWidth); 561 } 562 } 563 name.append("x"); 564 if (fTileHeightPercentage > 0) { 565 name.appendf("%.f%%", fTileHeightPercentage); 566 } else { 567 name.appendf("%i", fTileHeight); 568 } 569 return name; 570} 571 572/////////////////////////////////////////////////////////////////////////////////////////////// 573 574// Holds all of the information needed to draw a set of tiles. 575class CloneData : public SkRunnable { 576 577public: 578 CloneData(SkPicture* clone, SkCanvas* canvas, SkTDArray<SkRect>& rects, int start, int end, 579 SkRunnable* done) 580 : fClone(clone) 581 , fCanvas(canvas) 582 , fPath(NULL) 583 , fRects(rects) 584 , fStart(start) 585 , fEnd(end) 586 , fSuccess(NULL) 587 , fDone(done) { 588 SkASSERT(fDone != NULL); 589 } 590 591 virtual void run() SK_OVERRIDE { 592 SkGraphics::SetTLSFontCacheLimit(1024 * 1024); 593 594 SkBitmap bitmap; 595 if (fBitmap != NULL) { 596 // All tiles are the same size. 597 setup_bitmap(&bitmap, fRects[0].width(), fRects[0].height()); 598 } 599 600 for (int i = fStart; i < fEnd; i++) { 601 DrawTileToCanvas(fCanvas, fRects[i], fClone); 602 if (fPath != NULL && !writeAppendNumber(fCanvas, fPath, i) 603 && fSuccess != NULL) { 604 *fSuccess = false; 605 // If one tile fails to write to a file, do not continue drawing the rest. 606 break; 607 } 608 if (fBitmap != NULL) { 609 if (fCanvas->readPixels(&bitmap, 0, 0)) { 610 SkAutoLockPixels alp(*fBitmap); 611 bitmapCopySubset(bitmap, fBitmap, fRects[i].left(), 612 fRects[i].top()); 613 } else { 614 *fSuccess = false; 615 // If one tile fails to read pixels, do not continue drawing the rest. 616 break; 617 } 618 } 619 } 620 fDone->run(); 621 } 622 623 void setPathAndSuccess(const SkString* path, bool* success) { 624 fPath = path; 625 fSuccess = success; 626 } 627 628 void setBitmap(SkBitmap* bitmap) { 629 fBitmap = bitmap; 630 } 631 632private: 633 // All pointers unowned. 634 SkPicture* fClone; // Picture to draw from. Each CloneData has a unique one which 635 // is threadsafe. 636 SkCanvas* fCanvas; // Canvas to draw to. Reused for each tile. 637 const SkString* fPath; // If non-null, path to write the result to as a PNG. 638 SkTDArray<SkRect>& fRects; // All tiles of the picture. 639 const int fStart; // Range of tiles drawn by this thread. 640 const int fEnd; 641 bool* fSuccess; // Only meaningful if path is non-null. Shared by all threads, 642 // and only set to false upon failure to write to a PNG. 643 SkRunnable* fDone; 644 SkBitmap* fBitmap; 645}; 646 647MultiCorePictureRenderer::MultiCorePictureRenderer(int threadCount) 648: fNumThreads(threadCount) 649, fThreadPool(threadCount) 650, fCountdown(threadCount) { 651 // Only need to create fNumThreads - 1 clones, since one thread will use the base 652 // picture. 653 fPictureClones = SkNEW_ARRAY(SkPicture, fNumThreads - 1); 654 fCloneData = SkNEW_ARRAY(CloneData*, fNumThreads); 655} 656 657void MultiCorePictureRenderer::init(SkPicture *pict) { 658 // Set fPicture and the tiles. 659 this->INHERITED::init(pict); 660 for (int i = 0; i < fNumThreads; ++i) { 661 *fCanvasPool.append() = this->setupCanvas(this->getTileWidth(), this->getTileHeight()); 662 } 663 // Only need to create fNumThreads - 1 clones, since one thread will use the base picture. 664 fPicture->clone(fPictureClones, fNumThreads - 1); 665 // Populate each thread with the appropriate data. 666 // Group the tiles into nearly equal size chunks, rounding up so we're sure to cover them all. 667 const int chunkSize = (fTileRects.count() + fNumThreads - 1) / fNumThreads; 668 669 for (int i = 0; i < fNumThreads; i++) { 670 SkPicture* pic; 671 if (i == fNumThreads-1) { 672 // The last set will use the original SkPicture. 673 pic = fPicture; 674 } else { 675 pic = &fPictureClones[i]; 676 } 677 const int start = i * chunkSize; 678 const int end = SkMin32(start + chunkSize, fTileRects.count()); 679 fCloneData[i] = SkNEW_ARGS(CloneData, 680 (pic, fCanvasPool[i], fTileRects, start, end, &fCountdown)); 681 } 682} 683 684bool MultiCorePictureRenderer::render(const SkString *path, SkBitmap** out) { 685 bool success = true; 686 if (path != NULL) { 687 for (int i = 0; i < fNumThreads-1; i++) { 688 fCloneData[i]->setPathAndSuccess(path, &success); 689 } 690 } 691 692 if (NULL != out) { 693 *out = SkNEW(SkBitmap); 694 setup_bitmap(*out, fPicture->width(), fPicture->height()); 695 for (int i = 0; i < fNumThreads; i++) { 696 fCloneData[i]->setBitmap(*out); 697 } 698 } else { 699 for (int i = 0; i < fNumThreads; i++) { 700 fCloneData[i]->setBitmap(NULL); 701 } 702 } 703 704 fCountdown.reset(fNumThreads); 705 for (int i = 0; i < fNumThreads; i++) { 706 fThreadPool.add(fCloneData[i]); 707 } 708 fCountdown.wait(); 709 710 return success; 711} 712 713void MultiCorePictureRenderer::end() { 714 for (int i = 0; i < fNumThreads - 1; i++) { 715 SkDELETE(fCloneData[i]); 716 fCloneData[i] = NULL; 717 } 718 719 fCanvasPool.unrefAll(); 720 721 this->INHERITED::end(); 722} 723 724MultiCorePictureRenderer::~MultiCorePictureRenderer() { 725 // Each individual CloneData was deleted in end. 726 SkDELETE_ARRAY(fCloneData); 727 SkDELETE_ARRAY(fPictureClones); 728} 729 730SkString MultiCorePictureRenderer::getConfigNameInternal() { 731 SkString name = this->INHERITED::getConfigNameInternal(); 732 name.appendf("_multi_%i_threads", fNumThreads); 733 return name; 734} 735 736/////////////////////////////////////////////////////////////////////////////////////////////// 737 738void PlaybackCreationRenderer::setup() { 739 fReplayer.reset(this->createPicture()); 740 SkCanvas* recorder = fReplayer->beginRecording(this->getViewWidth(), this->getViewHeight(), 741 this->recordFlags()); 742 this->scaleToScaleFactor(recorder); 743 fPicture->draw(recorder); 744} 745 746bool PlaybackCreationRenderer::render(const SkString*, SkBitmap** out) { 747 fReplayer->endRecording(); 748 // Since this class does not actually render, return false. 749 return false; 750} 751 752SkString PlaybackCreationRenderer::getConfigNameInternal() { 753 return SkString("playback_creation"); 754} 755 756/////////////////////////////////////////////////////////////////////////////////////////////// 757// SkPicture variants for each BBoxHierarchy type 758 759class RTreePicture : public SkPicture { 760public: 761 virtual SkBBoxHierarchy* createBBoxHierarchy() const SK_OVERRIDE{ 762 static const int kRTreeMinChildren = 6; 763 static const int kRTreeMaxChildren = 11; 764 SkScalar aspectRatio = SkScalarDiv(SkIntToScalar(fWidth), 765 SkIntToScalar(fHeight)); 766 return SkRTree::Create(kRTreeMinChildren, kRTreeMaxChildren, 767 aspectRatio); 768 } 769}; 770 771SkPicture* PictureRenderer::createPicture() { 772 switch (fBBoxHierarchyType) { 773 case kNone_BBoxHierarchyType: 774 return SkNEW(SkPicture); 775 case kRTree_BBoxHierarchyType: 776 return SkNEW(RTreePicture); 777 case kTileGrid_BBoxHierarchyType: 778 return SkNEW_ARGS(SkTileGridPicture, (fGridWidth, fGridHeight, fPicture->width(), 779 fPicture->height())); 780 } 781 SkASSERT(0); // invalid bbhType 782 return NULL; 783} 784 785/////////////////////////////////////////////////////////////////////////////// 786 787class GatherRenderer : public PictureRenderer { 788public: 789 virtual bool render(const SkString* path, SkBitmap** out = NULL) 790 SK_OVERRIDE { 791 SkRect bounds = SkRect::MakeWH(SkIntToScalar(fPicture->width()), 792 SkIntToScalar(fPicture->height())); 793 SkData* data = SkPictureUtils::GatherPixelRefs(fPicture, bounds); 794 SkSafeUnref(data); 795 796 return NULL == path; // we don't have anything to write 797 } 798 799private: 800 virtual SkString getConfigNameInternal() SK_OVERRIDE { 801 return SkString("gather_pixelrefs"); 802 } 803}; 804 805PictureRenderer* CreateGatherPixelRefsRenderer() { 806 return SkNEW(GatherRenderer); 807} 808 809/////////////////////////////////////////////////////////////////////////////// 810 811class PictureCloneRenderer : public PictureRenderer { 812public: 813 virtual bool render(const SkString* path, SkBitmap** out = NULL) 814 SK_OVERRIDE { 815 for (int i = 0; i < 100; ++i) { 816 SkPicture* clone = fPicture->clone(); 817 SkSafeUnref(clone); 818 } 819 820 return NULL == path; // we don't have anything to write 821 } 822 823private: 824 virtual SkString getConfigNameInternal() SK_OVERRIDE { 825 return SkString("picture_clone"); 826 } 827}; 828 829PictureRenderer* CreatePictureCloneRenderer() { 830 return SkNEW(PictureCloneRenderer); 831} 832 833} // namespace sk_tools 834