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(const SkPicture* pict,
52                           const SkString* writePath,
53                           const SkString* mismatchPath,
54                           const SkString* inputFilename,
55                           bool useChecksumBasedFilenames) {
56    this->CopyString(&fWritePath, writePath);
57    this->CopyString(&fMismatchPath, mismatchPath);
58    this->CopyString(&fInputFilename, inputFilename);
59    fUseChecksumBasedFilenames = useChecksumBasedFilenames;
60
61    SkASSERT(NULL == fPicture);
62    SkASSERT(NULL == fCanvas.get());
63    if (fPicture || fCanvas.get()) {
64        return;
65    }
66
67    SkASSERT(pict != NULL);
68    if (NULL == pict) {
69        return;
70    }
71
72    fPicture.reset(pict)->ref();
73    fCanvas.reset(this->setupCanvas());
74}
75
76void PictureRenderer::CopyString(SkString* dest, const SkString* src) {
77    if (src) {
78        dest->set(*src);
79    } else {
80        dest->reset();
81    }
82}
83
84class FlagsDrawFilter : public SkDrawFilter {
85public:
86    FlagsDrawFilter(PictureRenderer::DrawFilterFlags* flags) :
87        fFlags(flags) {}
88
89    virtual bool filter(SkPaint* paint, Type t) {
90        paint->setFlags(paint->getFlags() & ~fFlags[t] & SkPaint::kAllFlags);
91        if (PictureRenderer::kMaskFilter_DrawFilterFlag & fFlags[t]) {
92            SkMaskFilter* maskFilter = paint->getMaskFilter();
93            if (maskFilter) {
94                paint->setMaskFilter(NULL);
95            }
96        }
97        if (PictureRenderer::kHinting_DrawFilterFlag & fFlags[t]) {
98            paint->setHinting(SkPaint::kNo_Hinting);
99        } else if (PictureRenderer::kSlightHinting_DrawFilterFlag & fFlags[t]) {
100            paint->setHinting(SkPaint::kSlight_Hinting);
101        }
102        return true;
103    }
104
105private:
106    PictureRenderer::DrawFilterFlags* fFlags;
107};
108
109static void setUpFilter(SkCanvas* canvas, PictureRenderer::DrawFilterFlags* drawFilters) {
110    if (drawFilters && !canvas->getDrawFilter()) {
111        canvas->setDrawFilter(SkNEW_ARGS(FlagsDrawFilter, (drawFilters)))->unref();
112        if (drawFilters[0] & PictureRenderer::kAAClip_DrawFilterFlag) {
113            canvas->setAllowSoftClip(false);
114        }
115    }
116}
117
118SkCanvas* PictureRenderer::setupCanvas() {
119    const int width = this->getViewWidth();
120    const int height = this->getViewHeight();
121    return this->setupCanvas(width, height);
122}
123
124SkCanvas* PictureRenderer::setupCanvas(int width, int height) {
125    SkCanvas* canvas;
126    switch(fDeviceType) {
127        case kBitmap_DeviceType: {
128            SkBitmap bitmap;
129            sk_tools::setup_bitmap(&bitmap, width, height);
130            canvas = SkNEW_ARGS(SkCanvas, (bitmap));
131        }
132        break;
133#if SK_SUPPORT_GPU
134#if SK_ANGLE
135        case kAngle_DeviceType:
136            // fall through
137#endif
138#if SK_MESA
139        case kMesa_DeviceType:
140            // fall through
141#endif
142        case kGPU_DeviceType:
143        case kNVPR_DeviceType: {
144            SkAutoTUnref<GrSurface> target;
145            if (fGrContext) {
146                // create a render target to back the device
147                GrTextureDesc desc;
148                desc.fConfig = kSkia8888_GrPixelConfig;
149                desc.fFlags = kRenderTarget_GrTextureFlagBit;
150                desc.fWidth = width;
151                desc.fHeight = height;
152                desc.fSampleCnt = fSampleCount;
153                target.reset(fGrContext->createUncachedTexture(desc, NULL, 0));
154            }
155            if (NULL == target.get()) {
156                SkASSERT(0);
157                return NULL;
158            }
159
160            SkAutoTUnref<SkGpuDevice> device(SkGpuDevice::Create(target,
161                                         SkSurfaceProps(SkSurfaceProps::kLegacyFontHost_InitType)));
162            canvas = SkNEW_ARGS(SkCanvas, (device.get()));
163            break;
164        }
165#endif
166        default:
167            SkASSERT(0);
168            return NULL;
169    }
170    setUpFilter(canvas, fDrawFilters);
171    this->scaleToScaleFactor(canvas);
172
173    // Pictures often lie about their extent (i.e., claim to be 100x100 but
174    // only ever draw to 90x100). Clear here so the undrawn portion will have
175    // a consistent color
176    canvas->clear(SK_ColorTRANSPARENT);
177    return canvas;
178}
179
180void PictureRenderer::scaleToScaleFactor(SkCanvas* canvas) {
181    SkASSERT(canvas != NULL);
182    if (fScaleFactor != SK_Scalar1) {
183        canvas->scale(fScaleFactor, fScaleFactor);
184    }
185}
186
187void PictureRenderer::end() {
188    this->resetState(true);
189    fPicture.reset(NULL);
190    fCanvas.reset(NULL);
191}
192
193int PictureRenderer::getViewWidth() {
194    SkASSERT(fPicture != NULL);
195    int width = SkScalarCeilToInt(fPicture->cullRect().width() * fScaleFactor);
196    if (fViewport.width() > 0) {
197        width = SkMin32(width, fViewport.width());
198    }
199    return width;
200}
201
202int PictureRenderer::getViewHeight() {
203    SkASSERT(fPicture != NULL);
204    int height = SkScalarCeilToInt(fPicture->cullRect().height() * fScaleFactor);
205    if (fViewport.height() > 0) {
206        height = SkMin32(height, fViewport.height());
207    }
208    return height;
209}
210
211/** Converts fPicture to a picture that uses a BBoxHierarchy.
212 *  PictureRenderer subclasses that are used to test picture playback
213 *  should call this method during init.
214 */
215void PictureRenderer::buildBBoxHierarchy() {
216    SkASSERT(fPicture);
217    if (kNone_BBoxHierarchyType != fBBoxHierarchyType && fPicture) {
218        SkAutoTDelete<SkBBHFactory> factory(this->getFactory());
219        SkPictureRecorder recorder;
220        SkCanvas* canvas = recorder.beginRecording(fPicture->cullRect().width(),
221                                                   fPicture->cullRect().height(),
222                                                   factory.get(),
223                                                   this->recordFlags());
224        fPicture->playback(canvas);
225        fPicture.reset(recorder.endRecording());
226    }
227}
228
229void PictureRenderer::resetState(bool callFinish) {
230#if SK_SUPPORT_GPU
231    SkGLContextHelper* glContext = this->getGLContext();
232    if (NULL == glContext) {
233        SkASSERT(kBitmap_DeviceType == fDeviceType);
234        return;
235    }
236
237    fGrContext->flush();
238    glContext->swapBuffers();
239    if (callFinish) {
240        SK_GL(*glContext, Finish());
241    }
242#endif
243}
244
245void PictureRenderer::purgeTextures() {
246    SkDiscardableMemoryPool* pool = SkGetGlobalDiscardableMemoryPool();
247
248    pool->dumpPool();
249
250#if SK_SUPPORT_GPU
251    SkGLContextHelper* glContext = this->getGLContext();
252    if (NULL == glContext) {
253        SkASSERT(kBitmap_DeviceType == fDeviceType);
254        return;
255    }
256
257    // resetState should've already done this
258    fGrContext->flush();
259
260    fGrContext->purgeAllUnlockedResources();
261#endif
262}
263
264/**
265 * Write the canvas to an image file and/or JSON summary.
266 *
267 * @param canvas Must be non-null. Canvas to be written to a file.
268 * @param writePath If nonempty, write the binary image to a file within this directory.
269 * @param mismatchPath If nonempty, write the binary image to a file within this directory,
270 *     but only if the image does not match expectations.
271 * @param inputFilename If we are writing out a binary image, use this to build its filename.
272 * @param jsonSummaryPtr If not null, add image results (checksum) to this summary.
273 * @param useChecksumBasedFilenames If true, use checksum-based filenames when writing to disk.
274 * @param tileNumberPtr If not null, which tile number this image contains.
275 *
276 * @return bool True if the operation completed successfully.
277 */
278static bool write(SkCanvas* canvas, const SkString& writePath, const SkString& mismatchPath,
279                  const SkString& inputFilename, ImageResultsAndExpectations *jsonSummaryPtr,
280                  bool useChecksumBasedFilenames, const int* tileNumberPtr=NULL) {
281    SkASSERT(canvas != NULL);
282    if (NULL == canvas) {
283        return false;
284    }
285
286    SkBitmap bitmap;
287    SkISize size = canvas->getDeviceSize();
288    setup_bitmap(&bitmap, size.width(), size.height());
289
290    canvas->readPixels(&bitmap, 0, 0);
291    force_all_opaque(bitmap);
292    BitmapAndDigest bitmapAndDigest(bitmap);
293
294    SkString escapedInputFilename(inputFilename);
295    replace_char(&escapedInputFilename, '.', '_');
296
297    // TODO(epoger): what about including the config type within outputFilename?  That way,
298    // we could combine results of different config types without conflicting filenames.
299    SkString outputFilename;
300    const char *outputSubdirPtr = NULL;
301    if (useChecksumBasedFilenames) {
302        ImageDigest *imageDigestPtr = bitmapAndDigest.getImageDigestPtr();
303        outputSubdirPtr = escapedInputFilename.c_str();
304        outputFilename.set(imageDigestPtr->getHashType());
305        outputFilename.append("_");
306        outputFilename.appendU64(imageDigestPtr->getHashValue());
307    } else {
308        outputFilename.set(escapedInputFilename);
309        if (tileNumberPtr) {
310            outputFilename.append("-tile");
311            outputFilename.appendS32(*tileNumberPtr);
312        }
313    }
314    outputFilename.append(".png");
315
316    if (jsonSummaryPtr) {
317        ImageDigest *imageDigestPtr = bitmapAndDigest.getImageDigestPtr();
318        SkString outputRelativePath;
319        if (outputSubdirPtr) {
320            outputRelativePath.set(outputSubdirPtr);
321            outputRelativePath.append("/");  // always use "/", even on Windows
322            outputRelativePath.append(outputFilename);
323        } else {
324            outputRelativePath.set(outputFilename);
325        }
326
327        jsonSummaryPtr->add(inputFilename.c_str(), outputRelativePath.c_str(),
328                            *imageDigestPtr, tileNumberPtr);
329        if (!mismatchPath.isEmpty() &&
330            !jsonSummaryPtr->getExpectation(inputFilename.c_str(),
331                                            tileNumberPtr).matches(*imageDigestPtr)) {
332            if (!write_bitmap_to_disk(bitmap, mismatchPath, outputSubdirPtr, outputFilename)) {
333                return false;
334            }
335        }
336    }
337
338    if (writePath.isEmpty()) {
339        return true;
340    } else {
341        return write_bitmap_to_disk(bitmap, writePath, outputSubdirPtr, outputFilename);
342    }
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(SkBitmap** out) {
358    SkAutoTDelete<SkBBHFactory> factory(this->getFactory());
359    SkPictureRecorder recorder;
360    SkCanvas* canvas = recorder.beginRecording(SkIntToScalar(this->getViewWidth()),
361                                               SkIntToScalar(this->getViewHeight()),
362                                               factory.get(),
363                                               this->recordFlags());
364    this->scaleToScaleFactor(canvas);
365    fPicture->playback(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::Join(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 (out) {
397        *out = SkNEW(SkBitmap);
398        setup_bitmap(*out, SkScalarCeilToInt(fPicture->cullRect().width()),
399                           SkScalarCeilToInt(fPicture->cullRect().height()));
400        fCanvas->readPixels(*out, 0, 0);
401    }
402    if (fEnableWrites) {
403        return write(fCanvas, fWritePath, fMismatchPath, fInputFilename, fJsonSummaryPtr,
404                     fUseChecksumBasedFilenames);
405    } else {
406        return true;
407    }
408}
409
410SkString PipePictureRenderer::getConfigNameInternal() {
411    return SkString("pipe");
412}
413
414///////////////////////////////////////////////////////////////////////////////////////////////
415
416void SimplePictureRenderer::init(const SkPicture* picture, const SkString* writePath,
417                                 const SkString* mismatchPath, const SkString* inputFilename,
418                                 bool useChecksumBasedFilenames) {
419    INHERITED::init(picture, writePath, mismatchPath, inputFilename, useChecksumBasedFilenames);
420    this->buildBBoxHierarchy();
421}
422
423bool SimplePictureRenderer::render(SkBitmap** out) {
424    SkASSERT(fCanvas.get() != NULL);
425    SkASSERT(fPicture);
426    if (NULL == fCanvas.get() || NULL == fPicture) {
427        return false;
428    }
429
430    fCanvas->drawPicture(fPicture);
431    fCanvas->flush();
432    if (out) {
433        *out = SkNEW(SkBitmap);
434        setup_bitmap(*out, SkScalarCeilToInt(fPicture->cullRect().width()),
435                           SkScalarCeilToInt(fPicture->cullRect().height()));
436        fCanvas->readPixels(*out, 0, 0);
437    }
438    if (fEnableWrites) {
439        return write(fCanvas, fWritePath, fMismatchPath, fInputFilename, fJsonSummaryPtr,
440                     fUseChecksumBasedFilenames);
441    } else {
442        return true;
443    }
444}
445
446SkString SimplePictureRenderer::getConfigNameInternal() {
447    return SkString("simple");
448}
449
450///////////////////////////////////////////////////////////////////////////////////////////////
451
452#if SK_SUPPORT_GPU
453TiledPictureRenderer::TiledPictureRenderer(const GrContext::Options& opts)
454    : INHERITED(opts)
455    , fTileWidth(kDefaultTileWidth)
456#else
457TiledPictureRenderer::TiledPictureRenderer()
458    : fTileWidth(kDefaultTileWidth)
459#endif
460    , fTileHeight(kDefaultTileHeight)
461    , fTileWidthPercentage(0.0)
462    , fTileHeightPercentage(0.0)
463    , fTileMinPowerOf2Width(0)
464    , fCurrentTileOffset(-1)
465    , fTilesX(0)
466    , fTilesY(0) { }
467
468void TiledPictureRenderer::init(const SkPicture* pict, const SkString* writePath,
469                                const SkString* mismatchPath, const SkString* inputFilename,
470                                bool useChecksumBasedFilenames) {
471    SkASSERT(pict);
472    SkASSERT(0 == fTileRects.count());
473    if (NULL == pict || fTileRects.count() != 0) {
474        return;
475    }
476
477    // Do not call INHERITED::init(), which would create a (potentially large) canvas which is not
478    // used by bench_pictures.
479    fPicture.reset(pict)->ref();
480    this->CopyString(&fWritePath, writePath);
481    this->CopyString(&fMismatchPath, mismatchPath);
482    this->CopyString(&fInputFilename, inputFilename);
483    fUseChecksumBasedFilenames = useChecksumBasedFilenames;
484    this->buildBBoxHierarchy();
485
486    if (fTileWidthPercentage > 0) {
487        fTileWidth = SkScalarCeilToInt(float(fTileWidthPercentage * fPicture->cullRect().width() / 100));
488    }
489    if (fTileHeightPercentage > 0) {
490        fTileHeight = SkScalarCeilToInt(float(fTileHeightPercentage * fPicture->cullRect().height() / 100));
491    }
492
493    if (fTileMinPowerOf2Width > 0) {
494        this->setupPowerOf2Tiles();
495    } else {
496        this->setupTiles();
497    }
498    fCanvas.reset(this->setupCanvas(fTileWidth, fTileHeight));
499    // Initialize to -1 so that the first call to nextTile will set this up to draw tile 0 on the
500    // first call to drawCurrentTile.
501    fCurrentTileOffset = -1;
502}
503
504void TiledPictureRenderer::end() {
505    fTileRects.reset();
506    this->INHERITED::end();
507}
508
509void TiledPictureRenderer::setupTiles() {
510    // Only use enough tiles to cover the viewport
511    const int width = this->getViewWidth();
512    const int height = this->getViewHeight();
513
514    fTilesX = fTilesY = 0;
515    for (int tile_y_start = 0; tile_y_start < height; tile_y_start += fTileHeight) {
516        fTilesY++;
517        for (int tile_x_start = 0; tile_x_start < width; tile_x_start += fTileWidth) {
518            if (0 == tile_y_start) {
519                // Only count tiles in the X direction on the first pass.
520                fTilesX++;
521            }
522            *fTileRects.append() = SkRect::MakeXYWH(SkIntToScalar(tile_x_start),
523                                                    SkIntToScalar(tile_y_start),
524                                                    SkIntToScalar(fTileWidth),
525                                                    SkIntToScalar(fTileHeight));
526        }
527    }
528}
529
530bool TiledPictureRenderer::tileDimensions(int &x, int &y) {
531    if (fTileRects.count() == 0 || NULL == fPicture) {
532        return false;
533    }
534    x = fTilesX;
535    y = fTilesY;
536    return true;
537}
538
539// The goal of the powers of two tiles is to minimize the amount of wasted tile
540// space in the width-wise direction and then minimize the number of tiles. The
541// constraints are that every tile must have a pixel width that is a power of
542// two and also be of some minimal width (that is also a power of two).
543//
544// This is solved by first taking our picture size and rounding it up to the
545// multiple of the minimal width. The binary representation of this rounded
546// value gives us the tiles we need: a bit of value one means we need a tile of
547// that size.
548void TiledPictureRenderer::setupPowerOf2Tiles() {
549    // Only use enough tiles to cover the viewport
550    const int width = this->getViewWidth();
551    const int height = this->getViewHeight();
552
553    int rounded_value = width;
554    if (width % fTileMinPowerOf2Width != 0) {
555        rounded_value = width - (width % fTileMinPowerOf2Width) + fTileMinPowerOf2Width;
556    }
557
558    int num_bits = SkScalarCeilToInt(scalar_log2(SkIntToScalar(width)));
559    int largest_possible_tile_size = 1 << num_bits;
560
561    fTilesX = fTilesY = 0;
562    // The tile height is constant for a particular picture.
563    for (int tile_y_start = 0; tile_y_start < height; tile_y_start += fTileHeight) {
564        fTilesY++;
565        int tile_x_start = 0;
566        int current_width = largest_possible_tile_size;
567        // Set fTileWidth to be the width of the widest tile, so that each canvas is large enough
568        // to draw each tile.
569        fTileWidth = current_width;
570
571        while (current_width >= fTileMinPowerOf2Width) {
572            // It is very important this is a bitwise AND.
573            if (current_width & rounded_value) {
574                if (0 == tile_y_start) {
575                    // Only count tiles in the X direction on the first pass.
576                    fTilesX++;
577                }
578                *fTileRects.append() = SkRect::MakeXYWH(SkIntToScalar(tile_x_start),
579                                                        SkIntToScalar(tile_y_start),
580                                                        SkIntToScalar(current_width),
581                                                        SkIntToScalar(fTileHeight));
582                tile_x_start += current_width;
583            }
584
585            current_width >>= 1;
586        }
587    }
588}
589
590/**
591 * Draw the specified picture to the canvas translated to rectangle provided, so that this mini
592 * canvas represents the rectangle's portion of the overall picture.
593 * Saves and restores so that the initial clip and matrix return to their state before this function
594 * is called.
595 */
596static void draw_tile_to_canvas(SkCanvas* canvas,
597                                const SkRect& tileRect,
598                                const SkPicture* picture) {
599    int saveCount = canvas->save();
600    // Translate so that we draw the correct portion of the picture.
601    // Perform a postTranslate so that the scaleFactor does not interfere with the positioning.
602    SkMatrix mat(canvas->getTotalMatrix());
603    mat.postTranslate(-tileRect.fLeft, -tileRect.fTop);
604    canvas->setMatrix(mat);
605    canvas->drawPicture(picture);
606    canvas->restoreToCount(saveCount);
607    canvas->flush();
608}
609
610///////////////////////////////////////////////////////////////////////////////////////////////
611
612/**
613 * Copies the entirety of the src bitmap (typically a tile) into a portion of the dst bitmap.
614 * If the src bitmap is too large to fit within the dst bitmap after the x and y
615 * offsets have been applied, any excess will be ignored (so only the top-left portion of the
616 * src bitmap will be copied).
617 *
618 * @param src source bitmap
619 * @param dst destination bitmap
620 * @param xOffset x-offset within destination bitmap
621 * @param yOffset y-offset within destination bitmap
622 */
623static void bitmapCopyAtOffset(const SkBitmap& src, SkBitmap* dst,
624                               int xOffset, int yOffset) {
625    for (int y = 0; y <src.height() && y + yOffset < dst->height() ; y++) {
626        for (int x = 0; x < src.width() && x + xOffset < dst->width() ; x++) {
627            *dst->getAddr32(xOffset + x, yOffset + y) = *src.getAddr32(x, y);
628        }
629    }
630}
631
632bool TiledPictureRenderer::nextTile(int &i, int &j) {
633    if (++fCurrentTileOffset < fTileRects.count()) {
634        i = fCurrentTileOffset % fTilesX;
635        j = fCurrentTileOffset / fTilesX;
636        return true;
637    }
638    return false;
639}
640
641void TiledPictureRenderer::drawCurrentTile() {
642    SkASSERT(fCurrentTileOffset >= 0 && fCurrentTileOffset < fTileRects.count());
643    draw_tile_to_canvas(fCanvas, fTileRects[fCurrentTileOffset], fPicture);
644}
645
646bool TiledPictureRenderer::render(SkBitmap** out) {
647    SkASSERT(fPicture != NULL);
648    if (NULL == fPicture) {
649        return false;
650    }
651
652    SkBitmap bitmap;
653    if (out){
654        *out = SkNEW(SkBitmap);
655        setup_bitmap(*out, SkScalarCeilToInt(fPicture->cullRect().width()),
656                           SkScalarCeilToInt(fPicture->cullRect().height()));
657        setup_bitmap(&bitmap, fTileWidth, fTileHeight);
658    }
659    bool success = true;
660    for (int i = 0; i < fTileRects.count(); ++i) {
661        draw_tile_to_canvas(fCanvas, fTileRects[i], fPicture);
662        if (fEnableWrites) {
663            success &= write(fCanvas, fWritePath, fMismatchPath, fInputFilename, fJsonSummaryPtr,
664                             fUseChecksumBasedFilenames, &i);
665        }
666        if (out) {
667            if (fCanvas->readPixels(&bitmap, 0, 0)) {
668                // Add this tile to the entire bitmap.
669                bitmapCopyAtOffset(bitmap, *out, SkScalarFloorToInt(fTileRects[i].left()),
670                                   SkScalarFloorToInt(fTileRects[i].top()));
671            } else {
672                success = false;
673            }
674        }
675    }
676    return success;
677}
678
679SkCanvas* TiledPictureRenderer::setupCanvas(int width, int height) {
680    SkCanvas* canvas = this->INHERITED::setupCanvas(width, height);
681    SkASSERT(fPicture);
682    // Clip the tile to an area that is completely inside both the SkPicture and the viewport. This
683    // is mostly important for tiles on the right and bottom edges as they may go over this area and
684    // the picture may have some commands that draw outside of this area and so should not actually
685    // be written.
686    // Uses a clipRegion so that it will be unaffected by the scale factor, which may have been set
687    // by INHERITED::setupCanvas.
688    SkRegion clipRegion;
689    clipRegion.setRect(0, 0, this->getViewWidth(), this->getViewHeight());
690    canvas->clipRegion(clipRegion);
691    return canvas;
692}
693
694SkString TiledPictureRenderer::getConfigNameInternal() {
695    SkString name;
696    if (fTileMinPowerOf2Width > 0) {
697        name.append("pow2tile_");
698        name.appendf("%i", fTileMinPowerOf2Width);
699    } else {
700        name.append("tile_");
701        if (fTileWidthPercentage > 0) {
702            name.appendf("%.f%%", fTileWidthPercentage);
703        } else {
704            name.appendf("%i", fTileWidth);
705        }
706    }
707    name.append("x");
708    if (fTileHeightPercentage > 0) {
709        name.appendf("%.f%%", fTileHeightPercentage);
710    } else {
711        name.appendf("%i", fTileHeight);
712    }
713    return name;
714}
715
716///////////////////////////////////////////////////////////////////////////////////////////////
717
718void PlaybackCreationRenderer::setup() {
719    SkAutoTDelete<SkBBHFactory> factory(this->getFactory());
720    fRecorder.reset(SkNEW(SkPictureRecorder));
721    SkCanvas* canvas = fRecorder->beginRecording(SkIntToScalar(this->getViewWidth()),
722                                                 SkIntToScalar(this->getViewHeight()),
723                                                 factory.get(),
724                                                 this->recordFlags());
725    this->scaleToScaleFactor(canvas);
726    canvas->drawPicture(fPicture);
727}
728
729bool PlaybackCreationRenderer::render(SkBitmap** out) {
730    fPicture.reset(fRecorder->endRecording());
731    // Since this class does not actually render, return false.
732    return false;
733}
734
735SkString PlaybackCreationRenderer::getConfigNameInternal() {
736    return SkString("playback_creation");
737}
738
739///////////////////////////////////////////////////////////////////////////////////////////////
740// SkPicture variants for each BBoxHierarchy type
741
742SkBBHFactory* PictureRenderer::getFactory() {
743    switch (fBBoxHierarchyType) {
744        case kNone_BBoxHierarchyType:
745            return NULL;
746        case kRTree_BBoxHierarchyType:
747            return SkNEW(SkRTreeFactory);
748        case kTileGrid_BBoxHierarchyType:
749            return SkNEW_ARGS(SkTileGridFactory, (fGridInfo));
750    }
751    SkASSERT(0); // invalid bbhType
752    return NULL;
753}
754
755///////////////////////////////////////////////////////////////////////////////
756
757class GatherRenderer : public PictureRenderer {
758public:
759#if SK_SUPPORT_GPU
760    GatherRenderer(const GrContext::Options& opts) : INHERITED(opts) { }
761#endif
762
763    virtual bool render(SkBitmap** out = NULL) SK_OVERRIDE {
764        SkRect bounds = SkRect::MakeWH(SkIntToScalar(fPicture->cullRect().width()),
765                                       SkIntToScalar(fPicture->cullRect().height()));
766        SkData* data = SkPictureUtils::GatherPixelRefs(fPicture, bounds);
767        SkSafeUnref(data);
768
769        return (fWritePath.isEmpty());    // we don't have anything to write
770    }
771
772private:
773    virtual SkString getConfigNameInternal() SK_OVERRIDE {
774        return SkString("gather_pixelrefs");
775    }
776
777    typedef PictureRenderer INHERITED;
778};
779
780#if SK_SUPPORT_GPU
781PictureRenderer* CreateGatherPixelRefsRenderer(const GrContext::Options& opts) {
782    return SkNEW_ARGS(GatherRenderer, (opts));
783}
784#else
785PictureRenderer* CreateGatherPixelRefsRenderer() {
786    return SkNEW(GatherRenderer);
787}
788#endif
789
790} // namespace sk_tools
791