PictureRenderer.cpp revision 905379a74e307509f4f0d9ccdf96e846237f2c6c
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 "SkCanvas.h"
12#include "SkDevice.h"
13#include "SkGPipe.h"
14#if SK_SUPPORT_GPU
15#include "SkGpuDevice.h"
16#endif
17#include "SkGraphics.h"
18#include "SkImageEncoder.h"
19#include "SkMaskFilter.h"
20#include "SkMatrix.h"
21#include "SkPicture.h"
22#include "SkRTree.h"
23#include "SkScalar.h"
24#include "SkStream.h"
25#include "SkString.h"
26#include "SkTemplates.h"
27#include "SkTileGridPicture.h"
28#include "SkTDArray.h"
29#include "SkThreadUtils.h"
30#include "SkTypes.h"
31#include "SkData.h"
32#include "SkPictureUtils.h"
33
34namespace sk_tools {
35
36enum {
37    kDefaultTileWidth = 256,
38    kDefaultTileHeight = 256
39};
40
41void PictureRenderer::init(SkPicture* pict) {
42    SkASSERT(NULL == fPicture);
43    SkASSERT(NULL == fCanvas.get());
44    if (fPicture != NULL || NULL != fCanvas.get()) {
45        return;
46    }
47
48    SkASSERT(pict != NULL);
49    if (NULL == pict) {
50        return;
51    }
52
53    fPicture = pict;
54    fPicture->ref();
55    fCanvas.reset(this->setupCanvas());
56}
57
58class FlagsDrawFilter : public SkDrawFilter {
59public:
60    FlagsDrawFilter(PictureRenderer::DrawFilterFlags* flags) :
61        fFlags(flags) {}
62
63    virtual bool filter(SkPaint* paint, Type t) {
64        paint->setFlags(paint->getFlags() & ~fFlags[t] & SkPaint::kAllFlags);
65        if (PictureRenderer::kBlur_DrawFilterFlag & fFlags[t]) {
66            SkMaskFilter* maskFilter = paint->getMaskFilter();
67            SkMaskFilter::BlurInfo blurInfo;
68            if (maskFilter && maskFilter->asABlur(&blurInfo)) {
69                paint->setMaskFilter(NULL);
70            }
71        }
72        if (PictureRenderer::kHinting_DrawFilterFlag & fFlags[t]) {
73            paint->setHinting(SkPaint::kNo_Hinting);
74        } else if (PictureRenderer::kSlightHinting_DrawFilterFlag & fFlags[t]) {
75            paint->setHinting(SkPaint::kSlight_Hinting);
76        }
77        return true;
78    }
79
80private:
81    PictureRenderer::DrawFilterFlags* fFlags;
82};
83
84static void setUpFilter(SkCanvas* canvas, PictureRenderer::DrawFilterFlags* drawFilters) {
85    if (drawFilters && !canvas->getDrawFilter()) {
86        canvas->setDrawFilter(SkNEW_ARGS(FlagsDrawFilter, (drawFilters)))->unref();
87        if (drawFilters[0] & PictureRenderer::kAAClip_DrawFilterFlag) {
88            canvas->setAllowSoftClip(false);
89        }
90    }
91}
92
93SkCanvas* PictureRenderer::setupCanvas() {
94    const int width = this->getViewWidth();
95    const int height = this->getViewHeight();
96    return this->setupCanvas(width, height);
97}
98
99SkCanvas* PictureRenderer::setupCanvas(int width, int height) {
100    SkCanvas* canvas;
101    switch(fDeviceType) {
102        case kBitmap_DeviceType: {
103            SkBitmap bitmap;
104            sk_tools::setup_bitmap(&bitmap, width, height);
105            canvas = SkNEW_ARGS(SkCanvas, (bitmap));
106        }
107        break;
108#if SK_SUPPORT_GPU
109        case kGPU_DeviceType: {
110            SkAutoTUnref<SkGpuDevice> device(SkNEW_ARGS(SkGpuDevice,
111                                                    (fGrContext, SkBitmap::kARGB_8888_Config,
112                                                    width, height)));
113            canvas = SkNEW_ARGS(SkCanvas, (device.get()));
114        }
115        break;
116#endif
117        default:
118            SkASSERT(0);
119            return NULL;
120    }
121    setUpFilter(canvas, fDrawFilters);
122    this->scaleToScaleFactor(canvas);
123    return canvas;
124}
125
126void PictureRenderer::scaleToScaleFactor(SkCanvas* canvas) {
127    SkASSERT(canvas != NULL);
128    if (fScaleFactor != SK_Scalar1) {
129        canvas->scale(fScaleFactor, fScaleFactor);
130    }
131}
132
133void PictureRenderer::end() {
134    this->resetState(true);
135    SkSafeUnref(fPicture);
136    fPicture = NULL;
137    fCanvas.reset(NULL);
138}
139
140int PictureRenderer::getViewWidth() {
141    SkASSERT(fPicture != NULL);
142    int width = fPicture->width() * fScaleFactor;
143    if (fViewport.width() > 0) {
144        width = SkMin32(width, fViewport.width());
145    }
146    return width;
147}
148
149int PictureRenderer::getViewHeight() {
150    SkASSERT(fPicture != NULL);
151    int height = fPicture->height() * fScaleFactor;
152    if (fViewport.height() > 0) {
153        height = SkMin32(height, fViewport.height());
154    }
155    return height;
156}
157
158/** Converts fPicture to a picture that uses a BBoxHierarchy.
159 *  PictureRenderer subclasses that are used to test picture playback
160 *  should call this method during init.
161 */
162void PictureRenderer::buildBBoxHierarchy() {
163    SkASSERT(NULL != fPicture);
164    if (kNone_BBoxHierarchyType != fBBoxHierarchyType && NULL != fPicture) {
165        SkPicture* newPicture = this->createPicture();
166        SkCanvas* recorder = newPicture->beginRecording(fPicture->width(), fPicture->height(),
167                                                        this->recordFlags());
168        fPicture->draw(recorder);
169        newPicture->endRecording();
170        fPicture->unref();
171        fPicture = newPicture;
172    }
173}
174
175void PictureRenderer::resetState(bool callFinish) {
176#if SK_SUPPORT_GPU
177    if (this->isUsingGpuDevice()) {
178        SkGLContext* glContext = fGrContextFactory.getGLContext(
179            GrContextFactory::kNative_GLContextType);
180
181        SkASSERT(glContext != NULL);
182        if (NULL == glContext) {
183            return;
184        }
185
186        fGrContext->flush();
187        if (callFinish) {
188            SK_GL(*glContext, Finish());
189        }
190    }
191#endif
192}
193
194uint32_t PictureRenderer::recordFlags() {
195    return ((kNone_BBoxHierarchyType == fBBoxHierarchyType) ? 0 :
196        SkPicture::kOptimizeForClippedPlayback_RecordingFlag) |
197        SkPicture::kUsePathBoundsForClip_RecordingFlag;
198}
199
200/**
201 * Write the canvas to the specified path.
202 * @param canvas Must be non-null. Canvas to be written to a file.
203 * @param path Path for the file to be written. Should have no extension; write() will append
204 *             an appropriate one. Passed in by value so it can be modified.
205 * @return bool True if the Canvas is written to a file.
206 */
207static bool write(SkCanvas* canvas, SkString path) {
208    SkASSERT(canvas != NULL);
209    if (NULL == canvas) {
210        return false;
211    }
212
213    SkBitmap bitmap;
214    SkISize size = canvas->getDeviceSize();
215    sk_tools::setup_bitmap(&bitmap, size.width(), size.height());
216
217    canvas->readPixels(&bitmap, 0, 0);
218    sk_tools::force_all_opaque(bitmap);
219
220    // Since path is passed in by value, it is okay to modify it.
221    path.append(".png");
222    return SkImageEncoder::EncodeFile(path.c_str(), bitmap, SkImageEncoder::kPNG_Type, 100);
223}
224
225/**
226 * If path is non NULL, append number to it, and call write(SkCanvas*, SkString) to write the
227 * provided canvas to a file. Returns true if path is NULL or if write() succeeds.
228 */
229static bool writeAppendNumber(SkCanvas* canvas, const SkString* path, int number) {
230    if (NULL == path) {
231        return true;
232    }
233    SkString pathWithNumber(*path);
234    pathWithNumber.appendf("%i", number);
235    return write(canvas, pathWithNumber);
236}
237
238///////////////////////////////////////////////////////////////////////////////////////////////
239
240SkCanvas* RecordPictureRenderer::setupCanvas(int width, int height) {
241    // defer the canvas setup until the render step
242    return NULL;
243}
244
245static bool PNGEncodeBitmapToStream(SkWStream* wStream, const SkBitmap& bm) {
246    return SkImageEncoder::EncodeStream(wStream, bm, SkImageEncoder::kPNG_Type, 100);
247}
248
249bool RecordPictureRenderer::render(const SkString* path, SkBitmap** out) {
250    SkAutoTUnref<SkPicture> replayer(this->createPicture());
251    SkCanvas* recorder = replayer->beginRecording(this->getViewWidth(), this->getViewHeight(),
252                                                  this->recordFlags());
253    this->scaleToScaleFactor(recorder);
254    fPicture->draw(recorder);
255    replayer->endRecording();
256    if (path != NULL) {
257        // Record the new picture as a new SKP with PNG encoded bitmaps.
258        SkString skpPath(*path);
259        // ".skp" was removed from 'path' before being passed in here.
260        skpPath.append(".skp");
261        SkFILEWStream stream(skpPath.c_str());
262        replayer->serialize(&stream, &PNGEncodeBitmapToStream);
263        return true;
264    }
265    return false;
266}
267
268SkString RecordPictureRenderer::getConfigNameInternal() {
269    return SkString("record");
270}
271
272///////////////////////////////////////////////////////////////////////////////////////////////
273
274bool PipePictureRenderer::render(const SkString* path, SkBitmap** out) {
275    SkASSERT(fCanvas.get() != NULL);
276    SkASSERT(fPicture != NULL);
277    if (NULL == fCanvas.get() || NULL == fPicture) {
278        return false;
279    }
280
281    PipeController pipeController(fCanvas.get());
282    SkGPipeWriter writer;
283    SkCanvas* pipeCanvas = writer.startRecording(&pipeController);
284    pipeCanvas->drawPicture(*fPicture);
285    writer.endRecording();
286    fCanvas->flush();
287    if (NULL != path) {
288        return write(fCanvas, *path);
289    }
290    if (NULL != out) {
291        *out = SkNEW(SkBitmap);
292        setup_bitmap(*out, fPicture->width(), fPicture->height());
293        fCanvas->readPixels(*out, 0, 0);
294    }
295    return true;
296}
297
298SkString PipePictureRenderer::getConfigNameInternal() {
299    return SkString("pipe");
300}
301
302///////////////////////////////////////////////////////////////////////////////////////////////
303
304void SimplePictureRenderer::init(SkPicture* picture) {
305    INHERITED::init(picture);
306    this->buildBBoxHierarchy();
307}
308
309bool SimplePictureRenderer::render(const SkString* path, SkBitmap** out) {
310    SkASSERT(fCanvas.get() != NULL);
311    SkASSERT(fPicture != NULL);
312    if (NULL == fCanvas.get() || NULL == fPicture) {
313        return false;
314    }
315
316    fCanvas->drawPicture(*fPicture);
317    fCanvas->flush();
318    if (NULL != path) {
319        return write(fCanvas, *path);
320    }
321
322    if (NULL != out) {
323        *out = SkNEW(SkBitmap);
324        setup_bitmap(*out, fPicture->width(), fPicture->height());
325        fCanvas->readPixels(*out, 0, 0);
326    }
327
328    return true;
329}
330
331SkString SimplePictureRenderer::getConfigNameInternal() {
332    return SkString("simple");
333}
334
335///////////////////////////////////////////////////////////////////////////////////////////////
336
337TiledPictureRenderer::TiledPictureRenderer()
338    : fTileWidth(kDefaultTileWidth)
339    , fTileHeight(kDefaultTileHeight)
340    , fTileWidthPercentage(0.0)
341    , fTileHeightPercentage(0.0)
342    , fTileMinPowerOf2Width(0)
343    , fCurrentTileOffset(-1)
344    , fTilesX(0)
345    , fTilesY(0) { }
346
347void TiledPictureRenderer::init(SkPicture* pict) {
348    SkASSERT(pict != NULL);
349    SkASSERT(0 == fTileRects.count());
350    if (NULL == pict || fTileRects.count() != 0) {
351        return;
352    }
353
354    // Do not call INHERITED::init(), which would create a (potentially large) canvas which is not
355    // used by bench_pictures.
356    fPicture = pict;
357    fPicture->ref();
358    this->buildBBoxHierarchy();
359
360    if (fTileWidthPercentage > 0) {
361        fTileWidth = sk_float_ceil2int(float(fTileWidthPercentage * fPicture->width() / 100));
362    }
363    if (fTileHeightPercentage > 0) {
364        fTileHeight = sk_float_ceil2int(float(fTileHeightPercentage * fPicture->height() / 100));
365    }
366
367    if (fTileMinPowerOf2Width > 0) {
368        this->setupPowerOf2Tiles();
369    } else {
370        this->setupTiles();
371    }
372    fCanvas.reset(this->setupCanvas(fTileWidth, fTileHeight));
373    // Initialize to -1 so that the first call to nextTile will set this up to draw tile 0 on the
374    // first call to drawCurrentTile.
375    fCurrentTileOffset = -1;
376}
377
378void TiledPictureRenderer::end() {
379    fTileRects.reset();
380    this->INHERITED::end();
381}
382
383void TiledPictureRenderer::setupTiles() {
384    // Only use enough tiles to cover the viewport
385    const int width = this->getViewWidth();
386    const int height = this->getViewHeight();
387
388    fTilesX = fTilesY = 0;
389    for (int tile_y_start = 0; tile_y_start < height; tile_y_start += fTileHeight) {
390        fTilesY++;
391        for (int tile_x_start = 0; tile_x_start < width; tile_x_start += fTileWidth) {
392            if (0 == tile_y_start) {
393                // Only count tiles in the X direction on the first pass.
394                fTilesX++;
395            }
396            *fTileRects.append() = SkRect::MakeXYWH(SkIntToScalar(tile_x_start),
397                                                    SkIntToScalar(tile_y_start),
398                                                    SkIntToScalar(fTileWidth),
399                                                    SkIntToScalar(fTileHeight));
400        }
401    }
402}
403
404bool TiledPictureRenderer::tileDimensions(int &x, int &y) {
405    if (fTileRects.count() == 0 || NULL == fPicture) {
406        return false;
407    }
408    x = fTilesX;
409    y = fTilesY;
410    return true;
411}
412
413// The goal of the powers of two tiles is to minimize the amount of wasted tile
414// space in the width-wise direction and then minimize the number of tiles. The
415// constraints are that every tile must have a pixel width that is a power of
416// two and also be of some minimal width (that is also a power of two).
417//
418// This is solved by first taking our picture size and rounding it up to the
419// multiple of the minimal width. The binary representation of this rounded
420// value gives us the tiles we need: a bit of value one means we need a tile of
421// that size.
422void TiledPictureRenderer::setupPowerOf2Tiles() {
423    // Only use enough tiles to cover the viewport
424    const int width = this->getViewWidth();
425    const int height = this->getViewHeight();
426
427    int rounded_value = width;
428    if (width % fTileMinPowerOf2Width != 0) {
429        rounded_value = width - (width % fTileMinPowerOf2Width) + fTileMinPowerOf2Width;
430    }
431
432    int num_bits = SkScalarCeilToInt(SkScalarLog2(SkIntToScalar(width)));
433    int largest_possible_tile_size = 1 << num_bits;
434
435    fTilesX = fTilesY = 0;
436    // The tile height is constant for a particular picture.
437    for (int tile_y_start = 0; tile_y_start < height; tile_y_start += fTileHeight) {
438        fTilesY++;
439        int tile_x_start = 0;
440        int current_width = largest_possible_tile_size;
441        // Set fTileWidth to be the width of the widest tile, so that each canvas is large enough
442        // to draw each tile.
443        fTileWidth = current_width;
444
445        while (current_width >= fTileMinPowerOf2Width) {
446            // It is very important this is a bitwise AND.
447            if (current_width & rounded_value) {
448                if (0 == tile_y_start) {
449                    // Only count tiles in the X direction on the first pass.
450                    fTilesX++;
451                }
452                *fTileRects.append() = SkRect::MakeXYWH(SkIntToScalar(tile_x_start),
453                                                        SkIntToScalar(tile_y_start),
454                                                        SkIntToScalar(current_width),
455                                                        SkIntToScalar(fTileHeight));
456                tile_x_start += current_width;
457            }
458
459            current_width >>= 1;
460        }
461    }
462}
463
464/**
465 * Draw the specified playback to the canvas translated to rectangle provided, so that this mini
466 * canvas represents the rectangle's portion of the overall picture.
467 * Saves and restores so that the initial clip and matrix return to their state before this function
468 * is called.
469 */
470template<class T>
471static void DrawTileToCanvas(SkCanvas* canvas, const SkRect& tileRect, T* playback) {
472    int saveCount = canvas->save();
473    // Translate so that we draw the correct portion of the picture.
474    // Perform a postTranslate so that the scaleFactor does not interfere with the positioning.
475    SkMatrix mat(canvas->getTotalMatrix());
476    mat.postTranslate(-tileRect.fLeft, -tileRect.fTop);
477    canvas->setMatrix(mat);
478    playback->draw(canvas);
479    canvas->restoreToCount(saveCount);
480    canvas->flush();
481}
482
483///////////////////////////////////////////////////////////////////////////////////////////////
484
485static void bitmapCopySubset(const SkBitmap& src, SkBitmap* dst, int xDst,
486                             int yDst) {
487    for (int y = 0; y <src.height() && y + yDst < dst->height() ; y++) {
488        for (int x = 0; x < src.width() && x + xDst < dst->width() ; x++) {
489            *dst->getAddr32(xDst + x, yDst + y) = *src.getAddr32(x, y);
490        }
491    }
492}
493
494bool TiledPictureRenderer::nextTile(int &i, int &j) {
495    if (++fCurrentTileOffset < fTileRects.count()) {
496        i = fCurrentTileOffset % fTilesX;
497        j = fCurrentTileOffset / fTilesX;
498        return true;
499    }
500    return false;
501}
502
503void TiledPictureRenderer::drawCurrentTile() {
504    SkASSERT(fCurrentTileOffset >= 0 && fCurrentTileOffset < fTileRects.count());
505    DrawTileToCanvas(fCanvas, fTileRects[fCurrentTileOffset], fPicture);
506}
507
508bool TiledPictureRenderer::render(const SkString* path, SkBitmap** out) {
509    SkASSERT(fPicture != NULL);
510    if (NULL == fPicture) {
511        return false;
512    }
513
514    SkBitmap bitmap;
515    if (out){
516        *out = SkNEW(SkBitmap);
517        setup_bitmap(*out, fPicture->width(), fPicture->height());
518        setup_bitmap(&bitmap, fTileWidth, fTileHeight);
519    }
520    bool success = true;
521    for (int i = 0; i < fTileRects.count(); ++i) {
522        DrawTileToCanvas(fCanvas, fTileRects[i], fPicture);
523        if (NULL != path) {
524            success &= writeAppendNumber(fCanvas, path, i);
525        }
526        if (NULL != out) {
527            if (fCanvas->readPixels(&bitmap, 0, 0)) {
528                bitmapCopySubset(bitmap, *out, SkScalarFloorToInt(fTileRects[i].left()),
529                                 SkScalarFloorToInt(fTileRects[i].top()));
530            } else {
531                success = false;
532            }
533        }
534    }
535    return success;
536}
537
538SkCanvas* TiledPictureRenderer::setupCanvas(int width, int height) {
539    SkCanvas* canvas = this->INHERITED::setupCanvas(width, height);
540    SkASSERT(fPicture != NULL);
541    // Clip the tile to an area that is completely inside both the SkPicture and the viewport. This
542    // is mostly important for tiles on the right and bottom edges as they may go over this area and
543    // the picture may have some commands that draw outside of this area and so should not actually
544    // be written.
545    // Uses a clipRegion so that it will be unaffected by the scale factor, which may have been set
546    // by INHERITED::setupCanvas.
547    SkRegion clipRegion;
548    clipRegion.setRect(0, 0, this->getViewWidth(), this->getViewHeight());
549    canvas->clipRegion(clipRegion);
550    return canvas;
551}
552
553SkString TiledPictureRenderer::getConfigNameInternal() {
554    SkString name;
555    if (fTileMinPowerOf2Width > 0) {
556        name.append("pow2tile_");
557        name.appendf("%i", fTileMinPowerOf2Width);
558    } else {
559        name.append("tile_");
560        if (fTileWidthPercentage > 0) {
561            name.appendf("%.f%%", fTileWidthPercentage);
562        } else {
563            name.appendf("%i", fTileWidth);
564        }
565    }
566    name.append("x");
567    if (fTileHeightPercentage > 0) {
568        name.appendf("%.f%%", fTileHeightPercentage);
569    } else {
570        name.appendf("%i", fTileHeight);
571    }
572    return name;
573}
574
575///////////////////////////////////////////////////////////////////////////////////////////////
576
577// Holds all of the information needed to draw a set of tiles.
578class CloneData : public SkRunnable {
579
580public:
581    CloneData(SkPicture* clone, SkCanvas* canvas, SkTDArray<SkRect>& rects, int start, int end,
582              SkRunnable* done)
583        : fClone(clone)
584        , fCanvas(canvas)
585        , fPath(NULL)
586        , fRects(rects)
587        , fStart(start)
588        , fEnd(end)
589        , fSuccess(NULL)
590        , fDone(done) {
591        SkASSERT(fDone != NULL);
592    }
593
594    virtual void run() SK_OVERRIDE {
595        SkGraphics::SetTLSFontCacheLimit(1024 * 1024);
596
597        SkBitmap bitmap;
598        if (fBitmap != NULL) {
599            // All tiles are the same size.
600            setup_bitmap(&bitmap, SkScalarFloorToInt(fRects[0].width()), SkScalarFloorToInt(fRects[0].height()));
601        }
602
603        for (int i = fStart; i < fEnd; i++) {
604            DrawTileToCanvas(fCanvas, fRects[i], fClone);
605            if (fPath != NULL && !writeAppendNumber(fCanvas, fPath, i)
606                && fSuccess != NULL) {
607                *fSuccess = false;
608                // If one tile fails to write to a file, do not continue drawing the rest.
609                break;
610            }
611            if (fBitmap != NULL) {
612                if (fCanvas->readPixels(&bitmap, 0, 0)) {
613                    SkAutoLockPixels alp(*fBitmap);
614                    bitmapCopySubset(bitmap, fBitmap, SkScalarFloorToInt(fRects[i].left()),
615                                     SkScalarFloorToInt(fRects[i].top()));
616                } else {
617                    *fSuccess = false;
618                    // If one tile fails to read pixels, do not continue drawing the rest.
619                    break;
620                }
621            }
622        }
623        fDone->run();
624    }
625
626    void setPathAndSuccess(const SkString* path, bool* success) {
627        fPath = path;
628        fSuccess = success;
629    }
630
631    void setBitmap(SkBitmap* bitmap) {
632        fBitmap = bitmap;
633    }
634
635private:
636    // All pointers unowned.
637    SkPicture*         fClone;      // Picture to draw from. Each CloneData has a unique one which
638                                    // is threadsafe.
639    SkCanvas*          fCanvas;     // Canvas to draw to. Reused for each tile.
640    const SkString*    fPath;       // If non-null, path to write the result to as a PNG.
641    SkTDArray<SkRect>& fRects;      // All tiles of the picture.
642    const int          fStart;      // Range of tiles drawn by this thread.
643    const int          fEnd;
644    bool*              fSuccess;    // Only meaningful if path is non-null. Shared by all threads,
645                                    // and only set to false upon failure to write to a PNG.
646    SkRunnable*        fDone;
647    SkBitmap*          fBitmap;
648};
649
650MultiCorePictureRenderer::MultiCorePictureRenderer(int threadCount)
651: fNumThreads(threadCount)
652, fThreadPool(threadCount)
653, fCountdown(threadCount) {
654    // Only need to create fNumThreads - 1 clones, since one thread will use the base
655    // picture.
656    fPictureClones = SkNEW_ARRAY(SkPicture, fNumThreads - 1);
657    fCloneData = SkNEW_ARRAY(CloneData*, fNumThreads);
658}
659
660void MultiCorePictureRenderer::init(SkPicture *pict) {
661    // Set fPicture and the tiles.
662    this->INHERITED::init(pict);
663    for (int i = 0; i < fNumThreads; ++i) {
664        *fCanvasPool.append() = this->setupCanvas(this->getTileWidth(), this->getTileHeight());
665    }
666    // Only need to create fNumThreads - 1 clones, since one thread will use the base picture.
667    fPicture->clone(fPictureClones, fNumThreads - 1);
668    // Populate each thread with the appropriate data.
669    // Group the tiles into nearly equal size chunks, rounding up so we're sure to cover them all.
670    const int chunkSize = (fTileRects.count() + fNumThreads - 1) / fNumThreads;
671
672    for (int i = 0; i < fNumThreads; i++) {
673        SkPicture* pic;
674        if (i == fNumThreads-1) {
675            // The last set will use the original SkPicture.
676            pic = fPicture;
677        } else {
678            pic = &fPictureClones[i];
679        }
680        const int start = i * chunkSize;
681        const int end = SkMin32(start + chunkSize, fTileRects.count());
682        fCloneData[i] = SkNEW_ARGS(CloneData,
683                                   (pic, fCanvasPool[i], fTileRects, start, end, &fCountdown));
684    }
685}
686
687bool MultiCorePictureRenderer::render(const SkString *path, SkBitmap** out) {
688    bool success = true;
689    if (path != NULL) {
690        for (int i = 0; i < fNumThreads-1; i++) {
691            fCloneData[i]->setPathAndSuccess(path, &success);
692        }
693    }
694
695    if (NULL != out) {
696        *out = SkNEW(SkBitmap);
697        setup_bitmap(*out, fPicture->width(), fPicture->height());
698        for (int i = 0; i < fNumThreads; i++) {
699            fCloneData[i]->setBitmap(*out);
700        }
701    } else {
702        for (int i = 0; i < fNumThreads; i++) {
703            fCloneData[i]->setBitmap(NULL);
704        }
705    }
706
707    fCountdown.reset(fNumThreads);
708    for (int i = 0; i < fNumThreads; i++) {
709        fThreadPool.add(fCloneData[i]);
710    }
711    fCountdown.wait();
712
713    return success;
714}
715
716void MultiCorePictureRenderer::end() {
717    for (int i = 0; i < fNumThreads - 1; i++) {
718        SkDELETE(fCloneData[i]);
719        fCloneData[i] = NULL;
720    }
721
722    fCanvasPool.unrefAll();
723
724    this->INHERITED::end();
725}
726
727MultiCorePictureRenderer::~MultiCorePictureRenderer() {
728    // Each individual CloneData was deleted in end.
729    SkDELETE_ARRAY(fCloneData);
730    SkDELETE_ARRAY(fPictureClones);
731}
732
733SkString MultiCorePictureRenderer::getConfigNameInternal() {
734    SkString name = this->INHERITED::getConfigNameInternal();
735    name.appendf("_multi_%i_threads", fNumThreads);
736    return name;
737}
738
739///////////////////////////////////////////////////////////////////////////////////////////////
740
741void PlaybackCreationRenderer::setup() {
742    fReplayer.reset(this->createPicture());
743    SkCanvas* recorder = fReplayer->beginRecording(this->getViewWidth(), this->getViewHeight(),
744                                                   this->recordFlags());
745    this->scaleToScaleFactor(recorder);
746    fPicture->draw(recorder);
747}
748
749bool PlaybackCreationRenderer::render(const SkString*, SkBitmap** out) {
750    fReplayer->endRecording();
751    // Since this class does not actually render, return false.
752    return false;
753}
754
755SkString PlaybackCreationRenderer::getConfigNameInternal() {
756    return SkString("playback_creation");
757}
758
759///////////////////////////////////////////////////////////////////////////////////////////////
760// SkPicture variants for each BBoxHierarchy type
761
762class RTreePicture : public SkPicture {
763public:
764    virtual SkBBoxHierarchy* createBBoxHierarchy() const SK_OVERRIDE{
765        static const int kRTreeMinChildren = 6;
766        static const int kRTreeMaxChildren = 11;
767        SkScalar aspectRatio = SkScalarDiv(SkIntToScalar(fWidth),
768                                           SkIntToScalar(fHeight));
769        return SkRTree::Create(kRTreeMinChildren, kRTreeMaxChildren,
770                               aspectRatio);
771    }
772};
773
774SkPicture* PictureRenderer::createPicture() {
775    switch (fBBoxHierarchyType) {
776        case kNone_BBoxHierarchyType:
777            return SkNEW(SkPicture);
778        case kRTree_BBoxHierarchyType:
779            return SkNEW(RTreePicture);
780        case kTileGrid_BBoxHierarchyType:
781            return SkNEW_ARGS(SkTileGridPicture, (fGridWidth, fGridHeight, fPicture->width(),
782                fPicture->height()));
783    }
784    SkASSERT(0); // invalid bbhType
785    return NULL;
786}
787
788///////////////////////////////////////////////////////////////////////////////
789
790class GatherRenderer : public PictureRenderer {
791public:
792    virtual bool render(const SkString* path, SkBitmap** out = NULL)
793            SK_OVERRIDE {
794        SkRect bounds = SkRect::MakeWH(SkIntToScalar(fPicture->width()),
795                                       SkIntToScalar(fPicture->height()));
796        SkData* data = SkPictureUtils::GatherPixelRefs(fPicture, bounds);
797        SkSafeUnref(data);
798
799        return NULL == path;    // we don't have anything to write
800    }
801
802private:
803    virtual SkString getConfigNameInternal() SK_OVERRIDE {
804        return SkString("gather_pixelrefs");
805    }
806};
807
808PictureRenderer* CreateGatherPixelRefsRenderer() {
809    return SkNEW(GatherRenderer);
810}
811
812///////////////////////////////////////////////////////////////////////////////
813
814class PictureCloneRenderer : public PictureRenderer {
815public:
816    virtual bool render(const SkString* path, SkBitmap** out = NULL)
817            SK_OVERRIDE {
818        for (int i = 0; i < 100; ++i) {
819            SkPicture* clone = fPicture->clone();
820            SkSafeUnref(clone);
821        }
822
823        return NULL == path;    // we don't have anything to write
824    }
825
826private:
827    virtual SkString getConfigNameInternal() SK_OVERRIDE {
828        return SkString("picture_clone");
829    }
830};
831
832PictureRenderer* CreatePictureCloneRenderer() {
833    return SkNEW(PictureCloneRenderer);
834}
835
836} // namespace sk_tools
837