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