PictureRenderer.cpp revision 8991c67f0c1b9364bb12dfb0f32a53bd5a3357f3
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 38static inline SkScalar scalar_log2(SkScalar x) { 39 static const SkScalar log2_conversion_factor = SkScalarDiv(1, SkScalarLog(2)); 40 41 return SkScalarLog(x) * log2_conversion_factor; 42} 43 44namespace sk_tools { 45 46enum { 47 kDefaultTileWidth = 256, 48 kDefaultTileHeight = 256 49}; 50 51void 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 73void PictureRenderer::CopyString(SkString* dest, const SkString* src) { 74 if (NULL != src) { 75 dest->set(*src); 76 } else { 77 dest->reset(); 78 } 79} 80 81class FlagsDrawFilter : public SkDrawFilter { 82public: 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 102private: 103 PictureRenderer::DrawFilterFlags* fFlags; 104}; 105 106static 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 115SkCanvas* PictureRenderer::setupCanvas() { 116 const int width = this->getViewWidth(); 117 const int height = this->getViewHeight(); 118 return this->setupCanvas(width, height); 119} 120 121SkCanvas* 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 176void PictureRenderer::scaleToScaleFactor(SkCanvas* canvas) { 177 SkASSERT(canvas != NULL); 178 if (fScaleFactor != SK_Scalar1) { 179 canvas->scale(fScaleFactor, fScaleFactor); 180 } 181} 182 183void PictureRenderer::end() { 184 this->resetState(true); 185 fPicture.reset(NULL); 186 fCanvas.reset(NULL); 187} 188 189int 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 198int 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 */ 211void 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 224void 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 240void 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 259uint32_t PictureRenderer::recordFlags() { 260 return (kNone_BBoxHierarchyType == fBBoxHierarchyType) 261 ? 0 262 : SkPicture::kUsePathBoundsForClip_RecordingFlag; 263} 264 265/** 266 * Write the canvas to an image file and/or JSON summary. 267 * 268 * @param canvas Must be non-null. Canvas to be written to a file. 269 * @param writePath If nonempty, write the binary image to a file within this directory. 270 * @param mismatchPath If nonempty, write the binary image to a file within this directory, 271 * but only if the image does not match expectations. 272 * @param inputFilename If we are writing out a binary image, use this to build its filename. 273 * @param jsonSummaryPtr If not null, add image results (checksum) to this summary. 274 * @param useChecksumBasedFilenames If true, use checksum-based filenames when writing to disk. 275 * @param tileNumberPtr If not null, which tile number this image contains. 276 * 277 * @return bool True if the operation completed successfully. 278 */ 279static bool write(SkCanvas* canvas, const SkString& writePath, const SkString& mismatchPath, 280 const SkString& inputFilename, ImageResultsAndExpectations *jsonSummaryPtr, 281 bool useChecksumBasedFilenames, const int* tileNumberPtr=NULL) { 282 SkASSERT(canvas != NULL); 283 if (NULL == canvas) { 284 return false; 285 } 286 287 SkBitmap bitmap; 288 SkISize size = canvas->getDeviceSize(); 289 setup_bitmap(&bitmap, size.width(), size.height()); 290 291 canvas->readPixels(&bitmap, 0, 0); 292 force_all_opaque(bitmap); 293 BitmapAndDigest bitmapAndDigest(bitmap); 294 295 SkString escapedInputFilename(inputFilename); 296 replace_char(&escapedInputFilename, '.', '_'); 297 298 // TODO(epoger): what about including the config type within outputFilename? That way, 299 // we could combine results of different config types without conflicting filenames. 300 SkString outputFilename; 301 const char *outputSubdirPtr = NULL; 302 if (useChecksumBasedFilenames) { 303 const ImageDigest *imageDigestPtr = bitmapAndDigest.getImageDigestPtr(); 304 outputSubdirPtr = escapedInputFilename.c_str(); 305 outputFilename.set(imageDigestPtr->getHashType()); 306 outputFilename.append("_"); 307 outputFilename.appendU64(imageDigestPtr->getHashValue()); 308 } else { 309 outputFilename.set(escapedInputFilename); 310 if (NULL != tileNumberPtr) { 311 outputFilename.append("-tile"); 312 outputFilename.appendS32(*tileNumberPtr); 313 } 314 } 315 outputFilename.append(".png"); 316 317 if (NULL != jsonSummaryPtr) { 318 const ImageDigest *imageDigestPtr = bitmapAndDigest.getImageDigestPtr(); 319 SkString outputRelativePath; 320 if (outputSubdirPtr) { 321 outputRelativePath.set(outputSubdirPtr); 322 outputRelativePath.append("/"); // always use "/", even on Windows 323 outputRelativePath.append(outputFilename); 324 } else { 325 outputRelativePath.set(outputFilename); 326 } 327 328 jsonSummaryPtr->add(inputFilename.c_str(), outputRelativePath.c_str(), 329 *imageDigestPtr, tileNumberPtr); 330 if (!mismatchPath.isEmpty() && 331 !jsonSummaryPtr->matchesExpectation(inputFilename.c_str(), *imageDigestPtr, 332 tileNumberPtr)) { 333 if (!write_bitmap_to_disk(bitmap, mismatchPath, outputSubdirPtr, outputFilename)) { 334 return false; 335 } 336 } 337 } 338 339 if (writePath.isEmpty()) { 340 return true; 341 } else { 342 return write_bitmap_to_disk(bitmap, writePath, outputSubdirPtr, outputFilename); 343 } 344} 345 346/////////////////////////////////////////////////////////////////////////////////////////////// 347 348SkCanvas* RecordPictureRenderer::setupCanvas(int width, int height) { 349 // defer the canvas setup until the render step 350 return NULL; 351} 352 353// the size_t* parameter is deprecated, so we ignore it 354static SkData* encode_bitmap_to_data(size_t*, const SkBitmap& bm) { 355 return SkImageEncoder::EncodeData(bm, SkImageEncoder::kPNG_Type, 100); 356} 357 358bool RecordPictureRenderer::render(SkBitmap** out) { 359 SkAutoTDelete<SkBBHFactory> factory(this->getFactory()); 360 SkPictureRecorder recorder; 361 SkCanvas* canvas = recorder.beginRecording(this->getViewWidth(), this->getViewHeight(), 362 factory.get(), 363 this->recordFlags()); 364 this->scaleToScaleFactor(canvas); 365 fPicture->draw(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::SkPathJoin(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 377SkString RecordPictureRenderer::getConfigNameInternal() { 378 return SkString("record"); 379} 380 381/////////////////////////////////////////////////////////////////////////////////////////////// 382 383bool 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 (NULL != out) { 397 *out = SkNEW(SkBitmap); 398 setup_bitmap(*out, fPicture->width(), fPicture->height()); 399 fCanvas->readPixels(*out, 0, 0); 400 } 401 if (fEnableWrites) { 402 return write(fCanvas, fWritePath, fMismatchPath, fInputFilename, fJsonSummaryPtr, 403 fUseChecksumBasedFilenames); 404 } else { 405 return true; 406 } 407} 408 409SkString PipePictureRenderer::getConfigNameInternal() { 410 return SkString("pipe"); 411} 412 413/////////////////////////////////////////////////////////////////////////////////////////////// 414 415void SimplePictureRenderer::init(SkPicture* picture, const SkString* writePath, 416 const SkString* mismatchPath, const SkString* inputFilename, 417 bool useChecksumBasedFilenames) { 418 INHERITED::init(picture, writePath, mismatchPath, inputFilename, useChecksumBasedFilenames); 419 this->buildBBoxHierarchy(); 420} 421 422bool SimplePictureRenderer::render(SkBitmap** out) { 423 SkASSERT(fCanvas.get() != NULL); 424 SkASSERT(NULL != fPicture); 425 if (NULL == fCanvas.get() || NULL == fPicture) { 426 return false; 427 } 428 429 fCanvas->drawPicture(*fPicture); 430 fCanvas->flush(); 431 if (NULL != out) { 432 *out = SkNEW(SkBitmap); 433 setup_bitmap(*out, fPicture->width(), fPicture->height()); 434 fCanvas->readPixels(*out, 0, 0); 435 } 436 if (fEnableWrites) { 437 return write(fCanvas, fWritePath, fMismatchPath, fInputFilename, fJsonSummaryPtr, 438 fUseChecksumBasedFilenames); 439 } else { 440 return true; 441 } 442} 443 444SkString SimplePictureRenderer::getConfigNameInternal() { 445 return SkString("simple"); 446} 447 448/////////////////////////////////////////////////////////////////////////////////////////////// 449 450TiledPictureRenderer::TiledPictureRenderer() 451 : fTileWidth(kDefaultTileWidth) 452 , fTileHeight(kDefaultTileHeight) 453 , fTileWidthPercentage(0.0) 454 , fTileHeightPercentage(0.0) 455 , fTileMinPowerOf2Width(0) 456 , fCurrentTileOffset(-1) 457 , fTilesX(0) 458 , fTilesY(0) { } 459 460void TiledPictureRenderer::init(SkPicture* pict, const SkString* writePath, 461 const SkString* mismatchPath, const SkString* inputFilename, 462 bool useChecksumBasedFilenames) { 463 SkASSERT(NULL != pict); 464 SkASSERT(0 == fTileRects.count()); 465 if (NULL == pict || fTileRects.count() != 0) { 466 return; 467 } 468 469 // Do not call INHERITED::init(), which would create a (potentially large) canvas which is not 470 // used by bench_pictures. 471 fPicture.reset(pict)->ref(); 472 this->CopyString(&fWritePath, writePath); 473 this->CopyString(&fMismatchPath, mismatchPath); 474 this->CopyString(&fInputFilename, inputFilename); 475 fUseChecksumBasedFilenames = useChecksumBasedFilenames; 476 this->buildBBoxHierarchy(); 477 478 if (fTileWidthPercentage > 0) { 479 fTileWidth = sk_float_ceil2int(float(fTileWidthPercentage * fPicture->width() / 100)); 480 } 481 if (fTileHeightPercentage > 0) { 482 fTileHeight = sk_float_ceil2int(float(fTileHeightPercentage * fPicture->height() / 100)); 483 } 484 485 if (fTileMinPowerOf2Width > 0) { 486 this->setupPowerOf2Tiles(); 487 } else { 488 this->setupTiles(); 489 } 490 fCanvas.reset(this->setupCanvas(fTileWidth, fTileHeight)); 491 // Initialize to -1 so that the first call to nextTile will set this up to draw tile 0 on the 492 // first call to drawCurrentTile. 493 fCurrentTileOffset = -1; 494} 495 496void TiledPictureRenderer::end() { 497 fTileRects.reset(); 498 this->INHERITED::end(); 499} 500 501void TiledPictureRenderer::setupTiles() { 502 // Only use enough tiles to cover the viewport 503 const int width = this->getViewWidth(); 504 const int height = this->getViewHeight(); 505 506 fTilesX = fTilesY = 0; 507 for (int tile_y_start = 0; tile_y_start < height; tile_y_start += fTileHeight) { 508 fTilesY++; 509 for (int tile_x_start = 0; tile_x_start < width; tile_x_start += fTileWidth) { 510 if (0 == tile_y_start) { 511 // Only count tiles in the X direction on the first pass. 512 fTilesX++; 513 } 514 *fTileRects.append() = SkRect::MakeXYWH(SkIntToScalar(tile_x_start), 515 SkIntToScalar(tile_y_start), 516 SkIntToScalar(fTileWidth), 517 SkIntToScalar(fTileHeight)); 518 } 519 } 520} 521 522bool TiledPictureRenderer::tileDimensions(int &x, int &y) { 523 if (fTileRects.count() == 0 || NULL == fPicture) { 524 return false; 525 } 526 x = fTilesX; 527 y = fTilesY; 528 return true; 529} 530 531// The goal of the powers of two tiles is to minimize the amount of wasted tile 532// space in the width-wise direction and then minimize the number of tiles. The 533// constraints are that every tile must have a pixel width that is a power of 534// two and also be of some minimal width (that is also a power of two). 535// 536// This is solved by first taking our picture size and rounding it up to the 537// multiple of the minimal width. The binary representation of this rounded 538// value gives us the tiles we need: a bit of value one means we need a tile of 539// that size. 540void TiledPictureRenderer::setupPowerOf2Tiles() { 541 // Only use enough tiles to cover the viewport 542 const int width = this->getViewWidth(); 543 const int height = this->getViewHeight(); 544 545 int rounded_value = width; 546 if (width % fTileMinPowerOf2Width != 0) { 547 rounded_value = width - (width % fTileMinPowerOf2Width) + fTileMinPowerOf2Width; 548 } 549 550 int num_bits = SkScalarCeilToInt(scalar_log2(SkIntToScalar(width))); 551 int largest_possible_tile_size = 1 << num_bits; 552 553 fTilesX = fTilesY = 0; 554 // The tile height is constant for a particular picture. 555 for (int tile_y_start = 0; tile_y_start < height; tile_y_start += fTileHeight) { 556 fTilesY++; 557 int tile_x_start = 0; 558 int current_width = largest_possible_tile_size; 559 // Set fTileWidth to be the width of the widest tile, so that each canvas is large enough 560 // to draw each tile. 561 fTileWidth = current_width; 562 563 while (current_width >= fTileMinPowerOf2Width) { 564 // It is very important this is a bitwise AND. 565 if (current_width & rounded_value) { 566 if (0 == tile_y_start) { 567 // Only count tiles in the X direction on the first pass. 568 fTilesX++; 569 } 570 *fTileRects.append() = SkRect::MakeXYWH(SkIntToScalar(tile_x_start), 571 SkIntToScalar(tile_y_start), 572 SkIntToScalar(current_width), 573 SkIntToScalar(fTileHeight)); 574 tile_x_start += current_width; 575 } 576 577 current_width >>= 1; 578 } 579 } 580} 581 582/** 583 * Draw the specified picture to the canvas translated to rectangle provided, so that this mini 584 * canvas represents the rectangle's portion of the overall picture. 585 * Saves and restores so that the initial clip and matrix return to their state before this function 586 * is called. 587 */ 588static void draw_tile_to_canvas(SkCanvas* canvas, const SkRect& tileRect, SkPicture* picture) { 589 int saveCount = canvas->save(); 590 // Translate so that we draw the correct portion of the picture. 591 // Perform a postTranslate so that the scaleFactor does not interfere with the positioning. 592 SkMatrix mat(canvas->getTotalMatrix()); 593 mat.postTranslate(-tileRect.fLeft, -tileRect.fTop); 594 canvas->setMatrix(mat); 595 canvas->drawPicture(*picture); 596 canvas->restoreToCount(saveCount); 597 canvas->flush(); 598} 599 600/////////////////////////////////////////////////////////////////////////////////////////////// 601 602/** 603 * Copies the entirety of the src bitmap (typically a tile) into a portion of the dst bitmap. 604 * If the src bitmap is too large to fit within the dst bitmap after the x and y 605 * offsets have been applied, any excess will be ignored (so only the top-left portion of the 606 * src bitmap will be copied). 607 * 608 * @param src source bitmap 609 * @param dst destination bitmap 610 * @param xOffset x-offset within destination bitmap 611 * @param yOffset y-offset within destination bitmap 612 */ 613static void bitmapCopyAtOffset(const SkBitmap& src, SkBitmap* dst, 614 int xOffset, int yOffset) { 615 for (int y = 0; y <src.height() && y + yOffset < dst->height() ; y++) { 616 for (int x = 0; x < src.width() && x + xOffset < dst->width() ; x++) { 617 *dst->getAddr32(xOffset + x, yOffset + y) = *src.getAddr32(x, y); 618 } 619 } 620} 621 622bool TiledPictureRenderer::nextTile(int &i, int &j) { 623 if (++fCurrentTileOffset < fTileRects.count()) { 624 i = fCurrentTileOffset % fTilesX; 625 j = fCurrentTileOffset / fTilesX; 626 return true; 627 } 628 return false; 629} 630 631void TiledPictureRenderer::drawCurrentTile() { 632 SkASSERT(fCurrentTileOffset >= 0 && fCurrentTileOffset < fTileRects.count()); 633 draw_tile_to_canvas(fCanvas, fTileRects[fCurrentTileOffset], fPicture); 634} 635 636bool TiledPictureRenderer::render(SkBitmap** out) { 637 SkASSERT(fPicture != NULL); 638 if (NULL == fPicture) { 639 return false; 640 } 641 642 SkBitmap bitmap; 643 if (out){ 644 *out = SkNEW(SkBitmap); 645 setup_bitmap(*out, fPicture->width(), fPicture->height()); 646 setup_bitmap(&bitmap, fTileWidth, fTileHeight); 647 } 648 bool success = true; 649 for (int i = 0; i < fTileRects.count(); ++i) { 650 draw_tile_to_canvas(fCanvas, fTileRects[i], fPicture); 651 if (fEnableWrites) { 652 success &= write(fCanvas, fWritePath, fMismatchPath, fInputFilename, fJsonSummaryPtr, 653 fUseChecksumBasedFilenames, &i); 654 } 655 if (NULL != out) { 656 if (fCanvas->readPixels(&bitmap, 0, 0)) { 657 // Add this tile to the entire bitmap. 658 bitmapCopyAtOffset(bitmap, *out, SkScalarFloorToInt(fTileRects[i].left()), 659 SkScalarFloorToInt(fTileRects[i].top())); 660 } else { 661 success = false; 662 } 663 } 664 } 665 return success; 666} 667 668SkCanvas* TiledPictureRenderer::setupCanvas(int width, int height) { 669 SkCanvas* canvas = this->INHERITED::setupCanvas(width, height); 670 SkASSERT(NULL != fPicture); 671 // Clip the tile to an area that is completely inside both the SkPicture and the viewport. This 672 // is mostly important for tiles on the right and bottom edges as they may go over this area and 673 // the picture may have some commands that draw outside of this area and so should not actually 674 // be written. 675 // Uses a clipRegion so that it will be unaffected by the scale factor, which may have been set 676 // by INHERITED::setupCanvas. 677 SkRegion clipRegion; 678 clipRegion.setRect(0, 0, this->getViewWidth(), this->getViewHeight()); 679 canvas->clipRegion(clipRegion); 680 return canvas; 681} 682 683SkString TiledPictureRenderer::getConfigNameInternal() { 684 SkString name; 685 if (fTileMinPowerOf2Width > 0) { 686 name.append("pow2tile_"); 687 name.appendf("%i", fTileMinPowerOf2Width); 688 } else { 689 name.append("tile_"); 690 if (fTileWidthPercentage > 0) { 691 name.appendf("%.f%%", fTileWidthPercentage); 692 } else { 693 name.appendf("%i", fTileWidth); 694 } 695 } 696 name.append("x"); 697 if (fTileHeightPercentage > 0) { 698 name.appendf("%.f%%", fTileHeightPercentage); 699 } else { 700 name.appendf("%i", fTileHeight); 701 } 702 return name; 703} 704 705/////////////////////////////////////////////////////////////////////////////////////////////// 706 707// Holds all of the information needed to draw a set of tiles. 708class CloneData : public SkRunnable { 709 710public: 711 CloneData(SkPicture* clone, SkCanvas* canvas, SkTDArray<SkRect>& rects, int start, int end, 712 SkRunnable* done, ImageResultsAndExpectations* jsonSummaryPtr, 713 bool useChecksumBasedFilenames, bool enableWrites) 714 : fClone(clone) 715 , fCanvas(canvas) 716 , fEnableWrites(enableWrites) 717 , fRects(rects) 718 , fStart(start) 719 , fEnd(end) 720 , fSuccess(NULL) 721 , fDone(done) 722 , fJsonSummaryPtr(jsonSummaryPtr) 723 , fUseChecksumBasedFilenames(useChecksumBasedFilenames) { 724 SkASSERT(fDone != NULL); 725 } 726 727 virtual void run() SK_OVERRIDE { 728 SkGraphics::SetTLSFontCacheLimit(1024 * 1024); 729 730 SkBitmap bitmap; 731 if (fBitmap != NULL) { 732 // All tiles are the same size. 733 setup_bitmap(&bitmap, SkScalarFloorToInt(fRects[0].width()), SkScalarFloorToInt(fRects[0].height())); 734 } 735 736 for (int i = fStart; i < fEnd; i++) { 737 draw_tile_to_canvas(fCanvas, fRects[i], fClone); 738 if (fEnableWrites) { 739 if (!write(fCanvas, fWritePath, fMismatchPath, fInputFilename, fJsonSummaryPtr, 740 fUseChecksumBasedFilenames, &i) 741 && fSuccess != NULL) { 742 *fSuccess = false; 743 // If one tile fails to write to a file, do not continue drawing the rest. 744 break; 745 } 746 if (fBitmap != NULL) { 747 if (fCanvas->readPixels(&bitmap, 0, 0)) { 748 SkAutoLockPixels alp(*fBitmap); 749 bitmapCopyAtOffset(bitmap, fBitmap, SkScalarFloorToInt(fRects[i].left()), 750 SkScalarFloorToInt(fRects[i].top())); 751 } else { 752 *fSuccess = false; 753 // If one tile fails to read pixels, do not continue drawing the rest. 754 break; 755 } 756 } 757 } 758 } 759 fDone->run(); 760 } 761 762 void setPathsAndSuccess(const SkString& writePath, const SkString& mismatchPath, 763 const SkString& inputFilename, bool* success) { 764 fWritePath.set(writePath); 765 fMismatchPath.set(mismatchPath); 766 fInputFilename.set(inputFilename); 767 fSuccess = success; 768 } 769 770 void setBitmap(SkBitmap* bitmap) { 771 fBitmap = bitmap; 772 } 773 774private: 775 // All pointers unowned. 776 SkPicture* fClone; // Picture to draw from. Each CloneData has a unique one which 777 // is threadsafe. 778 SkCanvas* fCanvas; // Canvas to draw to. Reused for each tile. 779 bool fEnableWrites; // TODO(epoger): Temporary hack; see declaration of 780 // fEnableWrites in PictureRenderer.h. 781 SkString fWritePath; // If not empty, write all results into this directory. 782 SkString fMismatchPath; // If not empty, write all unexpected results into this dir. 783 SkString fInputFilename; // Filename of input SkPicture file. 784 SkTDArray<SkRect>& fRects; // All tiles of the picture. 785 const int fStart; // Range of tiles drawn by this thread. 786 const int fEnd; 787 bool* fSuccess; // Only meaningful if path is non-null. Shared by all threads, 788 // and only set to false upon failure to write to a PNG. 789 SkRunnable* fDone; 790 SkBitmap* fBitmap; 791 ImageResultsAndExpectations* fJsonSummaryPtr; 792 bool fUseChecksumBasedFilenames; 793}; 794 795MultiCorePictureRenderer::MultiCorePictureRenderer(int threadCount) 796: fNumThreads(threadCount) 797, fThreadPool(threadCount) 798, fCountdown(threadCount) { 799 // Only need to create fNumThreads - 1 clones, since one thread will use the base 800 // picture. 801 fPictureClones = SkNEW_ARRAY(SkPicture, fNumThreads - 1); 802 fCloneData = SkNEW_ARRAY(CloneData*, fNumThreads); 803} 804 805void MultiCorePictureRenderer::init(SkPicture *pict, const SkString* writePath, 806 const SkString* mismatchPath, const SkString* inputFilename, 807 bool useChecksumBasedFilenames) { 808 // Set fPicture and the tiles. 809 this->INHERITED::init(pict, writePath, mismatchPath, inputFilename, useChecksumBasedFilenames); 810 for (int i = 0; i < fNumThreads; ++i) { 811 *fCanvasPool.append() = this->setupCanvas(this->getTileWidth(), this->getTileHeight()); 812 } 813 // Only need to create fNumThreads - 1 clones, since one thread will use the base picture. 814 fPicture->clone(fPictureClones, fNumThreads - 1); 815 // Populate each thread with the appropriate data. 816 // Group the tiles into nearly equal size chunks, rounding up so we're sure to cover them all. 817 const int chunkSize = (fTileRects.count() + fNumThreads - 1) / fNumThreads; 818 819 for (int i = 0; i < fNumThreads; i++) { 820 SkPicture* pic; 821 if (i == fNumThreads-1) { 822 // The last set will use the original SkPicture. 823 pic = fPicture; 824 } else { 825 pic = &fPictureClones[i]; 826 } 827 const int start = i * chunkSize; 828 const int end = SkMin32(start + chunkSize, fTileRects.count()); 829 fCloneData[i] = SkNEW_ARGS(CloneData, 830 (pic, fCanvasPool[i], fTileRects, start, end, &fCountdown, 831 fJsonSummaryPtr, useChecksumBasedFilenames, fEnableWrites)); 832 } 833} 834 835bool MultiCorePictureRenderer::render(SkBitmap** out) { 836 bool success = true; 837 if (!fWritePath.isEmpty() || !fMismatchPath.isEmpty()) { 838 for (int i = 0; i < fNumThreads-1; i++) { 839 fCloneData[i]->setPathsAndSuccess(fWritePath, fMismatchPath, fInputFilename, &success); 840 } 841 } 842 843 if (NULL != out) { 844 *out = SkNEW(SkBitmap); 845 setup_bitmap(*out, fPicture->width(), fPicture->height()); 846 for (int i = 0; i < fNumThreads; i++) { 847 fCloneData[i]->setBitmap(*out); 848 } 849 } else { 850 for (int i = 0; i < fNumThreads; i++) { 851 fCloneData[i]->setBitmap(NULL); 852 } 853 } 854 855 fCountdown.reset(fNumThreads); 856 for (int i = 0; i < fNumThreads; i++) { 857 fThreadPool.add(fCloneData[i]); 858 } 859 fCountdown.wait(); 860 861 return success; 862} 863 864void MultiCorePictureRenderer::end() { 865 for (int i = 0; i < fNumThreads - 1; i++) { 866 SkDELETE(fCloneData[i]); 867 fCloneData[i] = NULL; 868 } 869 870 fCanvasPool.unrefAll(); 871 872 this->INHERITED::end(); 873} 874 875MultiCorePictureRenderer::~MultiCorePictureRenderer() { 876 // Each individual CloneData was deleted in end. 877 SkDELETE_ARRAY(fCloneData); 878 SkDELETE_ARRAY(fPictureClones); 879} 880 881SkString MultiCorePictureRenderer::getConfigNameInternal() { 882 SkString name = this->INHERITED::getConfigNameInternal(); 883 name.appendf("_multi_%i_threads", fNumThreads); 884 return name; 885} 886 887/////////////////////////////////////////////////////////////////////////////////////////////// 888 889void PlaybackCreationRenderer::setup() { 890 SkAutoTDelete<SkBBHFactory> factory(this->getFactory()); 891 fRecorder.reset(SkNEW(SkPictureRecorder)); 892 SkCanvas* canvas = fRecorder->beginRecording(this->getViewWidth(), this->getViewHeight(), 893 factory.get(), 894 this->recordFlags()); 895 this->scaleToScaleFactor(canvas); 896 canvas->drawPicture(*fPicture); 897} 898 899bool PlaybackCreationRenderer::render(SkBitmap** out) { 900 fPicture.reset(fRecorder->endRecording()); 901 // Since this class does not actually render, return false. 902 return false; 903} 904 905SkString PlaybackCreationRenderer::getConfigNameInternal() { 906 return SkString("playback_creation"); 907} 908 909/////////////////////////////////////////////////////////////////////////////////////////////// 910// SkPicture variants for each BBoxHierarchy type 911 912SkBBHFactory* PictureRenderer::getFactory() { 913 switch (fBBoxHierarchyType) { 914 case kNone_BBoxHierarchyType: 915 return NULL; 916 case kQuadTree_BBoxHierarchyType: 917 return SkNEW(SkQuadTreeFactory); 918 case kRTree_BBoxHierarchyType: 919 return SkNEW(SkRTreeFactory); 920 case kTileGrid_BBoxHierarchyType: 921 return SkNEW_ARGS(SkTileGridFactory, (fGridInfo)); 922 } 923 SkASSERT(0); // invalid bbhType 924 return NULL; 925} 926 927/////////////////////////////////////////////////////////////////////////////// 928 929class GatherRenderer : public PictureRenderer { 930public: 931 virtual bool render(SkBitmap** out = NULL) SK_OVERRIDE { 932 SkRect bounds = SkRect::MakeWH(SkIntToScalar(fPicture->width()), 933 SkIntToScalar(fPicture->height())); 934 SkData* data = SkPictureUtils::GatherPixelRefs(fPicture, bounds); 935 SkSafeUnref(data); 936 937 return (fWritePath.isEmpty()); // we don't have anything to write 938 } 939 940private: 941 virtual SkString getConfigNameInternal() SK_OVERRIDE { 942 return SkString("gather_pixelrefs"); 943 } 944}; 945 946PictureRenderer* CreateGatherPixelRefsRenderer() { 947 return SkNEW(GatherRenderer); 948} 949 950/////////////////////////////////////////////////////////////////////////////// 951 952class PictureCloneRenderer : public PictureRenderer { 953public: 954 virtual bool render(SkBitmap** out = NULL) SK_OVERRIDE { 955 for (int i = 0; i < 100; ++i) { 956 SkPicture* clone = fPicture->clone(); 957 SkSafeUnref(clone); 958 } 959 960 return (fWritePath.isEmpty()); // we don't have anything to write 961 } 962 963private: 964 virtual SkString getConfigNameInternal() SK_OVERRIDE { 965 return SkString("picture_clone"); 966 } 967}; 968 969PictureRenderer* CreatePictureCloneRenderer() { 970 return SkNEW(PictureCloneRenderer); 971} 972 973} // namespace sk_tools 974