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