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