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