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