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