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