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