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