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