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