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