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