blob: 59ba680842fe8e77eb1c90f4b4ec753f4d3d8ae7 [file] [log] [blame]
keyar@chromium.orgb3fb7c12012-08-20 21:02:49 +00001/*
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
keyar@chromium.org451bb9f2012-07-26 17:27:57 +00008#include "PictureRenderer.h"
scroggo@google.com58b4ead2012-08-31 16:15:22 +00009#include "picture_utils.h"
keyar@chromium.org451bb9f2012-07-26 17:27:57 +000010#include "SamplePipeControllers.h"
11#include "SkCanvas.h"
12#include "SkDevice.h"
13#include "SkGPipe.h"
scroggo@google.com58b4ead2012-08-31 16:15:22 +000014#if SK_SUPPORT_GPU
15#include "SkGpuDevice.h"
16#endif
17#include "SkGraphics.h"
18#include "SkImageEncoder.h"
caryclark@google.coma3622372012-11-06 21:26:13 +000019#include "SkMaskFilter.h"
keyar@chromium.orgea826952012-08-23 15:24:13 +000020#include "SkMatrix.h"
keyar@chromium.org451bb9f2012-07-26 17:27:57 +000021#include "SkPicture.h"
junov@chromium.org9313ca42012-11-02 18:11:49 +000022#include "SkRTree.h"
keyar@chromium.orgea826952012-08-23 15:24:13 +000023#include "SkScalar.h"
keyar@chromium.org9299ede2012-08-21 19:05:08 +000024#include "SkString.h"
scroggo@google.com58b4ead2012-08-31 16:15:22 +000025#include "SkTemplates.h"
junov@chromium.org7b537062012-11-06 18:58:43 +000026#include "SkTileGrid.h"
keyar@chromium.org451bb9f2012-07-26 17:27:57 +000027#include "SkTDArray.h"
scroggo@google.com58b4ead2012-08-31 16:15:22 +000028#include "SkThreadUtils.h"
keyar@chromium.org451bb9f2012-07-26 17:27:57 +000029#include "SkTypes.h"
keyar@chromium.org4ea96c52012-08-20 15:03:29 +000030
keyar@chromium.org451bb9f2012-07-26 17:27:57 +000031namespace sk_tools {
32
33enum {
34 kDefaultTileWidth = 256,
35 kDefaultTileHeight = 256
36};
37
keyar@chromium.org9d696c02012-08-07 17:11:33 +000038void PictureRenderer::init(SkPicture* pict) {
keyar@chromium.org78a35c52012-08-20 15:03:44 +000039 SkASSERT(NULL == fPicture);
40 SkASSERT(NULL == fCanvas.get());
41 if (fPicture != NULL || NULL != fCanvas.get()) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +000042 return;
43 }
44
45 SkASSERT(pict != NULL);
keyar@chromium.org78a35c52012-08-20 15:03:44 +000046 if (NULL == pict) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +000047 return;
48 }
49
50 fPicture = pict;
junov@chromium.org9313ca42012-11-02 18:11:49 +000051 fPicture->ref();
keyar@chromium.orga474ce32012-08-20 15:03:57 +000052 fCanvas.reset(this->setupCanvas());
53}
54
caryclark@google.coma3622372012-11-06 21:26:13 +000055class FlagsDrawFilter : public SkDrawFilter {
56public:
57 FlagsDrawFilter(PictureRenderer::DrawFilterFlags* flags) :
58 fFlags(flags) {}
59
60 virtual void filter(SkPaint* paint, Type t) {
61 paint->setFlags(paint->getFlags() & ~fFlags[t] & SkPaint::kAllFlags);
62 if (PictureRenderer::kBlur_DrawFilterFlag & fFlags[t]) {
63 SkMaskFilter* maskFilter = paint->getMaskFilter();
64 SkMaskFilter::BlurInfo blurInfo;
65 if (maskFilter && maskFilter->asABlur(&blurInfo)) {
66 paint->setMaskFilter(NULL);
67 }
68 }
69 if (PictureRenderer::kHinting_DrawFilterFlag & fFlags[t]) {
70 paint->setHinting(SkPaint::kNo_Hinting);
71 } else if (PictureRenderer::kSlightHinting_DrawFilterFlag & fFlags[t]) {
72 paint->setHinting(SkPaint::kSlight_Hinting);
73 }
74 }
75
76private:
77 PictureRenderer::DrawFilterFlags* fFlags;
78};
79
80static SkCanvas* setUpFilter(SkCanvas* canvas, PictureRenderer::DrawFilterFlags* drawFilters) {
81 if (drawFilters && !canvas->getDrawFilter()) {
82 canvas->setDrawFilter(SkNEW_ARGS(FlagsDrawFilter, (drawFilters)))->unref();
83 }
84 return canvas;
85}
86
keyar@chromium.orga474ce32012-08-20 15:03:57 +000087SkCanvas* PictureRenderer::setupCanvas() {
88 return this->setupCanvas(fPicture->width(), fPicture->height());
89}
90
91SkCanvas* PictureRenderer::setupCanvas(int width, int height) {
caryclark@google.coma3622372012-11-06 21:26:13 +000092 SkCanvas* canvas;
keyar@chromium.org4ea96c52012-08-20 15:03:29 +000093 switch(fDeviceType) {
94 case kBitmap_DeviceType: {
95 SkBitmap bitmap;
keyar@chromium.orga474ce32012-08-20 15:03:57 +000096 sk_tools::setup_bitmap(&bitmap, width, height);
caryclark@google.coma3622372012-11-06 21:26:13 +000097 canvas = SkNEW_ARGS(SkCanvas, (bitmap));
98 return setUpFilter(canvas, fDrawFilters);
keyar@chromium.org4ea96c52012-08-20 15:03:29 +000099 }
100#if SK_SUPPORT_GPU
101 case kGPU_DeviceType: {
keyar@chromium.org4ea96c52012-08-20 15:03:29 +0000102 SkAutoTUnref<SkGpuDevice> device(SkNEW_ARGS(SkGpuDevice,
keyar@chromium.org06125642012-08-20 15:03:33 +0000103 (fGrContext, SkBitmap::kARGB_8888_Config,
keyar@chromium.orga474ce32012-08-20 15:03:57 +0000104 width, height)));
caryclark@google.coma3622372012-11-06 21:26:13 +0000105 canvas = SkNEW_ARGS(SkCanvas, (device.get()));
106 return setUpFilter(canvas, fDrawFilters);
keyar@chromium.org4ea96c52012-08-20 15:03:29 +0000107 }
108#endif
109 default:
110 SkASSERT(0);
111 }
keyar@chromium.orga474ce32012-08-20 15:03:57 +0000112
113 return NULL;
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000114}
115
116void PictureRenderer::end() {
keyar@chromium.org77a55222012-08-20 15:03:47 +0000117 this->resetState();
junov@chromium.org9313ca42012-11-02 18:11:49 +0000118 SkSafeUnref(fPicture);
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000119 fPicture = NULL;
120 fCanvas.reset(NULL);
121}
122
junov@chromium.org9313ca42012-11-02 18:11:49 +0000123/** Converts fPicture to a picture that uses a BBoxHierarchy.
124 * PictureRenderer subclasses that are used to test picture playback
125 * should call this method during init.
126 */
127void PictureRenderer::buildBBoxHierarchy() {
128 SkASSERT(NULL != fPicture);
129 if (kNone_BBoxHierarchyType != fBBoxHierarchyType && NULL != fPicture) {
130 SkPicture* newPicture = this->createPicture();
131 SkCanvas* recorder = newPicture->beginRecording(fPicture->width(), fPicture->height(),
132 this->recordFlags());
133 fPicture->draw(recorder);
134 newPicture->endRecording();
135 fPicture->unref();
136 fPicture = newPicture;
137 }
138}
139
keyar@chromium.org77a55222012-08-20 15:03:47 +0000140void PictureRenderer::resetState() {
keyar@chromium.org28136b32012-08-20 15:04:15 +0000141#if SK_SUPPORT_GPU
142 if (this->isUsingGpuDevice()) {
143 SkGLContext* glContext = fGrContextFactory.getGLContext(
144 GrContextFactory::kNative_GLContextType);
keyar@chromium.org28136b32012-08-20 15:04:15 +0000145
146 SkASSERT(glContext != NULL);
147 if (NULL == glContext) {
148 return;
149 }
150
scroggo@google.com9a412522012-09-07 15:21:18 +0000151 fGrContext->flush();
keyar@chromium.org77a55222012-08-20 15:03:47 +0000152 SK_GL(*glContext, Finish());
keyar@chromium.org77a55222012-08-20 15:03:47 +0000153 }
keyar@chromium.orga40c20d2012-08-20 15:04:12 +0000154#endif
keyar@chromium.org77a55222012-08-20 15:03:47 +0000155}
156
junov@chromium.org9313ca42012-11-02 18:11:49 +0000157uint32_t PictureRenderer::recordFlags() {
158 return kNone_BBoxHierarchyType == fBBoxHierarchyType ? 0 :
159 SkPicture::kOptimizeForClippedPlayback_RecordingFlag;
160}
161
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000162/**
163 * Write the canvas to the specified path.
164 * @param canvas Must be non-null. Canvas to be written to a file.
165 * @param path Path for the file to be written. Should have no extension; write() will append
166 * an appropriate one. Passed in by value so it can be modified.
167 * @return bool True if the Canvas is written to a file.
168 */
169static bool write(SkCanvas* canvas, SkString path) {
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000170 SkASSERT(canvas != NULL);
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000171 if (NULL == canvas) {
keyar@chromium.org9299ede2012-08-21 19:05:08 +0000172 return false;
173 }
174
175 SkBitmap bitmap;
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000176 SkISize size = canvas->getDeviceSize();
177 sk_tools::setup_bitmap(&bitmap, size.width(), size.height());
keyar@chromium.org9299ede2012-08-21 19:05:08 +0000178
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000179 canvas->readPixels(&bitmap, 0, 0);
keyar@chromium.org9299ede2012-08-21 19:05:08 +0000180 sk_tools::force_all_opaque(bitmap);
181
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000182 // Since path is passed in by value, it is okay to modify it.
183 path.append(".png");
keyar@chromium.org9299ede2012-08-21 19:05:08 +0000184 return SkImageEncoder::EncodeFile(path.c_str(), bitmap, SkImageEncoder::kPNG_Type, 100);
185}
186
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000187/**
188 * If path is non NULL, append number to it, and call write(SkCanvas*, SkString) to write the
189 * provided canvas to a file. Returns true if path is NULL or if write() succeeds.
190 */
191static bool writeAppendNumber(SkCanvas* canvas, const SkString* path, int number) {
192 if (NULL == path) {
193 return true;
194 }
195 SkString pathWithNumber(*path);
196 pathWithNumber.appendf("%i", number);
197 return write(canvas, pathWithNumber);
198}
199
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000200///////////////////////////////////////////////////////////////////////////////////////////////
201
djsollen@google.comfd9720c2012-11-06 16:54:40 +0000202SkCanvas* RecordPictureRenderer::setupCanvas(int width, int height) {
203 // defer the canvas setup until the render step
204 return NULL;
205}
206
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000207bool RecordPictureRenderer::render(const SkString*) {
junov@chromium.org9313ca42012-11-02 18:11:49 +0000208 SkAutoTUnref<SkPicture> replayer(this->createPicture());
209 SkCanvas* recorder = replayer->beginRecording(fPicture->width(), fPicture->height(),
210 this->recordFlags());
scroggo@google.com9a412522012-09-07 15:21:18 +0000211 fPicture->draw(recorder);
junov@chromium.org9313ca42012-11-02 18:11:49 +0000212 replayer->endRecording();
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000213 // Since this class does not actually render, return false.
214 return false;
scroggo@google.com9a412522012-09-07 15:21:18 +0000215}
216
scroggo@google.com0a049b82012-11-02 22:01:26 +0000217SkString RecordPictureRenderer::getConfigNameInternal() {
218 return SkString("record");
219}
220
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000221///////////////////////////////////////////////////////////////////////////////////////////////
222
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000223bool PipePictureRenderer::render(const SkString* path) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000224 SkASSERT(fCanvas.get() != NULL);
225 SkASSERT(fPicture != NULL);
keyar@chromium.org78a35c52012-08-20 15:03:44 +0000226 if (NULL == fCanvas.get() || NULL == fPicture) {
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000227 return false;
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000228 }
229
230 PipeController pipeController(fCanvas.get());
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000231 SkGPipeWriter writer;
232 SkCanvas* pipeCanvas = writer.startRecording(&pipeController);
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000233 pipeCanvas->drawPicture(*fPicture);
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000234 writer.endRecording();
scroggo@google.com9a412522012-09-07 15:21:18 +0000235 fCanvas->flush();
borenet@google.com070d3542012-10-26 13:26:55 +0000236 if (NULL != path) {
237 return write(fCanvas, *path);
238 }
239 return true;
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000240}
241
scroggo@google.com0a049b82012-11-02 22:01:26 +0000242SkString PipePictureRenderer::getConfigNameInternal() {
243 return SkString("pipe");
244}
245
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000246///////////////////////////////////////////////////////////////////////////////////////////////
247
junov@chromium.org9313ca42012-11-02 18:11:49 +0000248void SimplePictureRenderer::init(SkPicture* picture) {
249 INHERITED::init(picture);
250 this->buildBBoxHierarchy();
251}
252
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000253bool SimplePictureRenderer::render(const SkString* path) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000254 SkASSERT(fCanvas.get() != NULL);
255 SkASSERT(fPicture != NULL);
keyar@chromium.org78a35c52012-08-20 15:03:44 +0000256 if (NULL == fCanvas.get() || NULL == fPicture) {
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000257 return false;
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000258 }
259
260 fCanvas->drawPicture(*fPicture);
scroggo@google.com9a412522012-09-07 15:21:18 +0000261 fCanvas->flush();
borenet@google.com070d3542012-10-26 13:26:55 +0000262 if (NULL != path) {
263 return write(fCanvas, *path);
264 }
265 return true;
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000266}
267
scroggo@google.com0a049b82012-11-02 22:01:26 +0000268SkString SimplePictureRenderer::getConfigNameInternal() {
269 return SkString("simple");
270}
271
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000272///////////////////////////////////////////////////////////////////////////////////////////////
273
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000274TiledPictureRenderer::TiledPictureRenderer()
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000275 : fTileWidth(kDefaultTileWidth)
rileya@google.comb947b912012-08-29 17:35:07 +0000276 , fTileHeight(kDefaultTileHeight)
rileya@google.coma04dc022012-09-10 19:01:38 +0000277 , fTileWidthPercentage(0.0)
rileya@google.comb947b912012-08-29 17:35:07 +0000278 , fTileHeightPercentage(0.0)
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000279 , fTileMinPowerOf2Width(0) { }
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000280
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000281void TiledPictureRenderer::init(SkPicture* pict) {
282 SkASSERT(pict != NULL);
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000283 SkASSERT(0 == fTileRects.count());
284 if (NULL == pict || fTileRects.count() != 0) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000285 return;
286 }
287
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000288 // Do not call INHERITED::init(), which would create a (potentially large) canvas which is not
289 // used by bench_pictures.
290 fPicture = pict;
junov@chromium.org9313ca42012-11-02 18:11:49 +0000291 fPicture->ref();
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000292 this->buildBBoxHierarchy();
keyar@chromium.orgcc6e5ef2012-07-27 20:09:26 +0000293
294 if (fTileWidthPercentage > 0) {
robertphillips@google.com5d8d1862012-08-15 14:36:41 +0000295 fTileWidth = sk_float_ceil2int(float(fTileWidthPercentage * fPicture->width() / 100));
keyar@chromium.orgcc6e5ef2012-07-27 20:09:26 +0000296 }
297 if (fTileHeightPercentage > 0) {
robertphillips@google.com5d8d1862012-08-15 14:36:41 +0000298 fTileHeight = sk_float_ceil2int(float(fTileHeightPercentage * fPicture->height() / 100));
keyar@chromium.orgcc6e5ef2012-07-27 20:09:26 +0000299 }
300
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000301 if (fTileMinPowerOf2Width > 0) {
302 this->setupPowerOf2Tiles();
303 } else {
304 this->setupTiles();
305 }
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000306}
307
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000308void TiledPictureRenderer::end() {
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000309 fTileRects.reset();
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000310 this->INHERITED::end();
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000311}
312
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000313void TiledPictureRenderer::setupTiles() {
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000314 for (int tile_y_start = 0; tile_y_start < fPicture->height(); tile_y_start += fTileHeight) {
315 for (int tile_x_start = 0; tile_x_start < fPicture->width(); tile_x_start += fTileWidth) {
316 *fTileRects.append() = SkRect::MakeXYWH(SkIntToScalar(tile_x_start),
317 SkIntToScalar(tile_y_start),
318 SkIntToScalar(fTileWidth),
319 SkIntToScalar(fTileHeight));
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000320 }
321 }
322}
323
324// The goal of the powers of two tiles is to minimize the amount of wasted tile
325// space in the width-wise direction and then minimize the number of tiles. The
326// constraints are that every tile must have a pixel width that is a power of
327// two and also be of some minimal width (that is also a power of two).
328//
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000329// This is solved by first taking our picture size and rounding it up to the
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000330// multiple of the minimal width. The binary representation of this rounded
331// value gives us the tiles we need: a bit of value one means we need a tile of
332// that size.
333void TiledPictureRenderer::setupPowerOf2Tiles() {
334 int rounded_value = fPicture->width();
335 if (fPicture->width() % fTileMinPowerOf2Width != 0) {
336 rounded_value = fPicture->width() - (fPicture->width() % fTileMinPowerOf2Width)
337 + fTileMinPowerOf2Width;
338 }
339
robertphillips@google.com94acc702012-09-06 18:43:21 +0000340 int num_bits = SkScalarCeilToInt(SkScalarLog2(SkIntToScalar(fPicture->width())));
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000341 int largest_possible_tile_size = 1 << num_bits;
342
343 // The tile height is constant for a particular picture.
344 for (int tile_y_start = 0; tile_y_start < fPicture->height(); tile_y_start += fTileHeight) {
345 int tile_x_start = 0;
346 int current_width = largest_possible_tile_size;
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000347 // Set fTileWidth to be the width of the widest tile, so that each canvas is large enough
348 // to draw each tile.
349 fTileWidth = current_width;
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000350
351 while (current_width >= fTileMinPowerOf2Width) {
352 // It is very important this is a bitwise AND.
353 if (current_width & rounded_value) {
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000354 *fTileRects.append() = SkRect::MakeXYWH(SkIntToScalar(tile_x_start),
355 SkIntToScalar(tile_y_start),
356 SkIntToScalar(current_width),
357 SkIntToScalar(fTileHeight));
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000358 tile_x_start += current_width;
359 }
360
361 current_width >>= 1;
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000362 }
363 }
364}
365
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000366/**
367 * Draw the specified playback to the canvas translated to rectangle provided, so that this mini
368 * canvas represents the rectangle's portion of the overall picture.
369 * Saves and restores so that the initial clip and matrix return to their state before this function
370 * is called.
371 */
372template<class T>
373static void DrawTileToCanvas(SkCanvas* canvas, const SkRect& tileRect, T* playback) {
374 int saveCount = canvas->save();
375 // Translate so that we draw the correct portion of the picture
376 canvas->translate(-tileRect.fLeft, -tileRect.fTop);
377 playback->draw(canvas);
378 canvas->restoreToCount(saveCount);
379 canvas->flush();
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000380}
381
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000382///////////////////////////////////////////////////////////////////////////////////////////////
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000383
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000384bool TiledPictureRenderer::render(const SkString* path) {
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000385 SkASSERT(fPicture != NULL);
386 if (NULL == fPicture) {
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000387 return false;
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000388 }
389
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000390 // Reuse one canvas for all tiles.
391 SkCanvas* canvas = this->setupCanvas(fTileWidth, fTileHeight);
392 SkAutoUnref aur(canvas);
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000393
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000394 bool success = true;
395 for (int i = 0; i < fTileRects.count(); ++i) {
396 DrawTileToCanvas(canvas, fTileRects[i], fPicture);
397 if (NULL != path) {
398 success &= writeAppendNumber(canvas, path, i);
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000399 }
keyar@chromium.org163b5672012-08-01 17:53:29 +0000400 }
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000401 return success;
keyar@chromium.org163b5672012-08-01 17:53:29 +0000402}
403
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000404SkCanvas* TiledPictureRenderer::setupCanvas(int width, int height) {
405 SkCanvas* canvas = this->INHERITED::setupCanvas(width, height);
406 SkASSERT(fPicture != NULL);
407 // Clip the tile to an area that is completely in what the SkPicture says is the
408 // drawn-to area. This is mostly important for tiles on the right and bottom edges
409 // as they may go over this area and the picture may have some commands that
410 // draw outside of this area and so should not actually be written.
411 SkRect clip = SkRect::MakeWH(SkIntToScalar(fPicture->width()),
412 SkIntToScalar(fPicture->height()));
413 canvas->clipRect(clip);
414 return canvas;
415}
scroggo@google.com0a049b82012-11-02 22:01:26 +0000416
417SkString TiledPictureRenderer::getConfigNameInternal() {
418 SkString name;
419 if (fTileMinPowerOf2Width > 0) {
420 name.append("pow2tile_");
421 name.appendf("%i", fTileMinPowerOf2Width);
422 } else {
423 name.append("tile_");
424 if (fTileWidthPercentage > 0) {
425 name.appendf("%.f%%", fTileWidthPercentage);
426 } else {
427 name.appendf("%i", fTileWidth);
428 }
429 }
430 name.append("x");
431 if (fTileHeightPercentage > 0) {
432 name.appendf("%.f%%", fTileHeightPercentage);
433 } else {
434 name.appendf("%i", fTileHeight);
435 }
436 return name;
437}
438
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000439///////////////////////////////////////////////////////////////////////////////////////////////
440
441// Holds all of the information needed to draw a set of tiles.
442class CloneData : public SkRunnable {
443
444public:
445 CloneData(SkPicture* clone, SkCanvas* canvas, SkTDArray<SkRect>& rects, int start, int end,
446 SkRunnable* done)
447 : fClone(clone)
448 , fCanvas(canvas)
449 , fPath(NULL)
450 , fRects(rects)
451 , fStart(start)
452 , fEnd(end)
453 , fSuccess(NULL)
454 , fDone(done) {
455 SkASSERT(fDone != NULL);
456 }
457
458 virtual void run() SK_OVERRIDE {
459 SkGraphics::SetTLSFontCacheLimit(1024 * 1024);
460 for (int i = fStart; i < fEnd; i++) {
461 DrawTileToCanvas(fCanvas, fRects[i], fClone);
462 if (fPath != NULL && !writeAppendNumber(fCanvas, fPath, i)
463 && fSuccess != NULL) {
464 *fSuccess = false;
465 // If one tile fails to write to a file, do not continue drawing the rest.
466 break;
467 }
468 }
469 fDone->run();
470 }
471
472 void setPathAndSuccess(const SkString* path, bool* success) {
473 fPath = path;
474 fSuccess = success;
475 }
476
477private:
478 // All pointers unowned.
479 SkPicture* fClone; // Picture to draw from. Each CloneData has a unique one which
480 // is threadsafe.
481 SkCanvas* fCanvas; // Canvas to draw to. Reused for each tile.
482 const SkString* fPath; // If non-null, path to write the result to as a PNG.
483 SkTDArray<SkRect>& fRects; // All tiles of the picture.
484 const int fStart; // Range of tiles drawn by this thread.
485 const int fEnd;
486 bool* fSuccess; // Only meaningful if path is non-null. Shared by all threads,
487 // and only set to false upon failure to write to a PNG.
488 SkRunnable* fDone;
489};
490
491MultiCorePictureRenderer::MultiCorePictureRenderer(int threadCount)
492: fNumThreads(threadCount)
493, fThreadPool(threadCount)
494, fCountdown(threadCount) {
495 // Only need to create fNumThreads - 1 clones, since one thread will use the base
496 // picture.
497 fPictureClones = SkNEW_ARRAY(SkPicture, fNumThreads - 1);
498 fCloneData = SkNEW_ARRAY(CloneData*, fNumThreads);
499}
500
501void MultiCorePictureRenderer::init(SkPicture *pict) {
502 // Set fPicture and the tiles.
503 this->INHERITED::init(pict);
504 for (int i = 0; i < fNumThreads; ++i) {
505 *fCanvasPool.append() = this->setupCanvas(this->getTileWidth(), this->getTileHeight());
506 }
507 // Only need to create fNumThreads - 1 clones, since one thread will use the base picture.
508 fPicture->clone(fPictureClones, fNumThreads - 1);
509 // Populate each thread with the appropriate data.
510 // Group the tiles into nearly equal size chunks, rounding up so we're sure to cover them all.
511 const int chunkSize = (fTileRects.count() + fNumThreads - 1) / fNumThreads;
512
513 for (int i = 0; i < fNumThreads; i++) {
514 SkPicture* pic;
515 if (i == fNumThreads-1) {
516 // The last set will use the original SkPicture.
517 pic = fPicture;
518 } else {
519 pic = &fPictureClones[i];
520 }
521 const int start = i * chunkSize;
522 const int end = SkMin32(start + chunkSize, fTileRects.count());
523 fCloneData[i] = SkNEW_ARGS(CloneData,
524 (pic, fCanvasPool[i], fTileRects, start, end, &fCountdown));
525 }
526}
527
528bool MultiCorePictureRenderer::render(const SkString *path) {
529 bool success = true;
530 if (path != NULL) {
531 for (int i = 0; i < fNumThreads-1; i++) {
532 fCloneData[i]->setPathAndSuccess(path, &success);
533 }
534 }
535
536 fCountdown.reset(fNumThreads);
537 for (int i = 0; i < fNumThreads; i++) {
538 fThreadPool.add(fCloneData[i]);
539 }
540 fCountdown.wait();
541
542 return success;
543}
544
545void MultiCorePictureRenderer::end() {
546 for (int i = 0; i < fNumThreads - 1; i++) {
547 SkDELETE(fCloneData[i]);
548 fCloneData[i] = NULL;
549 }
550
551 fCanvasPool.unrefAll();
552
553 this->INHERITED::end();
554}
555
556MultiCorePictureRenderer::~MultiCorePictureRenderer() {
557 // Each individual CloneData was deleted in end.
558 SkDELETE_ARRAY(fCloneData);
559 SkDELETE_ARRAY(fPictureClones);
560}
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000561
scroggo@google.com0a049b82012-11-02 22:01:26 +0000562SkString MultiCorePictureRenderer::getConfigNameInternal() {
563 SkString name = this->INHERITED::getConfigNameInternal();
564 name.appendf("_multi_%i_threads", fNumThreads);
565 return name;
566}
567
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000568///////////////////////////////////////////////////////////////////////////////////////////////
scroggo@google.com9a412522012-09-07 15:21:18 +0000569
570void PlaybackCreationRenderer::setup() {
junov@chromium.org9313ca42012-11-02 18:11:49 +0000571 fReplayer.reset(this->createPicture());
572 SkCanvas* recorder = fReplayer->beginRecording(fPicture->width(), fPicture->height(),
573 this->recordFlags());
scroggo@google.com9a412522012-09-07 15:21:18 +0000574 fPicture->draw(recorder);
575}
576
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000577bool PlaybackCreationRenderer::render(const SkString*) {
junov@chromium.org9313ca42012-11-02 18:11:49 +0000578 fReplayer->endRecording();
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000579 // Since this class does not actually render, return false.
580 return false;
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000581}
582
scroggo@google.com0a049b82012-11-02 22:01:26 +0000583SkString PlaybackCreationRenderer::getConfigNameInternal() {
584 return SkString("playback_creation");
585}
586
junov@chromium.org9313ca42012-11-02 18:11:49 +0000587///////////////////////////////////////////////////////////////////////////////////////////////
588// SkPicture variants for each BBoxHierarchy type
589
590class RTreePicture : public SkPicture {
591public:
592 virtual SkBBoxHierarchy* createBBoxHierarchy() const SK_OVERRIDE{
593 static const int kRTreeMinChildren = 6;
594 static const int kRTreeMaxChildren = 11;
595 SkScalar aspectRatio = SkScalarDiv(SkIntToScalar(fWidth),
596 SkIntToScalar(fHeight));
597 return SkRTree::Create(kRTreeMinChildren, kRTreeMaxChildren,
598 aspectRatio);
599 }
600};
601
junov@chromium.org7b537062012-11-06 18:58:43 +0000602class TileGridPicture : public SkPicture {
603public:
604 TileGridPicture(int tileWidth, int tileHeight, int xTileCount, int yTileCount) {
605 fTileWidth = tileWidth;
606 fTileHeight = tileHeight;
607 fXTileCount = xTileCount;
608 fYTileCount = yTileCount;
609 }
610
611 virtual SkBBoxHierarchy* createBBoxHierarchy() const SK_OVERRIDE{
612 return SkNEW_ARGS(SkTileGrid, (fTileWidth, fTileHeight, fXTileCount, fYTileCount));
613 }
614private:
615 int fTileWidth, fTileHeight, fXTileCount, fYTileCount;
616};
617
junov@chromium.org9313ca42012-11-02 18:11:49 +0000618SkPicture* PictureRenderer::createPicture() {
619 switch (fBBoxHierarchyType) {
620 case kNone_BBoxHierarchyType:
621 return SkNEW(SkPicture);
622 case kRTree_BBoxHierarchyType:
623 return SkNEW(RTreePicture);
junov@chromium.org7b537062012-11-06 18:58:43 +0000624 case kTileGrid_BBoxHierarchyType:
625 {
626 int xTileCount = fPicture->width() / fGridWidth +
627 ((fPicture->width() % fGridWidth) ? 1 : 0);
628 int yTileCount = fPicture->height() / fGridHeight +
629 ((fPicture->height() % fGridHeight) ? 1 : 0);
630 return SkNEW_ARGS(TileGridPicture, (fGridWidth, fGridHeight, xTileCount,
631 yTileCount));
632 }
junov@chromium.org9313ca42012-11-02 18:11:49 +0000633 }
634 SkASSERT(0); // invalid bbhType
635 return NULL;
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000636}
junov@chromium.org9313ca42012-11-02 18:11:49 +0000637
638} // namespace sk_tools