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