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