Home | History | Annotate | Download | only in tools
      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