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