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