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