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 #ifndef PictureRenderer_DEFINED
      9 #define PictureRenderer_DEFINED
     10 
     11 #include "SkCanvas.h"
     12 #include "SkDrawFilter.h"
     13 #include "SkJSONCPP.h"
     14 #include "SkMath.h"
     15 #include "SkPaint.h"
     16 #include "SkPicture.h"
     17 #include "SkPictureRecorder.h"
     18 #include "SkRect.h"
     19 #include "SkRefCnt.h"
     20 #include "SkString.h"
     21 #include "SkTDArray.h"
     22 #include "SkTypes.h"
     23 
     24 #if SK_SUPPORT_GPU
     25 #include "GrContextFactory.h"
     26 #include "GrContext.h"
     27 #endif
     28 
     29 #include "image_expectations.h"
     30 
     31 class SkBitmap;
     32 class SkCanvas;
     33 class SkGLContextHelper;
     34 class SkThread;
     35 
     36 namespace sk_tools {
     37 
     38 class TiledPictureRenderer;
     39 
     40 class PictureRenderer : public SkRefCnt {
     41 
     42 public:
     43     enum SkDeviceTypes {
     44 #if SK_ANGLE
     45         kAngle_DeviceType,
     46 #endif
     47 #if SK_MESA
     48         kMesa_DeviceType,
     49 #endif
     50         kBitmap_DeviceType,
     51 #if SK_SUPPORT_GPU
     52         kGPU_DeviceType,
     53         kNVPR_DeviceType,
     54 #endif
     55     };
     56 
     57     enum BBoxHierarchyType {
     58         kNone_BBoxHierarchyType = 0,
     59         kRTree_BBoxHierarchyType,
     60         kTileGrid_BBoxHierarchyType,
     61 
     62         kLast_BBoxHierarchyType = kTileGrid_BBoxHierarchyType,
     63     };
     64 
     65     // this uses SkPaint::Flags as a base and adds additional flags
     66     enum DrawFilterFlags {
     67         kNone_DrawFilterFlag = 0,
     68         kHinting_DrawFilterFlag = 0x10000, // toggles between no hinting and normal hinting
     69         kSlightHinting_DrawFilterFlag = 0x20000, // toggles between slight and normal hinting
     70         kAAClip_DrawFilterFlag = 0x40000, // toggles between soft and hard clip
     71         kMaskFilter_DrawFilterFlag = 0x80000, // toggles on/off mask filters (e.g., blurs)
     72     };
     73 
     74     SK_COMPILE_ASSERT(!(kMaskFilter_DrawFilterFlag & SkPaint::kAllFlags), maskfilter_flag_must_be_greater);
     75     SK_COMPILE_ASSERT(!(kHinting_DrawFilterFlag & SkPaint::kAllFlags),
     76             hinting_flag_must_be_greater);
     77     SK_COMPILE_ASSERT(!(kSlightHinting_DrawFilterFlag & SkPaint::kAllFlags),
     78             slight_hinting_flag_must_be_greater);
     79 
     80     /**
     81      * Called with each new SkPicture to render.
     82      *
     83      * @param pict The SkPicture to render.
     84      * @param writePath The output directory within which this renderer should write all images,
     85      *     or NULL if this renderer should not write all images.
     86      * @param mismatchPath The output directory within which this renderer should write any images
     87      *     which do not match expectations, or NULL if this renderer should not write mismatches.
     88      * @param inputFilename The name of the input file we are rendering.
     89      * @param useChecksumBasedFilenames Whether to use checksum-based filenames when writing
     90      *     bitmap images to disk.
     91      */
     92     virtual void init(const SkPicture* pict,
     93                       const SkString* writePath,
     94                       const SkString* mismatchPath,
     95                       const SkString* inputFilename,
     96                       bool useChecksumBasedFilenames);
     97 
     98     /**
     99      * TODO(epoger): Temporary hack, while we work on http://skbug.com/2584 ('bench_pictures is
    100      * timing reading pixels and writing json files'), such that:
    101      * - render_pictures can call this method and continue to work
    102      * - any other callers (bench_pictures) will skip calls to write() by default
    103      */
    104     void enableWrites() { fEnableWrites = true; }
    105 
    106     /**
    107      *  Set the viewport so that only the portion listed gets drawn.
    108      */
    109     void setViewport(SkISize size) { fViewport = size; }
    110 
    111     /**
    112      *  Set the scale factor at which draw the picture.
    113      */
    114     void setScaleFactor(SkScalar scale) { fScaleFactor = scale; }
    115 
    116     /**
    117      * Perform any setup that should done prior to each iteration of render() which should not be
    118      * timed.
    119      */
    120     virtual void setup() {}
    121 
    122     /**
    123      * Perform the work.  If this is being called within the context of bench_pictures,
    124      * this is the step that will be timed.
    125      *
    126      * Typically "the work" is rendering an SkPicture into a bitmap, but in some subclasses
    127      * it is recording the source SkPicture into another SkPicture.
    128      *
    129      * If fWritePath has been specified, the result of the work will be written to that dir.
    130      * If fMismatchPath has been specified, and the actual image result differs from its
    131      * expectation, the result of the work will be written to that dir.
    132      *
    133      * @param out If non-null, the implementing subclass MAY allocate an SkBitmap, copy the
    134      *            output image into it, and return it here.  (Some subclasses ignore this parameter)
    135      * @return bool True if rendering succeeded and, if fWritePath had been specified, the output
    136      *              was successfully written to a file.
    137      */
    138     virtual bool render(SkBitmap** out = NULL) = 0;
    139 
    140     /**
    141      * Called once finished with a particular SkPicture, before calling init again, and before
    142      * being done with this Renderer.
    143      */
    144     virtual void end();
    145 
    146     /**
    147      * If this PictureRenderer is actually a TiledPictureRender, return a pointer to this as a
    148      * TiledPictureRender so its methods can be called.
    149      */
    150     virtual TiledPictureRenderer* getTiledRenderer() { return NULL; }
    151 
    152     /**
    153      * Resets the GPU's state. Does nothing if the backing is raster. For a GPU renderer, calls
    154      * flush, swapBuffers and, if callFinish is true, finish.
    155      * @param callFinish Whether to call finish.
    156      */
    157     void resetState(bool callFinish);
    158 
    159     /**
    160      * Remove all decoded textures from the CPU caches and all uploaded textures
    161      * from the GPU.
    162      */
    163     void purgeTextures();
    164 
    165     /**
    166      * Set the backend type. Returns true on success and false on failure.
    167      */
    168 #if SK_SUPPORT_GPU
    169     bool setDeviceType(SkDeviceTypes deviceType, GrGLStandard gpuAPI = kNone_GrGLStandard) {
    170 #else
    171     bool setDeviceType(SkDeviceTypes deviceType) {
    172 #endif
    173         fDeviceType = deviceType;
    174 #if SK_SUPPORT_GPU
    175         // In case this function is called more than once
    176         SkSafeUnref(fGrContext);
    177         fGrContext = NULL;
    178         // Set to Native so it will have an initial value.
    179         GrContextFactory::GLContextType glContextType = GrContextFactory::kNative_GLContextType;
    180 #endif
    181         switch(deviceType) {
    182             case kBitmap_DeviceType:
    183                 return true;
    184 #if SK_SUPPORT_GPU
    185             case kGPU_DeviceType:
    186                 // Already set to GrContextFactory::kNative_GLContextType, above.
    187                 break;
    188             case kNVPR_DeviceType:
    189                 glContextType = GrContextFactory::kNVPR_GLContextType;
    190                 break;
    191 #if SK_ANGLE
    192             case kAngle_DeviceType:
    193                 glContextType = GrContextFactory::kANGLE_GLContextType;
    194                 break;
    195 #endif
    196 #if SK_MESA
    197             case kMesa_DeviceType:
    198                 glContextType = GrContextFactory::kMESA_GLContextType;
    199                 break;
    200 #endif
    201 #endif
    202             default:
    203                 // Invalid device type.
    204                 return false;
    205         }
    206 #if SK_SUPPORT_GPU
    207         fGrContext = fGrContextFactory.get(glContextType, gpuAPI);
    208         if (NULL == fGrContext) {
    209             return false;
    210         } else {
    211             fGrContext->ref();
    212             return true;
    213         }
    214 #endif
    215     }
    216 
    217 #if SK_SUPPORT_GPU
    218     void setSampleCount(int sampleCount) {
    219         fSampleCount = sampleCount;
    220     }
    221 #endif
    222 
    223     void setDrawFilters(DrawFilterFlags const * const filters, const SkString& configName) {
    224         memcpy(fDrawFilters, filters, sizeof(fDrawFilters));
    225         fDrawFiltersConfig = configName;
    226     }
    227 
    228     void setBBoxHierarchyType(BBoxHierarchyType bbhType) {
    229         fBBoxHierarchyType = bbhType;
    230     }
    231 
    232     BBoxHierarchyType getBBoxHierarchyType() { return fBBoxHierarchyType; }
    233 
    234     void setGridSize(int width, int height) {
    235         fGridInfo.fTileInterval.set(width, height);
    236     }
    237 
    238     void setJsonSummaryPtr(ImageResultsAndExpectations* jsonSummaryPtr) {
    239         fJsonSummaryPtr = jsonSummaryPtr;
    240     }
    241 
    242     bool isUsingBitmapDevice() {
    243         return kBitmap_DeviceType == fDeviceType;
    244     }
    245 
    246     virtual SkString getPerIterTimeFormat() { return SkString("%.2f"); }
    247 
    248     virtual SkString getNormalTimeFormat() { return SkString("%6.2f"); }
    249 
    250     /**
    251      * Reports the configuration of this PictureRenderer.
    252      */
    253     SkString getConfigName() {
    254         SkString config = this->getConfigNameInternal();
    255         if (!fViewport.isEmpty()) {
    256             config.appendf("_viewport_%ix%i", fViewport.width(), fViewport.height());
    257         }
    258         if (fScaleFactor != SK_Scalar1) {
    259             config.appendf("_scalar_%f", SkScalarToFloat(fScaleFactor));
    260         }
    261         if (kRTree_BBoxHierarchyType == fBBoxHierarchyType) {
    262             config.append("_rtree");
    263         } else if (kTileGrid_BBoxHierarchyType == fBBoxHierarchyType) {
    264             config.append("_grid");
    265             config.append("_");
    266             config.appendS32(fGridInfo.fTileInterval.width());
    267             config.append("x");
    268             config.appendS32(fGridInfo.fTileInterval.height());
    269         }
    270 #if SK_SUPPORT_GPU
    271         switch (fDeviceType) {
    272             case kGPU_DeviceType:
    273                 if (fSampleCount) {
    274                     config.appendf("_msaa%d", fSampleCount);
    275                 } else {
    276                     config.append("_gpu");
    277                 }
    278                 break;
    279             case kNVPR_DeviceType:
    280                 config.appendf("_nvprmsaa%d", fSampleCount);
    281                 break;
    282 #if SK_ANGLE
    283             case kAngle_DeviceType:
    284                 config.append("_angle");
    285                 break;
    286 #endif
    287 #if SK_MESA
    288             case kMesa_DeviceType:
    289                 config.append("_mesa");
    290                 break;
    291 #endif
    292             default:
    293                 // Assume that no extra info means bitmap.
    294                 break;
    295         }
    296 #endif
    297         config.append(fDrawFiltersConfig.c_str());
    298         return config;
    299     }
    300 
    301     Json::Value getJSONConfig() {
    302         Json::Value result;
    303 
    304         result["mode"] = this->getConfigNameInternal().c_str();
    305         result["scale"] = 1.0f;
    306         if (SK_Scalar1 != fScaleFactor) {
    307             result["scale"] = SkScalarToFloat(fScaleFactor);
    308         }
    309         if (kRTree_BBoxHierarchyType == fBBoxHierarchyType) {
    310             result["bbh"] = "rtree";
    311         } else if (kTileGrid_BBoxHierarchyType == fBBoxHierarchyType) {
    312             SkString tmp("grid_");
    313             tmp.appendS32(fGridInfo.fTileInterval.width());
    314             tmp.append("x");
    315             tmp.appendS32(fGridInfo.fTileInterval.height());
    316             result["bbh"] = tmp.c_str();
    317         }
    318 #if SK_SUPPORT_GPU
    319         SkString tmp;
    320         switch (fDeviceType) {
    321             case kGPU_DeviceType:
    322                 if (0 != fSampleCount) {
    323                     tmp = "msaa";
    324                     tmp.appendS32(fSampleCount);
    325                     result["config"] = tmp.c_str();
    326                 } else {
    327                     result["config"] = "gpu";
    328                 }
    329                 break;
    330             case kNVPR_DeviceType:
    331                 tmp = "nvprmsaa";
    332                 tmp.appendS32(fSampleCount);
    333                 result["config"] = tmp.c_str();
    334                 break;
    335 #if SK_ANGLE
    336             case kAngle_DeviceType:
    337                 result["config"] = "angle";
    338                 break;
    339 #endif
    340 #if SK_MESA
    341             case kMesa_DeviceType:
    342                 result["config"] = "mesa";
    343                 break;
    344 #endif
    345             default:
    346                 // Assume that no extra info means bitmap.
    347                 break;
    348         }
    349 #endif
    350         return result;
    351     }
    352 
    353 #if SK_SUPPORT_GPU
    354     bool isUsingGpuDevice() {
    355         switch (fDeviceType) {
    356             case kGPU_DeviceType:
    357             case kNVPR_DeviceType:
    358                 // fall through
    359 #if SK_ANGLE
    360             case kAngle_DeviceType:
    361                 // fall through
    362 #endif
    363 #if SK_MESA
    364             case kMesa_DeviceType:
    365 #endif
    366                 return true;
    367             default:
    368                 return false;
    369         }
    370     }
    371 
    372     SkGLContextHelper* getGLContext() {
    373         GrContextFactory::GLContextType glContextType
    374                 = GrContextFactory::kNull_GLContextType;
    375         switch(fDeviceType) {
    376             case kGPU_DeviceType:
    377                 glContextType = GrContextFactory::kNative_GLContextType;
    378                 break;
    379             case kNVPR_DeviceType:
    380                 glContextType = GrContextFactory::kNVPR_GLContextType;
    381                 break;
    382 #if SK_ANGLE
    383             case kAngle_DeviceType:
    384                 glContextType = GrContextFactory::kANGLE_GLContextType;
    385                 break;
    386 #endif
    387 #if SK_MESA
    388             case kMesa_DeviceType:
    389                 glContextType = GrContextFactory::kMESA_GLContextType;
    390                 break;
    391 #endif
    392             default:
    393                 return NULL;
    394         }
    395         return fGrContextFactory.getGLContext(glContextType);
    396     }
    397 
    398     GrContext* getGrContext() {
    399         return fGrContext;
    400     }
    401 
    402     const GrContext::Options& getGrContextOptions() {
    403         return fGrContextFactory.getGlobalOptions();
    404     }
    405 #endif
    406 
    407     SkCanvas* getCanvas() {
    408         return fCanvas;
    409     }
    410 
    411     const SkPicture* getPicture() {
    412         return fPicture;
    413     }
    414 
    415 #if SK_SUPPORT_GPU
    416     explicit PictureRenderer(const GrContext::Options &opts)
    417 #else
    418     PictureRenderer()
    419 #endif
    420         : fJsonSummaryPtr(NULL)
    421         , fDeviceType(kBitmap_DeviceType)
    422         , fEnableWrites(false)
    423         , fBBoxHierarchyType(kNone_BBoxHierarchyType)
    424         , fScaleFactor(SK_Scalar1)
    425 #if SK_SUPPORT_GPU
    426         , fGrContextFactory(opts)
    427         , fGrContext(NULL)
    428         , fSampleCount(0)
    429 #endif
    430         {
    431             fGridInfo.fMargin.setEmpty();
    432             fGridInfo.fOffset.setZero();
    433             fGridInfo.fTileInterval.set(1, 1);
    434             sk_bzero(fDrawFilters, sizeof(fDrawFilters));
    435             fViewport.set(0, 0);
    436         }
    437 
    438 #if SK_SUPPORT_GPU
    439     virtual ~PictureRenderer() {
    440         SkSafeUnref(fGrContext);
    441     }
    442 #endif
    443 
    444 protected:
    445     SkAutoTUnref<SkCanvas> fCanvas;
    446     SkAutoTUnref<const SkPicture> fPicture;
    447     bool                   fUseChecksumBasedFilenames;
    448     ImageResultsAndExpectations*   fJsonSummaryPtr;
    449     SkDeviceTypes          fDeviceType;
    450     bool                   fEnableWrites;
    451     BBoxHierarchyType      fBBoxHierarchyType;
    452     DrawFilterFlags        fDrawFilters[SkDrawFilter::kTypeCount];
    453     SkString               fDrawFiltersConfig;
    454     SkString               fWritePath;
    455     SkString               fMismatchPath;
    456     SkString               fInputFilename;
    457     SkTileGridFactory::TileGridInfo fGridInfo; // used when fBBoxHierarchyType is TileGrid
    458 
    459     void buildBBoxHierarchy();
    460 
    461     /**
    462      * Return the total width that should be drawn. If the viewport width has been set greater than
    463      * 0, this will be the minimum of the current SkPicture's width and the viewport's width.
    464      */
    465     int getViewWidth();
    466 
    467     /**
    468      * Return the total height that should be drawn. If the viewport height has been set greater
    469      * than 0, this will be the minimum of the current SkPicture's height and the viewport's height.
    470      */
    471     int getViewHeight();
    472 
    473     /**
    474      * Scales the provided canvas to the scale factor set by setScaleFactor.
    475      */
    476     void scaleToScaleFactor(SkCanvas*);
    477 
    478     SkBBHFactory* getFactory();
    479     uint32_t recordFlags() const { return 0; }
    480     SkCanvas* setupCanvas();
    481     virtual SkCanvas* setupCanvas(int width, int height);
    482 
    483     /**
    484      * Copy src to dest; if src==NULL, set dest to empty string.
    485      */
    486     static void CopyString(SkString* dest, const SkString* src);
    487 
    488 private:
    489     SkISize                fViewport;
    490     SkScalar               fScaleFactor;
    491 #if SK_SUPPORT_GPU
    492     GrContextFactory       fGrContextFactory;
    493     GrContext*             fGrContext;
    494     int                    fSampleCount;
    495 #endif
    496 
    497     virtual SkString getConfigNameInternal() = 0;
    498 
    499     typedef SkRefCnt INHERITED;
    500 };
    501 
    502 /**
    503  * This class does not do any rendering, but its render function executes recording, which we want
    504  * to time.
    505  */
    506 class RecordPictureRenderer : public PictureRenderer {
    507 public:
    508 #if SK_SUPPORT_GPU
    509     RecordPictureRenderer(const GrContext::Options &opts) : INHERITED(opts) { }
    510 #endif
    511 
    512     virtual bool render(SkBitmap** out = NULL) SK_OVERRIDE;
    513 
    514     virtual SkString getPerIterTimeFormat() SK_OVERRIDE { return SkString("%.4f"); }
    515 
    516     virtual SkString getNormalTimeFormat() SK_OVERRIDE { return SkString("%6.4f"); }
    517 
    518 protected:
    519     virtual SkCanvas* setupCanvas(int width, int height) SK_OVERRIDE;
    520 
    521 private:
    522     virtual SkString getConfigNameInternal() SK_OVERRIDE;
    523 
    524     typedef PictureRenderer INHERITED;
    525 };
    526 
    527 class PipePictureRenderer : public PictureRenderer {
    528 public:
    529 #if SK_SUPPORT_GPU
    530     PipePictureRenderer(const GrContext::Options &opts) : INHERITED(opts) { }
    531 #endif
    532 
    533     virtual bool render(SkBitmap** out = NULL) SK_OVERRIDE;
    534 
    535 private:
    536     virtual SkString getConfigNameInternal() SK_OVERRIDE;
    537 
    538     typedef PictureRenderer INHERITED;
    539 };
    540 
    541 class SimplePictureRenderer : public PictureRenderer {
    542 public:
    543 #if SK_SUPPORT_GPU
    544     SimplePictureRenderer(const GrContext::Options &opts) : INHERITED(opts) { }
    545 #endif
    546 
    547     virtual void init(const SkPicture* pict,
    548                       const SkString* writePath,
    549                       const SkString* mismatchPath,
    550                       const SkString* inputFilename,
    551                       bool useChecksumBasedFilenames) SK_OVERRIDE;
    552 
    553     virtual bool render(SkBitmap** out = NULL) SK_OVERRIDE;
    554 
    555 private:
    556     virtual SkString getConfigNameInternal() SK_OVERRIDE;
    557 
    558     typedef PictureRenderer INHERITED;
    559 };
    560 
    561 class TiledPictureRenderer : public PictureRenderer {
    562 public:
    563 #if SK_SUPPORT_GPU
    564     TiledPictureRenderer(const GrContext::Options &opts);
    565 #else
    566     TiledPictureRenderer();
    567 #endif
    568 
    569     virtual void init(const SkPicture* pict,
    570                       const SkString* writePath,
    571                       const SkString* mismatchPath,
    572                       const SkString* inputFilename,
    573                       bool useChecksumBasedFilenames) SK_OVERRIDE;
    574 
    575     /**
    576      * Renders to tiles, rather than a single canvas.
    577      * If fWritePath was provided, a separate file is
    578      * created for each tile, named "path0.png", "path1.png", etc.
    579      */
    580     virtual bool render(SkBitmap** out = NULL) SK_OVERRIDE;
    581 
    582     virtual void end() SK_OVERRIDE;
    583 
    584     void setTileWidth(int width) {
    585         fTileWidth = width;
    586     }
    587 
    588     int getTileWidth() const {
    589         return fTileWidth;
    590     }
    591 
    592     void setTileHeight(int height) {
    593         fTileHeight = height;
    594     }
    595 
    596     int getTileHeight() const {
    597         return fTileHeight;
    598     }
    599 
    600     void setTileWidthPercentage(double percentage) {
    601         fTileWidthPercentage = percentage;
    602     }
    603 
    604     double getTileWidthPercentage() const {
    605         return fTileWidthPercentage;
    606     }
    607 
    608     void setTileHeightPercentage(double percentage) {
    609         fTileHeightPercentage = percentage;
    610     }
    611 
    612     double getTileHeightPercentage() const {
    613         return fTileHeightPercentage;
    614     }
    615 
    616     void setTileMinPowerOf2Width(int width) {
    617         SkASSERT(SkIsPow2(width) && width > 0);
    618         if (!SkIsPow2(width) || width <= 0) {
    619             return;
    620         }
    621 
    622         fTileMinPowerOf2Width = width;
    623     }
    624 
    625     int getTileMinPowerOf2Width() const {
    626         return fTileMinPowerOf2Width;
    627     }
    628 
    629     virtual TiledPictureRenderer* getTiledRenderer() SK_OVERRIDE { return this; }
    630 
    631     virtual bool supportsTimingIndividualTiles() { return true; }
    632 
    633     /**
    634      * Report the number of tiles in the x and y directions. Must not be called before init.
    635      * @param x Output parameter identifying the number of tiles in the x direction.
    636      * @param y Output parameter identifying the number of tiles in the y direction.
    637      * @return True if the tiles have been set up, and x and y are meaningful. If false, x and y are
    638      *         unmodified.
    639      */
    640     bool tileDimensions(int& x, int&y);
    641 
    642     /**
    643      * Move to the next tile and return its indices. Must be called before calling drawCurrentTile
    644      * for the first time.
    645      * @param i Output parameter identifying the column of the next tile to be drawn on the next
    646      *          call to drawNextTile.
    647      * @param j Output parameter identifying the row  of the next tile to be drawn on the next call
    648      *          to drawNextTile.
    649      * @param True if the tiles have been created and the next tile to be drawn by drawCurrentTile
    650      *        is within the range of tiles. If false, i and j are unmodified.
    651      */
    652     bool nextTile(int& i, int& j);
    653 
    654     /**
    655      * Render one tile. This will draw the same tile each time it is called until nextTile is
    656      * called. The tile rendered will depend on how many calls have been made to nextTile.
    657      * It is an error to call this without first calling nextTile, or if nextTile returns false.
    658      */
    659     void drawCurrentTile();
    660 
    661 protected:
    662     SkTDArray<SkRect> fTileRects;
    663 
    664     virtual SkCanvas* setupCanvas(int width, int height) SK_OVERRIDE;
    665     virtual SkString getConfigNameInternal() SK_OVERRIDE;
    666 
    667 private:
    668     int    fTileWidth;
    669     int    fTileHeight;
    670     double fTileWidthPercentage;
    671     double fTileHeightPercentage;
    672     int    fTileMinPowerOf2Width;
    673 
    674     // These variables are only used for timing individual tiles.
    675     // Next tile to draw in fTileRects.
    676     int    fCurrentTileOffset;
    677     // Number of tiles in the x direction.
    678     int    fTilesX;
    679     // Number of tiles in the y direction.
    680     int    fTilesY;
    681 
    682     void setupTiles();
    683     void setupPowerOf2Tiles();
    684 
    685     typedef PictureRenderer INHERITED;
    686 };
    687 
    688 /**
    689  * This class does not do any rendering, but its render function executes turning an SkPictureRecord
    690  * into an SkPicturePlayback, which we want to time.
    691  */
    692 class PlaybackCreationRenderer : public PictureRenderer {
    693 public:
    694 #if SK_SUPPORT_GPU
    695     PlaybackCreationRenderer(const GrContext::Options &opts) : INHERITED(opts) { }
    696 #endif
    697 
    698     virtual void setup() SK_OVERRIDE;
    699 
    700     virtual bool render(SkBitmap** out = NULL) SK_OVERRIDE;
    701 
    702     virtual SkString getPerIterTimeFormat() SK_OVERRIDE { return SkString("%.4f"); }
    703 
    704     virtual SkString getNormalTimeFormat() SK_OVERRIDE { return SkString("%6.4f"); }
    705 
    706 private:
    707     SkAutoTDelete<SkPictureRecorder> fRecorder;
    708 
    709     virtual SkString getConfigNameInternal() SK_OVERRIDE;
    710 
    711     typedef PictureRenderer INHERITED;
    712 };
    713 
    714 #if SK_SUPPORT_GPU
    715 extern PictureRenderer* CreateGatherPixelRefsRenderer(const GrContext::Options& opts);
    716 #else
    717 extern PictureRenderer* CreateGatherPixelRefsRenderer();
    718 #endif
    719 
    720 }
    721 
    722 #endif  // PictureRenderer_DEFINED
    723