blob: 84b67d84f9d6d5f8ad3d8837242483f444fdaf99 [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);
caryclark@google.come3e940c2012-11-07 16:42:17 +000062 if ((PictureRenderer::kBlur_DrawFilterFlag | PictureRenderer::kLowBlur_DrawFilterFlag)
63 & fFlags[t]) {
caryclark@google.coma3622372012-11-06 21:26:13 +000064 SkMaskFilter* maskFilter = paint->getMaskFilter();
65 SkMaskFilter::BlurInfo blurInfo;
66 if (maskFilter && maskFilter->asABlur(&blurInfo)) {
caryclark@google.come3e940c2012-11-07 16:42:17 +000067 if (PictureRenderer::kBlur_DrawFilterFlag & fFlags[t]) {
68 paint->setMaskFilter(NULL);
69 } else {
70 blurInfo.fHighQuality = false;
71 maskFilter->setAsABlur(blurInfo);
72 }
caryclark@google.coma3622372012-11-06 21:26:13 +000073 }
74 }
75 if (PictureRenderer::kHinting_DrawFilterFlag & fFlags[t]) {
76 paint->setHinting(SkPaint::kNo_Hinting);
77 } else if (PictureRenderer::kSlightHinting_DrawFilterFlag & fFlags[t]) {
78 paint->setHinting(SkPaint::kSlight_Hinting);
79 }
80 }
81
82private:
83 PictureRenderer::DrawFilterFlags* fFlags;
84};
85
86static SkCanvas* setUpFilter(SkCanvas* canvas, PictureRenderer::DrawFilterFlags* drawFilters) {
87 if (drawFilters && !canvas->getDrawFilter()) {
88 canvas->setDrawFilter(SkNEW_ARGS(FlagsDrawFilter, (drawFilters)))->unref();
caryclark@google.come3e940c2012-11-07 16:42:17 +000089 if (drawFilters[0] & PictureRenderer::kAAClip_DrawFilterFlag) {
90 canvas->setAllowSoftClip(false);
91 }
caryclark@google.coma3622372012-11-06 21:26:13 +000092 }
93 return canvas;
94}
95
keyar@chromium.orga474ce32012-08-20 15:03:57 +000096SkCanvas* PictureRenderer::setupCanvas() {
97 return this->setupCanvas(fPicture->width(), fPicture->height());
98}
99
100SkCanvas* PictureRenderer::setupCanvas(int width, int height) {
caryclark@google.coma3622372012-11-06 21:26:13 +0000101 SkCanvas* canvas;
keyar@chromium.org4ea96c52012-08-20 15:03:29 +0000102 switch(fDeviceType) {
103 case kBitmap_DeviceType: {
104 SkBitmap bitmap;
keyar@chromium.orga474ce32012-08-20 15:03:57 +0000105 sk_tools::setup_bitmap(&bitmap, width, height);
caryclark@google.coma3622372012-11-06 21:26:13 +0000106 canvas = SkNEW_ARGS(SkCanvas, (bitmap));
107 return setUpFilter(canvas, fDrawFilters);
keyar@chromium.org4ea96c52012-08-20 15:03:29 +0000108 }
109#if SK_SUPPORT_GPU
110 case kGPU_DeviceType: {
keyar@chromium.org4ea96c52012-08-20 15:03:29 +0000111 SkAutoTUnref<SkGpuDevice> device(SkNEW_ARGS(SkGpuDevice,
keyar@chromium.org06125642012-08-20 15:03:33 +0000112 (fGrContext, SkBitmap::kARGB_8888_Config,
keyar@chromium.orga474ce32012-08-20 15:03:57 +0000113 width, height)));
caryclark@google.coma3622372012-11-06 21:26:13 +0000114 canvas = SkNEW_ARGS(SkCanvas, (device.get()));
115 return setUpFilter(canvas, fDrawFilters);
keyar@chromium.org4ea96c52012-08-20 15:03:29 +0000116 }
117#endif
118 default:
119 SkASSERT(0);
120 }
keyar@chromium.orga474ce32012-08-20 15:03:57 +0000121
122 return NULL;
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000123}
124
125void PictureRenderer::end() {
keyar@chromium.org77a55222012-08-20 15:03:47 +0000126 this->resetState();
junov@chromium.org9313ca42012-11-02 18:11:49 +0000127 SkSafeUnref(fPicture);
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000128 fPicture = NULL;
129 fCanvas.reset(NULL);
130}
131
junov@chromium.org9313ca42012-11-02 18:11:49 +0000132/** Converts fPicture to a picture that uses a BBoxHierarchy.
133 * PictureRenderer subclasses that are used to test picture playback
134 * should call this method during init.
135 */
136void PictureRenderer::buildBBoxHierarchy() {
137 SkASSERT(NULL != fPicture);
138 if (kNone_BBoxHierarchyType != fBBoxHierarchyType && NULL != fPicture) {
139 SkPicture* newPicture = this->createPicture();
140 SkCanvas* recorder = newPicture->beginRecording(fPicture->width(), fPicture->height(),
141 this->recordFlags());
142 fPicture->draw(recorder);
143 newPicture->endRecording();
144 fPicture->unref();
145 fPicture = newPicture;
146 }
147}
148
keyar@chromium.org77a55222012-08-20 15:03:47 +0000149void PictureRenderer::resetState() {
keyar@chromium.org28136b32012-08-20 15:04:15 +0000150#if SK_SUPPORT_GPU
151 if (this->isUsingGpuDevice()) {
152 SkGLContext* glContext = fGrContextFactory.getGLContext(
153 GrContextFactory::kNative_GLContextType);
keyar@chromium.org28136b32012-08-20 15:04:15 +0000154
155 SkASSERT(glContext != NULL);
156 if (NULL == glContext) {
157 return;
158 }
159
scroggo@google.com9a412522012-09-07 15:21:18 +0000160 fGrContext->flush();
keyar@chromium.org77a55222012-08-20 15:03:47 +0000161 SK_GL(*glContext, Finish());
keyar@chromium.org77a55222012-08-20 15:03:47 +0000162 }
keyar@chromium.orga40c20d2012-08-20 15:04:12 +0000163#endif
keyar@chromium.org77a55222012-08-20 15:03:47 +0000164}
165
junov@chromium.org9313ca42012-11-02 18:11:49 +0000166uint32_t PictureRenderer::recordFlags() {
167 return kNone_BBoxHierarchyType == fBBoxHierarchyType ? 0 :
168 SkPicture::kOptimizeForClippedPlayback_RecordingFlag;
169}
170
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000171/**
172 * Write the canvas to the specified path.
173 * @param canvas Must be non-null. Canvas to be written to a file.
174 * @param path Path for the file to be written. Should have no extension; write() will append
175 * an appropriate one. Passed in by value so it can be modified.
176 * @return bool True if the Canvas is written to a file.
177 */
178static bool write(SkCanvas* canvas, SkString path) {
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000179 SkASSERT(canvas != NULL);
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000180 if (NULL == canvas) {
keyar@chromium.org9299ede2012-08-21 19:05:08 +0000181 return false;
182 }
183
184 SkBitmap bitmap;
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000185 SkISize size = canvas->getDeviceSize();
186 sk_tools::setup_bitmap(&bitmap, size.width(), size.height());
keyar@chromium.org9299ede2012-08-21 19:05:08 +0000187
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000188 canvas->readPixels(&bitmap, 0, 0);
keyar@chromium.org9299ede2012-08-21 19:05:08 +0000189 sk_tools::force_all_opaque(bitmap);
190
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000191 // Since path is passed in by value, it is okay to modify it.
192 path.append(".png");
keyar@chromium.org9299ede2012-08-21 19:05:08 +0000193 return SkImageEncoder::EncodeFile(path.c_str(), bitmap, SkImageEncoder::kPNG_Type, 100);
194}
195
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000196/**
197 * If path is non NULL, append number to it, and call write(SkCanvas*, SkString) to write the
198 * provided canvas to a file. Returns true if path is NULL or if write() succeeds.
199 */
200static bool writeAppendNumber(SkCanvas* canvas, const SkString* path, int number) {
201 if (NULL == path) {
202 return true;
203 }
204 SkString pathWithNumber(*path);
205 pathWithNumber.appendf("%i", number);
206 return write(canvas, pathWithNumber);
207}
208
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000209///////////////////////////////////////////////////////////////////////////////////////////////
210
djsollen@google.comfd9720c2012-11-06 16:54:40 +0000211SkCanvas* RecordPictureRenderer::setupCanvas(int width, int height) {
212 // defer the canvas setup until the render step
213 return NULL;
214}
215
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000216bool RecordPictureRenderer::render(const SkString*) {
junov@chromium.org9313ca42012-11-02 18:11:49 +0000217 SkAutoTUnref<SkPicture> replayer(this->createPicture());
218 SkCanvas* recorder = replayer->beginRecording(fPicture->width(), fPicture->height(),
219 this->recordFlags());
scroggo@google.com9a412522012-09-07 15:21:18 +0000220 fPicture->draw(recorder);
junov@chromium.org9313ca42012-11-02 18:11:49 +0000221 replayer->endRecording();
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000222 // Since this class does not actually render, return false.
223 return false;
scroggo@google.com9a412522012-09-07 15:21:18 +0000224}
225
scroggo@google.com0a049b82012-11-02 22:01:26 +0000226SkString RecordPictureRenderer::getConfigNameInternal() {
227 return SkString("record");
228}
229
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000230///////////////////////////////////////////////////////////////////////////////////////////////
231
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000232bool PipePictureRenderer::render(const SkString* path) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000233 SkASSERT(fCanvas.get() != NULL);
234 SkASSERT(fPicture != NULL);
keyar@chromium.org78a35c52012-08-20 15:03:44 +0000235 if (NULL == fCanvas.get() || NULL == fPicture) {
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000236 return false;
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000237 }
238
239 PipeController pipeController(fCanvas.get());
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000240 SkGPipeWriter writer;
241 SkCanvas* pipeCanvas = writer.startRecording(&pipeController);
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000242 pipeCanvas->drawPicture(*fPicture);
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000243 writer.endRecording();
scroggo@google.com9a412522012-09-07 15:21:18 +0000244 fCanvas->flush();
borenet@google.com070d3542012-10-26 13:26:55 +0000245 if (NULL != path) {
246 return write(fCanvas, *path);
247 }
248 return true;
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000249}
250
scroggo@google.com0a049b82012-11-02 22:01:26 +0000251SkString PipePictureRenderer::getConfigNameInternal() {
252 return SkString("pipe");
253}
254
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000255///////////////////////////////////////////////////////////////////////////////////////////////
256
junov@chromium.org9313ca42012-11-02 18:11:49 +0000257void SimplePictureRenderer::init(SkPicture* picture) {
258 INHERITED::init(picture);
259 this->buildBBoxHierarchy();
260}
261
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000262bool SimplePictureRenderer::render(const SkString* path) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000263 SkASSERT(fCanvas.get() != NULL);
264 SkASSERT(fPicture != NULL);
keyar@chromium.org78a35c52012-08-20 15:03:44 +0000265 if (NULL == fCanvas.get() || NULL == fPicture) {
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000266 return false;
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000267 }
268
269 fCanvas->drawPicture(*fPicture);
scroggo@google.com9a412522012-09-07 15:21:18 +0000270 fCanvas->flush();
borenet@google.com070d3542012-10-26 13:26:55 +0000271 if (NULL != path) {
272 return write(fCanvas, *path);
273 }
274 return true;
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000275}
276
scroggo@google.com0a049b82012-11-02 22:01:26 +0000277SkString SimplePictureRenderer::getConfigNameInternal() {
278 return SkString("simple");
279}
280
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000281///////////////////////////////////////////////////////////////////////////////////////////////
282
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000283TiledPictureRenderer::TiledPictureRenderer()
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000284 : fTileWidth(kDefaultTileWidth)
rileya@google.comb947b912012-08-29 17:35:07 +0000285 , fTileHeight(kDefaultTileHeight)
rileya@google.coma04dc022012-09-10 19:01:38 +0000286 , fTileWidthPercentage(0.0)
rileya@google.comb947b912012-08-29 17:35:07 +0000287 , fTileHeightPercentage(0.0)
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000288 , fTileMinPowerOf2Width(0) { }
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000289
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000290void TiledPictureRenderer::init(SkPicture* pict) {
291 SkASSERT(pict != NULL);
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000292 SkASSERT(0 == fTileRects.count());
293 if (NULL == pict || fTileRects.count() != 0) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000294 return;
295 }
296
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000297 // Do not call INHERITED::init(), which would create a (potentially large) canvas which is not
298 // used by bench_pictures.
299 fPicture = pict;
junov@chromium.org9313ca42012-11-02 18:11:49 +0000300 fPicture->ref();
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000301 this->buildBBoxHierarchy();
keyar@chromium.orgcc6e5ef2012-07-27 20:09:26 +0000302
303 if (fTileWidthPercentage > 0) {
robertphillips@google.com5d8d1862012-08-15 14:36:41 +0000304 fTileWidth = sk_float_ceil2int(float(fTileWidthPercentage * fPicture->width() / 100));
keyar@chromium.orgcc6e5ef2012-07-27 20:09:26 +0000305 }
306 if (fTileHeightPercentage > 0) {
robertphillips@google.com5d8d1862012-08-15 14:36:41 +0000307 fTileHeight = sk_float_ceil2int(float(fTileHeightPercentage * fPicture->height() / 100));
keyar@chromium.orgcc6e5ef2012-07-27 20:09:26 +0000308 }
309
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000310 if (fTileMinPowerOf2Width > 0) {
311 this->setupPowerOf2Tiles();
312 } else {
313 this->setupTiles();
314 }
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000315}
316
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000317void TiledPictureRenderer::end() {
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000318 fTileRects.reset();
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000319 this->INHERITED::end();
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000320}
321
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000322void TiledPictureRenderer::setupTiles() {
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000323 for (int tile_y_start = 0; tile_y_start < fPicture->height(); tile_y_start += fTileHeight) {
324 for (int tile_x_start = 0; tile_x_start < fPicture->width(); tile_x_start += fTileWidth) {
325 *fTileRects.append() = SkRect::MakeXYWH(SkIntToScalar(tile_x_start),
326 SkIntToScalar(tile_y_start),
327 SkIntToScalar(fTileWidth),
328 SkIntToScalar(fTileHeight));
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000329 }
330 }
331}
332
333// The goal of the powers of two tiles is to minimize the amount of wasted tile
334// space in the width-wise direction and then minimize the number of tiles. The
335// constraints are that every tile must have a pixel width that is a power of
336// two and also be of some minimal width (that is also a power of two).
337//
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000338// This is solved by first taking our picture size and rounding it up to the
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000339// multiple of the minimal width. The binary representation of this rounded
340// value gives us the tiles we need: a bit of value one means we need a tile of
341// that size.
342void TiledPictureRenderer::setupPowerOf2Tiles() {
343 int rounded_value = fPicture->width();
344 if (fPicture->width() % fTileMinPowerOf2Width != 0) {
345 rounded_value = fPicture->width() - (fPicture->width() % fTileMinPowerOf2Width)
346 + fTileMinPowerOf2Width;
347 }
348
robertphillips@google.com94acc702012-09-06 18:43:21 +0000349 int num_bits = SkScalarCeilToInt(SkScalarLog2(SkIntToScalar(fPicture->width())));
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000350 int largest_possible_tile_size = 1 << num_bits;
351
352 // The tile height is constant for a particular picture.
353 for (int tile_y_start = 0; tile_y_start < fPicture->height(); tile_y_start += fTileHeight) {
354 int tile_x_start = 0;
355 int current_width = largest_possible_tile_size;
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000356 // Set fTileWidth to be the width of the widest tile, so that each canvas is large enough
357 // to draw each tile.
358 fTileWidth = current_width;
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000359
360 while (current_width >= fTileMinPowerOf2Width) {
361 // It is very important this is a bitwise AND.
362 if (current_width & rounded_value) {
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000363 *fTileRects.append() = SkRect::MakeXYWH(SkIntToScalar(tile_x_start),
364 SkIntToScalar(tile_y_start),
365 SkIntToScalar(current_width),
366 SkIntToScalar(fTileHeight));
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000367 tile_x_start += current_width;
368 }
369
370 current_width >>= 1;
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000371 }
372 }
373}
374
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000375/**
376 * Draw the specified playback to the canvas translated to rectangle provided, so that this mini
377 * canvas represents the rectangle's portion of the overall picture.
378 * Saves and restores so that the initial clip and matrix return to their state before this function
379 * is called.
380 */
381template<class T>
382static void DrawTileToCanvas(SkCanvas* canvas, const SkRect& tileRect, T* playback) {
383 int saveCount = canvas->save();
384 // Translate so that we draw the correct portion of the picture
385 canvas->translate(-tileRect.fLeft, -tileRect.fTop);
386 playback->draw(canvas);
387 canvas->restoreToCount(saveCount);
388 canvas->flush();
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000389}
390
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000391///////////////////////////////////////////////////////////////////////////////////////////////
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000392
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000393bool TiledPictureRenderer::render(const SkString* path) {
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000394 SkASSERT(fPicture != NULL);
395 if (NULL == fPicture) {
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000396 return false;
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000397 }
398
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000399 // Reuse one canvas for all tiles.
400 SkCanvas* canvas = this->setupCanvas(fTileWidth, fTileHeight);
401 SkAutoUnref aur(canvas);
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000402
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000403 bool success = true;
404 for (int i = 0; i < fTileRects.count(); ++i) {
405 DrawTileToCanvas(canvas, fTileRects[i], fPicture);
406 if (NULL != path) {
407 success &= writeAppendNumber(canvas, path, i);
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000408 }
keyar@chromium.org163b5672012-08-01 17:53:29 +0000409 }
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000410 return success;
keyar@chromium.org163b5672012-08-01 17:53:29 +0000411}
412
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000413SkCanvas* TiledPictureRenderer::setupCanvas(int width, int height) {
414 SkCanvas* canvas = this->INHERITED::setupCanvas(width, height);
415 SkASSERT(fPicture != NULL);
416 // Clip the tile to an area that is completely in what the SkPicture says is the
417 // drawn-to area. This is mostly important for tiles on the right and bottom edges
418 // as they may go over this area and the picture may have some commands that
419 // draw outside of this area and so should not actually be written.
420 SkRect clip = SkRect::MakeWH(SkIntToScalar(fPicture->width()),
421 SkIntToScalar(fPicture->height()));
422 canvas->clipRect(clip);
423 return canvas;
424}
scroggo@google.com0a049b82012-11-02 22:01:26 +0000425
426SkString TiledPictureRenderer::getConfigNameInternal() {
427 SkString name;
428 if (fTileMinPowerOf2Width > 0) {
429 name.append("pow2tile_");
430 name.appendf("%i", fTileMinPowerOf2Width);
431 } else {
432 name.append("tile_");
433 if (fTileWidthPercentage > 0) {
434 name.appendf("%.f%%", fTileWidthPercentage);
435 } else {
436 name.appendf("%i", fTileWidth);
437 }
438 }
439 name.append("x");
440 if (fTileHeightPercentage > 0) {
441 name.appendf("%.f%%", fTileHeightPercentage);
442 } else {
443 name.appendf("%i", fTileHeight);
444 }
445 return name;
446}
447
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000448///////////////////////////////////////////////////////////////////////////////////////////////
449
450// Holds all of the information needed to draw a set of tiles.
451class CloneData : public SkRunnable {
452
453public:
454 CloneData(SkPicture* clone, SkCanvas* canvas, SkTDArray<SkRect>& rects, int start, int end,
455 SkRunnable* done)
456 : fClone(clone)
457 , fCanvas(canvas)
458 , fPath(NULL)
459 , fRects(rects)
460 , fStart(start)
461 , fEnd(end)
462 , fSuccess(NULL)
463 , fDone(done) {
464 SkASSERT(fDone != NULL);
465 }
466
467 virtual void run() SK_OVERRIDE {
468 SkGraphics::SetTLSFontCacheLimit(1024 * 1024);
469 for (int i = fStart; i < fEnd; i++) {
470 DrawTileToCanvas(fCanvas, fRects[i], fClone);
471 if (fPath != NULL && !writeAppendNumber(fCanvas, fPath, i)
472 && fSuccess != NULL) {
473 *fSuccess = false;
474 // If one tile fails to write to a file, do not continue drawing the rest.
475 break;
476 }
477 }
478 fDone->run();
479 }
480
481 void setPathAndSuccess(const SkString* path, bool* success) {
482 fPath = path;
483 fSuccess = success;
484 }
485
486private:
487 // All pointers unowned.
488 SkPicture* fClone; // Picture to draw from. Each CloneData has a unique one which
489 // is threadsafe.
490 SkCanvas* fCanvas; // Canvas to draw to. Reused for each tile.
491 const SkString* fPath; // If non-null, path to write the result to as a PNG.
492 SkTDArray<SkRect>& fRects; // All tiles of the picture.
493 const int fStart; // Range of tiles drawn by this thread.
494 const int fEnd;
495 bool* fSuccess; // Only meaningful if path is non-null. Shared by all threads,
496 // and only set to false upon failure to write to a PNG.
497 SkRunnable* fDone;
498};
499
500MultiCorePictureRenderer::MultiCorePictureRenderer(int threadCount)
501: fNumThreads(threadCount)
502, fThreadPool(threadCount)
503, fCountdown(threadCount) {
504 // Only need to create fNumThreads - 1 clones, since one thread will use the base
505 // picture.
506 fPictureClones = SkNEW_ARRAY(SkPicture, fNumThreads - 1);
507 fCloneData = SkNEW_ARRAY(CloneData*, fNumThreads);
508}
509
510void MultiCorePictureRenderer::init(SkPicture *pict) {
511 // Set fPicture and the tiles.
512 this->INHERITED::init(pict);
513 for (int i = 0; i < fNumThreads; ++i) {
514 *fCanvasPool.append() = this->setupCanvas(this->getTileWidth(), this->getTileHeight());
515 }
516 // Only need to create fNumThreads - 1 clones, since one thread will use the base picture.
517 fPicture->clone(fPictureClones, fNumThreads - 1);
518 // Populate each thread with the appropriate data.
519 // Group the tiles into nearly equal size chunks, rounding up so we're sure to cover them all.
520 const int chunkSize = (fTileRects.count() + fNumThreads - 1) / fNumThreads;
521
522 for (int i = 0; i < fNumThreads; i++) {
523 SkPicture* pic;
524 if (i == fNumThreads-1) {
525 // The last set will use the original SkPicture.
526 pic = fPicture;
527 } else {
528 pic = &fPictureClones[i];
529 }
530 const int start = i * chunkSize;
531 const int end = SkMin32(start + chunkSize, fTileRects.count());
532 fCloneData[i] = SkNEW_ARGS(CloneData,
533 (pic, fCanvasPool[i], fTileRects, start, end, &fCountdown));
534 }
535}
536
537bool MultiCorePictureRenderer::render(const SkString *path) {
538 bool success = true;
539 if (path != NULL) {
540 for (int i = 0; i < fNumThreads-1; i++) {
541 fCloneData[i]->setPathAndSuccess(path, &success);
542 }
543 }
544
545 fCountdown.reset(fNumThreads);
546 for (int i = 0; i < fNumThreads; i++) {
547 fThreadPool.add(fCloneData[i]);
548 }
549 fCountdown.wait();
550
551 return success;
552}
553
554void MultiCorePictureRenderer::end() {
555 for (int i = 0; i < fNumThreads - 1; i++) {
556 SkDELETE(fCloneData[i]);
557 fCloneData[i] = NULL;
558 }
559
560 fCanvasPool.unrefAll();
561
562 this->INHERITED::end();
563}
564
565MultiCorePictureRenderer::~MultiCorePictureRenderer() {
566 // Each individual CloneData was deleted in end.
567 SkDELETE_ARRAY(fCloneData);
568 SkDELETE_ARRAY(fPictureClones);
569}
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000570
scroggo@google.com0a049b82012-11-02 22:01:26 +0000571SkString MultiCorePictureRenderer::getConfigNameInternal() {
572 SkString name = this->INHERITED::getConfigNameInternal();
573 name.appendf("_multi_%i_threads", fNumThreads);
574 return name;
575}
576
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000577///////////////////////////////////////////////////////////////////////////////////////////////
scroggo@google.com9a412522012-09-07 15:21:18 +0000578
579void PlaybackCreationRenderer::setup() {
junov@chromium.org9313ca42012-11-02 18:11:49 +0000580 fReplayer.reset(this->createPicture());
581 SkCanvas* recorder = fReplayer->beginRecording(fPicture->width(), fPicture->height(),
582 this->recordFlags());
scroggo@google.com9a412522012-09-07 15:21:18 +0000583 fPicture->draw(recorder);
584}
585
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000586bool PlaybackCreationRenderer::render(const SkString*) {
junov@chromium.org9313ca42012-11-02 18:11:49 +0000587 fReplayer->endRecording();
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000588 // Since this class does not actually render, return false.
589 return false;
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000590}
591
scroggo@google.com0a049b82012-11-02 22:01:26 +0000592SkString PlaybackCreationRenderer::getConfigNameInternal() {
593 return SkString("playback_creation");
594}
595
junov@chromium.org9313ca42012-11-02 18:11:49 +0000596///////////////////////////////////////////////////////////////////////////////////////////////
597// SkPicture variants for each BBoxHierarchy type
598
599class RTreePicture : public SkPicture {
600public:
601 virtual SkBBoxHierarchy* createBBoxHierarchy() const SK_OVERRIDE{
602 static const int kRTreeMinChildren = 6;
603 static const int kRTreeMaxChildren = 11;
604 SkScalar aspectRatio = SkScalarDiv(SkIntToScalar(fWidth),
605 SkIntToScalar(fHeight));
606 return SkRTree::Create(kRTreeMinChildren, kRTreeMaxChildren,
607 aspectRatio);
608 }
609};
610
junov@chromium.org7b537062012-11-06 18:58:43 +0000611class TileGridPicture : public SkPicture {
612public:
613 TileGridPicture(int tileWidth, int tileHeight, int xTileCount, int yTileCount) {
614 fTileWidth = tileWidth;
615 fTileHeight = tileHeight;
616 fXTileCount = xTileCount;
617 fYTileCount = yTileCount;
618 }
619
620 virtual SkBBoxHierarchy* createBBoxHierarchy() const SK_OVERRIDE{
621 return SkNEW_ARGS(SkTileGrid, (fTileWidth, fTileHeight, fXTileCount, fYTileCount));
622 }
623private:
624 int fTileWidth, fTileHeight, fXTileCount, fYTileCount;
625};
626
junov@chromium.org9313ca42012-11-02 18:11:49 +0000627SkPicture* PictureRenderer::createPicture() {
628 switch (fBBoxHierarchyType) {
629 case kNone_BBoxHierarchyType:
630 return SkNEW(SkPicture);
631 case kRTree_BBoxHierarchyType:
632 return SkNEW(RTreePicture);
junov@chromium.org7b537062012-11-06 18:58:43 +0000633 case kTileGrid_BBoxHierarchyType:
634 {
635 int xTileCount = fPicture->width() / fGridWidth +
636 ((fPicture->width() % fGridWidth) ? 1 : 0);
637 int yTileCount = fPicture->height() / fGridHeight +
638 ((fPicture->height() % fGridHeight) ? 1 : 0);
639 return SkNEW_ARGS(TileGridPicture, (fGridWidth, fGridHeight, xTileCount,
640 yTileCount));
641 }
junov@chromium.org9313ca42012-11-02 18:11:49 +0000642 }
643 SkASSERT(0); // invalid bbhType
644 return NULL;
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000645}
junov@chromium.org9313ca42012-11-02 18:11:49 +0000646
647} // namespace sk_tools