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