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 "SkOSFile.h" 26 #include "SkPicture.h" 27 #include "SkPictureRecorder.h" 28 #include "SkPictureUtils.h" 29 #include "SkPixelRef.h" 30 #include "SkScalar.h" 31 #include "SkStream.h" 32 #include "SkString.h" 33 #include "SkTemplates.h" 34 #include "SkTDArray.h" 35 #include "SkThreadUtils.h" 36 #include "SkTypes.h" 37 38 static 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 44 namespace sk_tools { 45 46 enum { 47 kDefaultTileWidth = 256, 48 kDefaultTileHeight = 256 49 }; 50 51 void PictureRenderer::init(const SkPicture* pict, 52 const SkString* writePath, 53 const SkString* mismatchPath, 54 const SkString* inputFilename, 55 bool useChecksumBasedFilenames) { 56 this->CopyString(&fWritePath, writePath); 57 this->CopyString(&fMismatchPath, mismatchPath); 58 this->CopyString(&fInputFilename, inputFilename); 59 fUseChecksumBasedFilenames = useChecksumBasedFilenames; 60 61 SkASSERT(NULL == fPicture); 62 SkASSERT(NULL == fCanvas.get()); 63 if (fPicture || fCanvas.get()) { 64 return; 65 } 66 67 SkASSERT(pict != NULL); 68 if (NULL == pict) { 69 return; 70 } 71 72 fPicture.reset(pict)->ref(); 73 fCanvas.reset(this->setupCanvas()); 74 } 75 76 void PictureRenderer::CopyString(SkString* dest, const SkString* src) { 77 if (src) { 78 dest->set(*src); 79 } else { 80 dest->reset(); 81 } 82 } 83 84 class FlagsDrawFilter : public SkDrawFilter { 85 public: 86 FlagsDrawFilter(PictureRenderer::DrawFilterFlags* flags) : 87 fFlags(flags) {} 88 89 virtual bool filter(SkPaint* paint, Type t) { 90 paint->setFlags(paint->getFlags() & ~fFlags[t] & SkPaint::kAllFlags); 91 if (PictureRenderer::kMaskFilter_DrawFilterFlag & fFlags[t]) { 92 SkMaskFilter* maskFilter = paint->getMaskFilter(); 93 if (maskFilter) { 94 paint->setMaskFilter(NULL); 95 } 96 } 97 if (PictureRenderer::kHinting_DrawFilterFlag & fFlags[t]) { 98 paint->setHinting(SkPaint::kNo_Hinting); 99 } else if (PictureRenderer::kSlightHinting_DrawFilterFlag & fFlags[t]) { 100 paint->setHinting(SkPaint::kSlight_Hinting); 101 } 102 return true; 103 } 104 105 private: 106 PictureRenderer::DrawFilterFlags* fFlags; 107 }; 108 109 static void setUpFilter(SkCanvas* canvas, PictureRenderer::DrawFilterFlags* drawFilters) { 110 if (drawFilters && !canvas->getDrawFilter()) { 111 canvas->setDrawFilter(SkNEW_ARGS(FlagsDrawFilter, (drawFilters)))->unref(); 112 if (drawFilters[0] & PictureRenderer::kAAClip_DrawFilterFlag) { 113 canvas->setAllowSoftClip(false); 114 } 115 } 116 } 117 118 SkCanvas* PictureRenderer::setupCanvas() { 119 const int width = this->getViewWidth(); 120 const int height = this->getViewHeight(); 121 return this->setupCanvas(width, height); 122 } 123 124 SkCanvas* PictureRenderer::setupCanvas(int width, int height) { 125 SkCanvas* canvas; 126 switch(fDeviceType) { 127 case kBitmap_DeviceType: { 128 SkBitmap bitmap; 129 sk_tools::setup_bitmap(&bitmap, width, height); 130 canvas = SkNEW_ARGS(SkCanvas, (bitmap)); 131 } 132 break; 133 #if SK_SUPPORT_GPU 134 #if SK_ANGLE 135 case kAngle_DeviceType: 136 // fall through 137 #endif 138 #if SK_MESA 139 case kMesa_DeviceType: 140 // fall through 141 #endif 142 case kGPU_DeviceType: 143 case kNVPR_DeviceType: { 144 SkAutoTUnref<GrSurface> target; 145 if (fGrContext) { 146 // create a render target to back the device 147 GrTextureDesc desc; 148 desc.fConfig = kSkia8888_GrPixelConfig; 149 desc.fFlags = kRenderTarget_GrTextureFlagBit; 150 desc.fWidth = width; 151 desc.fHeight = height; 152 desc.fSampleCnt = fSampleCount; 153 target.reset(fGrContext->createUncachedTexture(desc, NULL, 0)); 154 } 155 if (NULL == target.get()) { 156 SkASSERT(0); 157 return NULL; 158 } 159 160 SkAutoTUnref<SkGpuDevice> device(SkGpuDevice::Create(target, 161 SkSurfaceProps(SkSurfaceProps::kLegacyFontHost_InitType))); 162 canvas = SkNEW_ARGS(SkCanvas, (device.get())); 163 break; 164 } 165 #endif 166 default: 167 SkASSERT(0); 168 return NULL; 169 } 170 setUpFilter(canvas, fDrawFilters); 171 this->scaleToScaleFactor(canvas); 172 173 // Pictures often lie about their extent (i.e., claim to be 100x100 but 174 // only ever draw to 90x100). Clear here so the undrawn portion will have 175 // a consistent color 176 canvas->clear(SK_ColorTRANSPARENT); 177 return canvas; 178 } 179 180 void PictureRenderer::scaleToScaleFactor(SkCanvas* canvas) { 181 SkASSERT(canvas != NULL); 182 if (fScaleFactor != SK_Scalar1) { 183 canvas->scale(fScaleFactor, fScaleFactor); 184 } 185 } 186 187 void PictureRenderer::end() { 188 this->resetState(true); 189 fPicture.reset(NULL); 190 fCanvas.reset(NULL); 191 } 192 193 int PictureRenderer::getViewWidth() { 194 SkASSERT(fPicture != NULL); 195 int width = SkScalarCeilToInt(fPicture->cullRect().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->cullRect().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(fPicture); 217 if (kNone_BBoxHierarchyType != fBBoxHierarchyType && fPicture) { 218 SkAutoTDelete<SkBBHFactory> factory(this->getFactory()); 219 SkPictureRecorder recorder; 220 SkCanvas* canvas = recorder.beginRecording(fPicture->cullRect().width(), 221 fPicture->cullRect().height(), 222 factory.get(), 223 this->recordFlags()); 224 fPicture->playback(canvas); 225 fPicture.reset(recorder.endRecording()); 226 } 227 } 228 229 void PictureRenderer::resetState(bool callFinish) { 230 #if SK_SUPPORT_GPU 231 SkGLContextHelper* glContext = this->getGLContext(); 232 if (NULL == glContext) { 233 SkASSERT(kBitmap_DeviceType == fDeviceType); 234 return; 235 } 236 237 fGrContext->flush(); 238 glContext->swapBuffers(); 239 if (callFinish) { 240 SK_GL(*glContext, Finish()); 241 } 242 #endif 243 } 244 245 void PictureRenderer::purgeTextures() { 246 SkDiscardableMemoryPool* pool = SkGetGlobalDiscardableMemoryPool(); 247 248 pool->dumpPool(); 249 250 #if SK_SUPPORT_GPU 251 SkGLContextHelper* glContext = this->getGLContext(); 252 if (NULL == glContext) { 253 SkASSERT(kBitmap_DeviceType == fDeviceType); 254 return; 255 } 256 257 // resetState should've already done this 258 fGrContext->flush(); 259 260 fGrContext->purgeAllUnlockedResources(); 261 #endif 262 } 263 264 /** 265 * Write the canvas to an image file and/or JSON summary. 266 * 267 * @param canvas Must be non-null. Canvas to be written to a file. 268 * @param writePath If nonempty, write the binary image to a file within this directory. 269 * @param mismatchPath If nonempty, write the binary image to a file within this directory, 270 * but only if the image does not match expectations. 271 * @param inputFilename If we are writing out a binary image, use this to build its filename. 272 * @param jsonSummaryPtr If not null, add image results (checksum) to this summary. 273 * @param useChecksumBasedFilenames If true, use checksum-based filenames when writing to disk. 274 * @param tileNumberPtr If not null, which tile number this image contains. 275 * 276 * @return bool True if the operation completed successfully. 277 */ 278 static bool write(SkCanvas* canvas, const SkString& writePath, const SkString& mismatchPath, 279 const SkString& inputFilename, ImageResultsAndExpectations *jsonSummaryPtr, 280 bool useChecksumBasedFilenames, const int* tileNumberPtr=NULL) { 281 SkASSERT(canvas != NULL); 282 if (NULL == canvas) { 283 return false; 284 } 285 286 SkBitmap bitmap; 287 SkISize size = canvas->getDeviceSize(); 288 setup_bitmap(&bitmap, size.width(), size.height()); 289 290 canvas->readPixels(&bitmap, 0, 0); 291 force_all_opaque(bitmap); 292 BitmapAndDigest bitmapAndDigest(bitmap); 293 294 SkString escapedInputFilename(inputFilename); 295 replace_char(&escapedInputFilename, '.', '_'); 296 297 // TODO(epoger): what about including the config type within outputFilename? That way, 298 // we could combine results of different config types without conflicting filenames. 299 SkString outputFilename; 300 const char *outputSubdirPtr = NULL; 301 if (useChecksumBasedFilenames) { 302 ImageDigest *imageDigestPtr = bitmapAndDigest.getImageDigestPtr(); 303 outputSubdirPtr = escapedInputFilename.c_str(); 304 outputFilename.set(imageDigestPtr->getHashType()); 305 outputFilename.append("_"); 306 outputFilename.appendU64(imageDigestPtr->getHashValue()); 307 } else { 308 outputFilename.set(escapedInputFilename); 309 if (tileNumberPtr) { 310 outputFilename.append("-tile"); 311 outputFilename.appendS32(*tileNumberPtr); 312 } 313 } 314 outputFilename.append(".png"); 315 316 if (jsonSummaryPtr) { 317 ImageDigest *imageDigestPtr = bitmapAndDigest.getImageDigestPtr(); 318 SkString outputRelativePath; 319 if (outputSubdirPtr) { 320 outputRelativePath.set(outputSubdirPtr); 321 outputRelativePath.append("/"); // always use "/", even on Windows 322 outputRelativePath.append(outputFilename); 323 } else { 324 outputRelativePath.set(outputFilename); 325 } 326 327 jsonSummaryPtr->add(inputFilename.c_str(), outputRelativePath.c_str(), 328 *imageDigestPtr, tileNumberPtr); 329 if (!mismatchPath.isEmpty() && 330 !jsonSummaryPtr->getExpectation(inputFilename.c_str(), 331 tileNumberPtr).matches(*imageDigestPtr)) { 332 if (!write_bitmap_to_disk(bitmap, mismatchPath, outputSubdirPtr, outputFilename)) { 333 return false; 334 } 335 } 336 } 337 338 if (writePath.isEmpty()) { 339 return true; 340 } else { 341 return write_bitmap_to_disk(bitmap, writePath, outputSubdirPtr, outputFilename); 342 } 343 } 344 345 /////////////////////////////////////////////////////////////////////////////////////////////// 346 347 SkCanvas* RecordPictureRenderer::setupCanvas(int width, int height) { 348 // defer the canvas setup until the render step 349 return NULL; 350 } 351 352 // the size_t* parameter is deprecated, so we ignore it 353 static SkData* encode_bitmap_to_data(size_t*, const SkBitmap& bm) { 354 return SkImageEncoder::EncodeData(bm, SkImageEncoder::kPNG_Type, 100); 355 } 356 357 bool RecordPictureRenderer::render(SkBitmap** out) { 358 SkAutoTDelete<SkBBHFactory> factory(this->getFactory()); 359 SkPictureRecorder recorder; 360 SkCanvas* canvas = recorder.beginRecording(SkIntToScalar(this->getViewWidth()), 361 SkIntToScalar(this->getViewHeight()), 362 factory.get(), 363 this->recordFlags()); 364 this->scaleToScaleFactor(canvas); 365 fPicture->playback(canvas); 366 SkAutoTUnref<SkPicture> picture(recorder.endRecording()); 367 if (!fWritePath.isEmpty()) { 368 // Record the new picture as a new SKP with PNG encoded bitmaps. 369 SkString skpPath = SkOSPath::Join(fWritePath.c_str(), fInputFilename.c_str()); 370 SkFILEWStream stream(skpPath.c_str()); 371 picture->serialize(&stream, &encode_bitmap_to_data); 372 return true; 373 } 374 return false; 375 } 376 377 SkString RecordPictureRenderer::getConfigNameInternal() { 378 return SkString("record"); 379 } 380 381 /////////////////////////////////////////////////////////////////////////////////////////////// 382 383 bool PipePictureRenderer::render(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 (out) { 397 *out = SkNEW(SkBitmap); 398 setup_bitmap(*out, SkScalarCeilToInt(fPicture->cullRect().width()), 399 SkScalarCeilToInt(fPicture->cullRect().height())); 400 fCanvas->readPixels(*out, 0, 0); 401 } 402 if (fEnableWrites) { 403 return write(fCanvas, fWritePath, fMismatchPath, fInputFilename, fJsonSummaryPtr, 404 fUseChecksumBasedFilenames); 405 } else { 406 return true; 407 } 408 } 409 410 SkString PipePictureRenderer::getConfigNameInternal() { 411 return SkString("pipe"); 412 } 413 414 /////////////////////////////////////////////////////////////////////////////////////////////// 415 416 void SimplePictureRenderer::init(const SkPicture* picture, const SkString* writePath, 417 const SkString* mismatchPath, const SkString* inputFilename, 418 bool useChecksumBasedFilenames) { 419 INHERITED::init(picture, writePath, mismatchPath, inputFilename, useChecksumBasedFilenames); 420 this->buildBBoxHierarchy(); 421 } 422 423 bool SimplePictureRenderer::render(SkBitmap** out) { 424 SkASSERT(fCanvas.get() != NULL); 425 SkASSERT(fPicture); 426 if (NULL == fCanvas.get() || NULL == fPicture) { 427 return false; 428 } 429 430 fCanvas->drawPicture(fPicture); 431 fCanvas->flush(); 432 if (out) { 433 *out = SkNEW(SkBitmap); 434 setup_bitmap(*out, SkScalarCeilToInt(fPicture->cullRect().width()), 435 SkScalarCeilToInt(fPicture->cullRect().height())); 436 fCanvas->readPixels(*out, 0, 0); 437 } 438 if (fEnableWrites) { 439 return write(fCanvas, fWritePath, fMismatchPath, fInputFilename, fJsonSummaryPtr, 440 fUseChecksumBasedFilenames); 441 } else { 442 return true; 443 } 444 } 445 446 SkString SimplePictureRenderer::getConfigNameInternal() { 447 return SkString("simple"); 448 } 449 450 /////////////////////////////////////////////////////////////////////////////////////////////// 451 452 #if SK_SUPPORT_GPU 453 TiledPictureRenderer::TiledPictureRenderer(const GrContext::Options& opts) 454 : INHERITED(opts) 455 , fTileWidth(kDefaultTileWidth) 456 #else 457 TiledPictureRenderer::TiledPictureRenderer() 458 : fTileWidth(kDefaultTileWidth) 459 #endif 460 , fTileHeight(kDefaultTileHeight) 461 , fTileWidthPercentage(0.0) 462 , fTileHeightPercentage(0.0) 463 , fTileMinPowerOf2Width(0) 464 , fCurrentTileOffset(-1) 465 , fTilesX(0) 466 , fTilesY(0) { } 467 468 void TiledPictureRenderer::init(const SkPicture* pict, const SkString* writePath, 469 const SkString* mismatchPath, const SkString* inputFilename, 470 bool useChecksumBasedFilenames) { 471 SkASSERT(pict); 472 SkASSERT(0 == fTileRects.count()); 473 if (NULL == pict || fTileRects.count() != 0) { 474 return; 475 } 476 477 // Do not call INHERITED::init(), which would create a (potentially large) canvas which is not 478 // used by bench_pictures. 479 fPicture.reset(pict)->ref(); 480 this->CopyString(&fWritePath, writePath); 481 this->CopyString(&fMismatchPath, mismatchPath); 482 this->CopyString(&fInputFilename, inputFilename); 483 fUseChecksumBasedFilenames = useChecksumBasedFilenames; 484 this->buildBBoxHierarchy(); 485 486 if (fTileWidthPercentage > 0) { 487 fTileWidth = SkScalarCeilToInt(float(fTileWidthPercentage * fPicture->cullRect().width() / 100)); 488 } 489 if (fTileHeightPercentage > 0) { 490 fTileHeight = SkScalarCeilToInt(float(fTileHeightPercentage * fPicture->cullRect().height() / 100)); 491 } 492 493 if (fTileMinPowerOf2Width > 0) { 494 this->setupPowerOf2Tiles(); 495 } else { 496 this->setupTiles(); 497 } 498 fCanvas.reset(this->setupCanvas(fTileWidth, fTileHeight)); 499 // Initialize to -1 so that the first call to nextTile will set this up to draw tile 0 on the 500 // first call to drawCurrentTile. 501 fCurrentTileOffset = -1; 502 } 503 504 void TiledPictureRenderer::end() { 505 fTileRects.reset(); 506 this->INHERITED::end(); 507 } 508 509 void TiledPictureRenderer::setupTiles() { 510 // Only use enough tiles to cover the viewport 511 const int width = this->getViewWidth(); 512 const int height = this->getViewHeight(); 513 514 fTilesX = fTilesY = 0; 515 for (int tile_y_start = 0; tile_y_start < height; tile_y_start += fTileHeight) { 516 fTilesY++; 517 for (int tile_x_start = 0; tile_x_start < width; tile_x_start += fTileWidth) { 518 if (0 == tile_y_start) { 519 // Only count tiles in the X direction on the first pass. 520 fTilesX++; 521 } 522 *fTileRects.append() = SkRect::MakeXYWH(SkIntToScalar(tile_x_start), 523 SkIntToScalar(tile_y_start), 524 SkIntToScalar(fTileWidth), 525 SkIntToScalar(fTileHeight)); 526 } 527 } 528 } 529 530 bool TiledPictureRenderer::tileDimensions(int &x, int &y) { 531 if (fTileRects.count() == 0 || NULL == fPicture) { 532 return false; 533 } 534 x = fTilesX; 535 y = fTilesY; 536 return true; 537 } 538 539 // The goal of the powers of two tiles is to minimize the amount of wasted tile 540 // space in the width-wise direction and then minimize the number of tiles. The 541 // constraints are that every tile must have a pixel width that is a power of 542 // two and also be of some minimal width (that is also a power of two). 543 // 544 // This is solved by first taking our picture size and rounding it up to the 545 // multiple of the minimal width. The binary representation of this rounded 546 // value gives us the tiles we need: a bit of value one means we need a tile of 547 // that size. 548 void TiledPictureRenderer::setupPowerOf2Tiles() { 549 // Only use enough tiles to cover the viewport 550 const int width = this->getViewWidth(); 551 const int height = this->getViewHeight(); 552 553 int rounded_value = width; 554 if (width % fTileMinPowerOf2Width != 0) { 555 rounded_value = width - (width % fTileMinPowerOf2Width) + fTileMinPowerOf2Width; 556 } 557 558 int num_bits = SkScalarCeilToInt(scalar_log2(SkIntToScalar(width))); 559 int largest_possible_tile_size = 1 << num_bits; 560 561 fTilesX = fTilesY = 0; 562 // The tile height is constant for a particular picture. 563 for (int tile_y_start = 0; tile_y_start < height; tile_y_start += fTileHeight) { 564 fTilesY++; 565 int tile_x_start = 0; 566 int current_width = largest_possible_tile_size; 567 // Set fTileWidth to be the width of the widest tile, so that each canvas is large enough 568 // to draw each tile. 569 fTileWidth = current_width; 570 571 while (current_width >= fTileMinPowerOf2Width) { 572 // It is very important this is a bitwise AND. 573 if (current_width & rounded_value) { 574 if (0 == tile_y_start) { 575 // Only count tiles in the X direction on the first pass. 576 fTilesX++; 577 } 578 *fTileRects.append() = SkRect::MakeXYWH(SkIntToScalar(tile_x_start), 579 SkIntToScalar(tile_y_start), 580 SkIntToScalar(current_width), 581 SkIntToScalar(fTileHeight)); 582 tile_x_start += current_width; 583 } 584 585 current_width >>= 1; 586 } 587 } 588 } 589 590 /** 591 * Draw the specified picture to the canvas translated to rectangle provided, so that this mini 592 * canvas represents the rectangle's portion of the overall picture. 593 * Saves and restores so that the initial clip and matrix return to their state before this function 594 * is called. 595 */ 596 static void draw_tile_to_canvas(SkCanvas* canvas, 597 const SkRect& tileRect, 598 const SkPicture* picture) { 599 int saveCount = canvas->save(); 600 // Translate so that we draw the correct portion of the picture. 601 // Perform a postTranslate so that the scaleFactor does not interfere with the positioning. 602 SkMatrix mat(canvas->getTotalMatrix()); 603 mat.postTranslate(-tileRect.fLeft, -tileRect.fTop); 604 canvas->setMatrix(mat); 605 canvas->drawPicture(picture); 606 canvas->restoreToCount(saveCount); 607 canvas->flush(); 608 } 609 610 /////////////////////////////////////////////////////////////////////////////////////////////// 611 612 /** 613 * Copies the entirety of the src bitmap (typically a tile) into a portion of the dst bitmap. 614 * If the src bitmap is too large to fit within the dst bitmap after the x and y 615 * offsets have been applied, any excess will be ignored (so only the top-left portion of the 616 * src bitmap will be copied). 617 * 618 * @param src source bitmap 619 * @param dst destination bitmap 620 * @param xOffset x-offset within destination bitmap 621 * @param yOffset y-offset within destination bitmap 622 */ 623 static void bitmapCopyAtOffset(const SkBitmap& src, SkBitmap* dst, 624 int xOffset, int yOffset) { 625 for (int y = 0; y <src.height() && y + yOffset < dst->height() ; y++) { 626 for (int x = 0; x < src.width() && x + xOffset < dst->width() ; x++) { 627 *dst->getAddr32(xOffset + x, yOffset + y) = *src.getAddr32(x, y); 628 } 629 } 630 } 631 632 bool TiledPictureRenderer::nextTile(int &i, int &j) { 633 if (++fCurrentTileOffset < fTileRects.count()) { 634 i = fCurrentTileOffset % fTilesX; 635 j = fCurrentTileOffset / fTilesX; 636 return true; 637 } 638 return false; 639 } 640 641 void TiledPictureRenderer::drawCurrentTile() { 642 SkASSERT(fCurrentTileOffset >= 0 && fCurrentTileOffset < fTileRects.count()); 643 draw_tile_to_canvas(fCanvas, fTileRects[fCurrentTileOffset], fPicture); 644 } 645 646 bool TiledPictureRenderer::render(SkBitmap** out) { 647 SkASSERT(fPicture != NULL); 648 if (NULL == fPicture) { 649 return false; 650 } 651 652 SkBitmap bitmap; 653 if (out){ 654 *out = SkNEW(SkBitmap); 655 setup_bitmap(*out, SkScalarCeilToInt(fPicture->cullRect().width()), 656 SkScalarCeilToInt(fPicture->cullRect().height())); 657 setup_bitmap(&bitmap, fTileWidth, fTileHeight); 658 } 659 bool success = true; 660 for (int i = 0; i < fTileRects.count(); ++i) { 661 draw_tile_to_canvas(fCanvas, fTileRects[i], fPicture); 662 if (fEnableWrites) { 663 success &= write(fCanvas, fWritePath, fMismatchPath, fInputFilename, fJsonSummaryPtr, 664 fUseChecksumBasedFilenames, &i); 665 } 666 if (out) { 667 if (fCanvas->readPixels(&bitmap, 0, 0)) { 668 // Add this tile to the entire bitmap. 669 bitmapCopyAtOffset(bitmap, *out, SkScalarFloorToInt(fTileRects[i].left()), 670 SkScalarFloorToInt(fTileRects[i].top())); 671 } else { 672 success = false; 673 } 674 } 675 } 676 return success; 677 } 678 679 SkCanvas* TiledPictureRenderer::setupCanvas(int width, int height) { 680 SkCanvas* canvas = this->INHERITED::setupCanvas(width, height); 681 SkASSERT(fPicture); 682 // Clip the tile to an area that is completely inside both the SkPicture and the viewport. This 683 // is mostly important for tiles on the right and bottom edges as they may go over this area and 684 // the picture may have some commands that draw outside of this area and so should not actually 685 // be written. 686 // Uses a clipRegion so that it will be unaffected by the scale factor, which may have been set 687 // by INHERITED::setupCanvas. 688 SkRegion clipRegion; 689 clipRegion.setRect(0, 0, this->getViewWidth(), this->getViewHeight()); 690 canvas->clipRegion(clipRegion); 691 return canvas; 692 } 693 694 SkString TiledPictureRenderer::getConfigNameInternal() { 695 SkString name; 696 if (fTileMinPowerOf2Width > 0) { 697 name.append("pow2tile_"); 698 name.appendf("%i", fTileMinPowerOf2Width); 699 } else { 700 name.append("tile_"); 701 if (fTileWidthPercentage > 0) { 702 name.appendf("%.f%%", fTileWidthPercentage); 703 } else { 704 name.appendf("%i", fTileWidth); 705 } 706 } 707 name.append("x"); 708 if (fTileHeightPercentage > 0) { 709 name.appendf("%.f%%", fTileHeightPercentage); 710 } else { 711 name.appendf("%i", fTileHeight); 712 } 713 return name; 714 } 715 716 /////////////////////////////////////////////////////////////////////////////////////////////// 717 718 void PlaybackCreationRenderer::setup() { 719 SkAutoTDelete<SkBBHFactory> factory(this->getFactory()); 720 fRecorder.reset(SkNEW(SkPictureRecorder)); 721 SkCanvas* canvas = fRecorder->beginRecording(SkIntToScalar(this->getViewWidth()), 722 SkIntToScalar(this->getViewHeight()), 723 factory.get(), 724 this->recordFlags()); 725 this->scaleToScaleFactor(canvas); 726 canvas->drawPicture(fPicture); 727 } 728 729 bool PlaybackCreationRenderer::render(SkBitmap** out) { 730 fPicture.reset(fRecorder->endRecording()); 731 // Since this class does not actually render, return false. 732 return false; 733 } 734 735 SkString PlaybackCreationRenderer::getConfigNameInternal() { 736 return SkString("playback_creation"); 737 } 738 739 /////////////////////////////////////////////////////////////////////////////////////////////// 740 // SkPicture variants for each BBoxHierarchy type 741 742 SkBBHFactory* PictureRenderer::getFactory() { 743 switch (fBBoxHierarchyType) { 744 case kNone_BBoxHierarchyType: 745 return NULL; 746 case kRTree_BBoxHierarchyType: 747 return SkNEW(SkRTreeFactory); 748 case kTileGrid_BBoxHierarchyType: 749 return SkNEW_ARGS(SkTileGridFactory, (fGridInfo)); 750 } 751 SkASSERT(0); // invalid bbhType 752 return NULL; 753 } 754 755 /////////////////////////////////////////////////////////////////////////////// 756 757 class GatherRenderer : public PictureRenderer { 758 public: 759 #if SK_SUPPORT_GPU 760 GatherRenderer(const GrContext::Options& opts) : INHERITED(opts) { } 761 #endif 762 763 virtual bool render(SkBitmap** out = NULL) SK_OVERRIDE { 764 SkRect bounds = SkRect::MakeWH(SkIntToScalar(fPicture->cullRect().width()), 765 SkIntToScalar(fPicture->cullRect().height())); 766 SkData* data = SkPictureUtils::GatherPixelRefs(fPicture, bounds); 767 SkSafeUnref(data); 768 769 return (fWritePath.isEmpty()); // we don't have anything to write 770 } 771 772 private: 773 virtual SkString getConfigNameInternal() SK_OVERRIDE { 774 return SkString("gather_pixelrefs"); 775 } 776 777 typedef PictureRenderer INHERITED; 778 }; 779 780 #if SK_SUPPORT_GPU 781 PictureRenderer* CreateGatherPixelRefsRenderer(const GrContext::Options& opts) { 782 return SkNEW_ARGS(GatherRenderer, (opts)); 783 } 784 #else 785 PictureRenderer* CreateGatherPixelRefsRenderer() { 786 return SkNEW(GatherRenderer); 787 } 788 #endif 789 790 } // namespace sk_tools 791