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