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