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