blob: 064557b8b235b14af492abbe1cb3c079ed179396 [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"
keyar@chromium.orgea826952012-08-23 15:24:13 +000019#include "SkMatrix.h"
keyar@chromium.org451bb9f2012-07-26 17:27:57 +000020#include "SkPicture.h"
junov@chromium.org9313ca42012-11-02 18:11:49 +000021#include "SkRTree.h"
keyar@chromium.orgea826952012-08-23 15:24:13 +000022#include "SkScalar.h"
keyar@chromium.org9299ede2012-08-21 19:05:08 +000023#include "SkString.h"
scroggo@google.com58b4ead2012-08-31 16:15:22 +000024#include "SkTemplates.h"
keyar@chromium.org451bb9f2012-07-26 17:27:57 +000025#include "SkTDArray.h"
scroggo@google.com58b4ead2012-08-31 16:15:22 +000026#include "SkThreadUtils.h"
keyar@chromium.org451bb9f2012-07-26 17:27:57 +000027#include "SkTypes.h"
keyar@chromium.org4ea96c52012-08-20 15:03:29 +000028
keyar@chromium.org451bb9f2012-07-26 17:27:57 +000029namespace sk_tools {
30
31enum {
32 kDefaultTileWidth = 256,
33 kDefaultTileHeight = 256
34};
35
keyar@chromium.org9d696c02012-08-07 17:11:33 +000036void PictureRenderer::init(SkPicture* pict) {
keyar@chromium.org78a35c52012-08-20 15:03:44 +000037 SkASSERT(NULL == fPicture);
38 SkASSERT(NULL == fCanvas.get());
39 if (fPicture != NULL || NULL != fCanvas.get()) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +000040 return;
41 }
42
43 SkASSERT(pict != NULL);
keyar@chromium.org78a35c52012-08-20 15:03:44 +000044 if (NULL == pict) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +000045 return;
46 }
47
48 fPicture = pict;
junov@chromium.org9313ca42012-11-02 18:11:49 +000049 fPicture->ref();
keyar@chromium.orga474ce32012-08-20 15:03:57 +000050 fCanvas.reset(this->setupCanvas());
51}
52
53SkCanvas* PictureRenderer::setupCanvas() {
54 return this->setupCanvas(fPicture->width(), fPicture->height());
55}
56
57SkCanvas* PictureRenderer::setupCanvas(int width, int height) {
keyar@chromium.org4ea96c52012-08-20 15:03:29 +000058 switch(fDeviceType) {
59 case kBitmap_DeviceType: {
60 SkBitmap bitmap;
keyar@chromium.orga474ce32012-08-20 15:03:57 +000061 sk_tools::setup_bitmap(&bitmap, width, height);
62 return SkNEW_ARGS(SkCanvas, (bitmap));
keyar@chromium.org4ea96c52012-08-20 15:03:29 +000063 break;
64 }
65#if SK_SUPPORT_GPU
66 case kGPU_DeviceType: {
keyar@chromium.org4ea96c52012-08-20 15:03:29 +000067 SkAutoTUnref<SkGpuDevice> device(SkNEW_ARGS(SkGpuDevice,
keyar@chromium.org06125642012-08-20 15:03:33 +000068 (fGrContext, SkBitmap::kARGB_8888_Config,
keyar@chromium.orga474ce32012-08-20 15:03:57 +000069 width, height)));
70 return SkNEW_ARGS(SkCanvas, (device.get()));
keyar@chromium.org4ea96c52012-08-20 15:03:29 +000071 break;
72 }
73#endif
74 default:
75 SkASSERT(0);
76 }
keyar@chromium.orga474ce32012-08-20 15:03:57 +000077
78 return NULL;
keyar@chromium.org9d696c02012-08-07 17:11:33 +000079}
80
81void PictureRenderer::end() {
keyar@chromium.org77a55222012-08-20 15:03:47 +000082 this->resetState();
junov@chromium.org9313ca42012-11-02 18:11:49 +000083 SkSafeUnref(fPicture);
keyar@chromium.org9d696c02012-08-07 17:11:33 +000084 fPicture = NULL;
85 fCanvas.reset(NULL);
86}
87
junov@chromium.org9313ca42012-11-02 18:11:49 +000088/** Converts fPicture to a picture that uses a BBoxHierarchy.
89 * PictureRenderer subclasses that are used to test picture playback
90 * should call this method during init.
91 */
92void PictureRenderer::buildBBoxHierarchy() {
93 SkASSERT(NULL != fPicture);
94 if (kNone_BBoxHierarchyType != fBBoxHierarchyType && NULL != fPicture) {
95 SkPicture* newPicture = this->createPicture();
96 SkCanvas* recorder = newPicture->beginRecording(fPicture->width(), fPicture->height(),
97 this->recordFlags());
98 fPicture->draw(recorder);
99 newPicture->endRecording();
100 fPicture->unref();
101 fPicture = newPicture;
102 }
103}
104
keyar@chromium.org77a55222012-08-20 15:03:47 +0000105void PictureRenderer::resetState() {
keyar@chromium.org28136b32012-08-20 15:04:15 +0000106#if SK_SUPPORT_GPU
107 if (this->isUsingGpuDevice()) {
108 SkGLContext* glContext = fGrContextFactory.getGLContext(
109 GrContextFactory::kNative_GLContextType);
keyar@chromium.org28136b32012-08-20 15:04:15 +0000110
111 SkASSERT(glContext != NULL);
112 if (NULL == glContext) {
113 return;
114 }
115
scroggo@google.com9a412522012-09-07 15:21:18 +0000116 fGrContext->flush();
keyar@chromium.org77a55222012-08-20 15:03:47 +0000117 SK_GL(*glContext, Finish());
keyar@chromium.org77a55222012-08-20 15:03:47 +0000118 }
keyar@chromium.orga40c20d2012-08-20 15:04:12 +0000119#endif
keyar@chromium.org77a55222012-08-20 15:03:47 +0000120}
121
junov@chromium.org9313ca42012-11-02 18:11:49 +0000122uint32_t PictureRenderer::recordFlags() {
123 return kNone_BBoxHierarchyType == fBBoxHierarchyType ? 0 :
124 SkPicture::kOptimizeForClippedPlayback_RecordingFlag;
125}
126
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000127/**
128 * Write the canvas to the specified path.
129 * @param canvas Must be non-null. Canvas to be written to a file.
130 * @param path Path for the file to be written. Should have no extension; write() will append
131 * an appropriate one. Passed in by value so it can be modified.
132 * @return bool True if the Canvas is written to a file.
133 */
134static bool write(SkCanvas* canvas, SkString path) {
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000135 SkASSERT(canvas != NULL);
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000136 if (NULL == canvas) {
keyar@chromium.org9299ede2012-08-21 19:05:08 +0000137 return false;
138 }
139
140 SkBitmap bitmap;
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000141 SkISize size = canvas->getDeviceSize();
142 sk_tools::setup_bitmap(&bitmap, size.width(), size.height());
keyar@chromium.org9299ede2012-08-21 19:05:08 +0000143
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000144 canvas->readPixels(&bitmap, 0, 0);
keyar@chromium.org9299ede2012-08-21 19:05:08 +0000145 sk_tools::force_all_opaque(bitmap);
146
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000147 // Since path is passed in by value, it is okay to modify it.
148 path.append(".png");
keyar@chromium.org9299ede2012-08-21 19:05:08 +0000149 return SkImageEncoder::EncodeFile(path.c_str(), bitmap, SkImageEncoder::kPNG_Type, 100);
150}
151
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000152/**
153 * If path is non NULL, append number to it, and call write(SkCanvas*, SkString) to write the
154 * provided canvas to a file. Returns true if path is NULL or if write() succeeds.
155 */
156static bool writeAppendNumber(SkCanvas* canvas, const SkString* path, int number) {
157 if (NULL == path) {
158 return true;
159 }
160 SkString pathWithNumber(*path);
161 pathWithNumber.appendf("%i", number);
162 return write(canvas, pathWithNumber);
163}
164
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000165///////////////////////////////////////////////////////////////////////////////////////////////
166
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000167bool RecordPictureRenderer::render(const SkString*) {
junov@chromium.org9313ca42012-11-02 18:11:49 +0000168 SkAutoTUnref<SkPicture> replayer(this->createPicture());
169 SkCanvas* recorder = replayer->beginRecording(fPicture->width(), fPicture->height(),
170 this->recordFlags());
scroggo@google.com9a412522012-09-07 15:21:18 +0000171 fPicture->draw(recorder);
junov@chromium.org9313ca42012-11-02 18:11:49 +0000172 replayer->endRecording();
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000173 // Since this class does not actually render, return false.
174 return false;
scroggo@google.com9a412522012-09-07 15:21:18 +0000175}
176
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000177///////////////////////////////////////////////////////////////////////////////////////////////
178
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000179bool PipePictureRenderer::render(const SkString* path) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000180 SkASSERT(fCanvas.get() != NULL);
181 SkASSERT(fPicture != NULL);
keyar@chromium.org78a35c52012-08-20 15:03:44 +0000182 if (NULL == fCanvas.get() || NULL == fPicture) {
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000183 return false;
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000184 }
185
186 PipeController pipeController(fCanvas.get());
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000187 SkGPipeWriter writer;
188 SkCanvas* pipeCanvas = writer.startRecording(&pipeController);
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000189 pipeCanvas->drawPicture(*fPicture);
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000190 writer.endRecording();
scroggo@google.com9a412522012-09-07 15:21:18 +0000191 fCanvas->flush();
borenet@google.com070d3542012-10-26 13:26:55 +0000192 if (NULL != path) {
193 return write(fCanvas, *path);
194 }
195 return true;
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000196}
197
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000198///////////////////////////////////////////////////////////////////////////////////////////////
199
junov@chromium.org9313ca42012-11-02 18:11:49 +0000200void SimplePictureRenderer::init(SkPicture* picture) {
201 INHERITED::init(picture);
202 this->buildBBoxHierarchy();
203}
204
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000205bool SimplePictureRenderer::render(const SkString* path) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000206 SkASSERT(fCanvas.get() != NULL);
207 SkASSERT(fPicture != NULL);
keyar@chromium.org78a35c52012-08-20 15:03:44 +0000208 if (NULL == fCanvas.get() || NULL == fPicture) {
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000209 return false;
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000210 }
211
212 fCanvas->drawPicture(*fPicture);
scroggo@google.com9a412522012-09-07 15:21:18 +0000213 fCanvas->flush();
borenet@google.com070d3542012-10-26 13:26:55 +0000214 if (NULL != path) {
215 return write(fCanvas, *path);
216 }
217 return true;
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000218}
219
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000220///////////////////////////////////////////////////////////////////////////////////////////////
221
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000222TiledPictureRenderer::TiledPictureRenderer()
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000223 : fUsePipe(false)
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000224 , fTileWidth(kDefaultTileWidth)
rileya@google.comb947b912012-08-29 17:35:07 +0000225 , fTileHeight(kDefaultTileHeight)
rileya@google.coma04dc022012-09-10 19:01:38 +0000226 , fTileWidthPercentage(0.0)
rileya@google.comb947b912012-08-29 17:35:07 +0000227 , fTileHeightPercentage(0.0)
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000228 , fTileMinPowerOf2Width(0)
229 , fTileCounter(0)
230 , fNumThreads(1)
231 , fPictureClones(NULL)
232 , fPipeController(NULL) { }
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000233
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000234void TiledPictureRenderer::init(SkPicture* pict) {
235 SkASSERT(pict != NULL);
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000236 SkASSERT(0 == fTileRects.count());
237 if (NULL == pict || fTileRects.count() != 0) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000238 return;
239 }
240
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000241 // Do not call INHERITED::init(), which would create a (potentially large) canvas which is not
242 // used by bench_pictures.
243 fPicture = pict;
junov@chromium.org9313ca42012-11-02 18:11:49 +0000244 fPicture->ref();
245 if (!fUsePipe) {
246 this->buildBBoxHierarchy();
247 }
keyar@chromium.orgcc6e5ef2012-07-27 20:09:26 +0000248
249 if (fTileWidthPercentage > 0) {
robertphillips@google.com5d8d1862012-08-15 14:36:41 +0000250 fTileWidth = sk_float_ceil2int(float(fTileWidthPercentage * fPicture->width() / 100));
keyar@chromium.orgcc6e5ef2012-07-27 20:09:26 +0000251 }
252 if (fTileHeightPercentage > 0) {
robertphillips@google.com5d8d1862012-08-15 14:36:41 +0000253 fTileHeight = sk_float_ceil2int(float(fTileHeightPercentage * fPicture->height() / 100));
keyar@chromium.orgcc6e5ef2012-07-27 20:09:26 +0000254 }
255
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000256 if (fTileMinPowerOf2Width > 0) {
257 this->setupPowerOf2Tiles();
258 } else {
259 this->setupTiles();
260 }
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000261
262 if (this->multiThreaded()) {
263 for (int i = 0; i < fNumThreads; ++i) {
264 *fCanvasPool.append() = this->setupCanvas(fTileWidth, fTileHeight);
265 }
266 if (!fUsePipe) {
267 SkASSERT(NULL == fPictureClones);
268 // Only need to create fNumThreads - 1 clones, since one thread will use the base
269 // picture.
270 int numberOfClones = fNumThreads - 1;
271 // This will be deleted in end().
272 fPictureClones = SkNEW_ARRAY(SkPicture, numberOfClones);
scroggo@google.comb4773b42012-10-01 20:06:09 +0000273 fPicture->clone(fPictureClones, numberOfClones);
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000274 }
275 }
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000276}
277
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000278void TiledPictureRenderer::end() {
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000279 fTileRects.reset();
280 SkDELETE_ARRAY(fPictureClones);
281 fPictureClones = NULL;
282 fCanvasPool.unrefAll();
283 if (fPipeController != NULL) {
284 SkASSERT(fUsePipe);
285 SkDELETE(fPipeController);
286 fPipeController = NULL;
287 }
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000288 this->INHERITED::end();
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000289}
290
291TiledPictureRenderer::~TiledPictureRenderer() {
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000292 // end() must be called to delete fPictureClones and fPipeController
293 SkASSERT(NULL == fPictureClones);
294 SkASSERT(NULL == fPipeController);
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000295}
296
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000297void TiledPictureRenderer::setupTiles() {
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000298 for (int tile_y_start = 0; tile_y_start < fPicture->height(); tile_y_start += fTileHeight) {
299 for (int tile_x_start = 0; tile_x_start < fPicture->width(); tile_x_start += fTileWidth) {
300 *fTileRects.append() = SkRect::MakeXYWH(SkIntToScalar(tile_x_start),
301 SkIntToScalar(tile_y_start),
302 SkIntToScalar(fTileWidth),
303 SkIntToScalar(fTileHeight));
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000304 }
305 }
306}
307
308// The goal of the powers of two tiles is to minimize the amount of wasted tile
309// space in the width-wise direction and then minimize the number of tiles. The
310// constraints are that every tile must have a pixel width that is a power of
311// two and also be of some minimal width (that is also a power of two).
312//
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000313// This is solved by first taking our picture size and rounding it up to the
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000314// multiple of the minimal width. The binary representation of this rounded
315// value gives us the tiles we need: a bit of value one means we need a tile of
316// that size.
317void TiledPictureRenderer::setupPowerOf2Tiles() {
318 int rounded_value = fPicture->width();
319 if (fPicture->width() % fTileMinPowerOf2Width != 0) {
320 rounded_value = fPicture->width() - (fPicture->width() % fTileMinPowerOf2Width)
321 + fTileMinPowerOf2Width;
322 }
323
robertphillips@google.com94acc702012-09-06 18:43:21 +0000324 int num_bits = SkScalarCeilToInt(SkScalarLog2(SkIntToScalar(fPicture->width())));
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000325 int largest_possible_tile_size = 1 << num_bits;
326
327 // The tile height is constant for a particular picture.
328 for (int tile_y_start = 0; tile_y_start < fPicture->height(); tile_y_start += fTileHeight) {
329 int tile_x_start = 0;
330 int current_width = largest_possible_tile_size;
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000331 // Set fTileWidth to be the width of the widest tile, so that each canvas is large enough
332 // to draw each tile.
333 fTileWidth = current_width;
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000334
335 while (current_width >= fTileMinPowerOf2Width) {
336 // It is very important this is a bitwise AND.
337 if (current_width & rounded_value) {
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000338 *fTileRects.append() = SkRect::MakeXYWH(SkIntToScalar(tile_x_start),
339 SkIntToScalar(tile_y_start),
340 SkIntToScalar(current_width),
341 SkIntToScalar(fTileHeight));
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000342 tile_x_start += current_width;
343 }
344
345 current_width >>= 1;
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000346 }
347 }
348}
349
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000350/**
351 * Draw the specified playback to the canvas translated to rectangle provided, so that this mini
352 * canvas represents the rectangle's portion of the overall picture.
353 * Saves and restores so that the initial clip and matrix return to their state before this function
354 * is called.
355 */
356template<class T>
357static void DrawTileToCanvas(SkCanvas* canvas, const SkRect& tileRect, T* playback) {
358 int saveCount = canvas->save();
359 // Translate so that we draw the correct portion of the picture
360 canvas->translate(-tileRect.fLeft, -tileRect.fTop);
361 playback->draw(canvas);
362 canvas->restoreToCount(saveCount);
363 canvas->flush();
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000364}
365
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000366///////////////////////////////////////////////////////////////////////////////////////////////
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000367// Base class for data used both by pipe and clone picture multi threaded drawing.
368
369struct ThreadData {
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000370 ThreadData(SkCanvas* target, int* tileCounter, SkTDArray<SkRect>* tileRects,
371 const SkString* path, bool* success)
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000372 : fCanvas(target)
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000373 , fPath(path)
scroggo@google.comb8e404a2012-10-11 19:10:33 +0000374 , fSuccess(success)
375 , fTileCounter(tileCounter)
376 , fTileRects(tileRects) {
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000377 SkASSERT(target != NULL && tileCounter != NULL && tileRects != NULL);
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000378 // Success must start off true, and it will be set to false upon failure.
379 SkASSERT(success != NULL && *success);
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000380 }
381
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000382 int32_t nextTile(SkRect* rect) {
scroggo@google.comb4773b42012-10-01 20:06:09 +0000383 int32_t i = sk_atomic_inc(fTileCounter);
384 if (i < fTileRects->count()) {
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000385 SkASSERT(rect != NULL);
386 *rect = fTileRects->operator[](i);
387 return i;
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000388 }
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000389 return -1;
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000390 }
391
392 // All of these are pointers to objects owned elsewhere
393 SkCanvas* fCanvas;
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000394 const SkString* fPath;
395 bool* fSuccess;
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000396private:
397 // Shared by all threads, this states which is the next tile to be drawn.
398 int32_t* fTileCounter;
399 // Points to the array of rectangles. The array is already created before any threads are
400 // started and then it is unmodified, so there is no danger of race conditions.
401 const SkTDArray<SkRect>* fTileRects;
402};
403
404///////////////////////////////////////////////////////////////////////////////////////////////
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000405// Draw using Pipe
406
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000407struct TileData : public ThreadData {
408 TileData(ThreadSafePipeController* controller, SkCanvas* canvas, int* tileCounter,
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000409 SkTDArray<SkRect>* tileRects, const SkString* path, bool* success)
410 : INHERITED(canvas, tileCounter, tileRects, path, success)
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000411 , fController(controller) {}
412
scroggo@google.com8e073ba2012-08-31 16:25:46 +0000413 ThreadSafePipeController* fController;
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000414
415 typedef ThreadData INHERITED;
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000416};
417
418static void DrawTile(void* data) {
419 SkGraphics::SetTLSFontCacheLimit(1 * 1024 * 1024);
420 TileData* tileData = static_cast<TileData*>(data);
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000421
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000422 SkRect tileRect;
423 int32_t i;
424 while ((i = tileData->nextTile(&tileRect)) != -1) {
425 DrawTileToCanvas(tileData->fCanvas, tileRect, tileData->fController);
borenet@google.com070d3542012-10-26 13:26:55 +0000426 if (NULL != tileData->fPath &&
427 !writeAppendNumber(tileData->fCanvas, tileData->fPath, i)) {
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000428 *tileData->fSuccess = false;
429 break;
430 }
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000431 }
432 SkDELETE(tileData);
433}
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000434
435///////////////////////////////////////////////////////////////////////////////////////////////
436// Draw using Picture
437
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000438struct CloneData : public ThreadData {
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000439 CloneData(SkPicture* clone, SkCanvas* target, int* tileCounter, SkTDArray<SkRect>* tileRects,
440 const SkString* path, bool* success)
441 : INHERITED(target, tileCounter, tileRects, path, success)
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000442 , fClone(clone) {}
443
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000444 SkPicture* fClone;
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000445
446 typedef ThreadData INHERITED;
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000447};
448
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000449static void DrawClonedTiles(void* data) {
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000450 SkGraphics::SetTLSFontCacheLimit(1 * 1024 * 1024);
451 CloneData* cloneData = static_cast<CloneData*>(data);
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000452
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000453 SkRect tileRect;
454 int32_t i;
455 while ((i = cloneData->nextTile(&tileRect)) != -1) {
456 DrawTileToCanvas(cloneData->fCanvas, tileRect, cloneData->fClone);
borenet@google.com070d3542012-10-26 13:26:55 +0000457 if (NULL != cloneData->fPath &&
458 !writeAppendNumber(cloneData->fCanvas, cloneData->fPath, i)) {
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000459 *cloneData->fSuccess = false;
460 break;
461 }
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000462 }
463 SkDELETE(cloneData);
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000464}
465
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000466///////////////////////////////////////////////////////////////////////////////////////////////
467
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000468void TiledPictureRenderer::setup() {
469 if (this->multiThreaded()) {
470 // Reset to zero so we start with the first tile.
471 fTileCounter = 0;
472 if (fUsePipe) {
473 // Record the picture into the pipe controller. It is done here because unlike
474 // SkPicture, the pipe is modified (bitmaps can be removed) by drawing.
475 // fPipeController is deleted here after each call to render() except the last one and
476 // in end() for the last one.
477 if (fPipeController != NULL) {
478 SkDELETE(fPipeController);
479 }
480 fPipeController = SkNEW_ARGS(ThreadSafePipeController, (fTileRects.count()));
481 SkGPipeWriter writer;
482 SkCanvas* pipeCanvas = writer.startRecording(fPipeController,
483 SkGPipeWriter::kSimultaneousReaders_Flag);
484 SkASSERT(fPicture != NULL);
485 fPicture->draw(pipeCanvas);
486 writer.endRecording();
487 }
488 }
489}
490
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000491bool TiledPictureRenderer::render(const SkString* path) {
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000492 SkASSERT(fPicture != NULL);
493 if (NULL == fPicture) {
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000494 return false;
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000495 }
496
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000497 if (this->multiThreaded()) {
498 SkASSERT(fCanvasPool.count() == fNumThreads);
499 SkTDArray<SkThread*> threads;
500 SkThread::entryPointProc proc = fUsePipe ? DrawTile : DrawClonedTiles;
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000501 bool success = true;
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000502 for (int i = 0; i < fNumThreads; ++i) {
503 // data will be deleted by the entryPointProc.
504 ThreadData* data;
505 if (fUsePipe) {
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000506 data = SkNEW_ARGS(TileData, (fPipeController, fCanvasPool[i], &fTileCounter,
507 &fTileRects, path, &success));
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000508 } else {
509 SkPicture* pic = (0 == i) ? fPicture : &fPictureClones[i-1];
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000510 data = SkNEW_ARGS(CloneData, (pic, fCanvasPool[i], &fTileCounter, &fTileRects, path,
511 &success));
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000512 }
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000513 SkThread* thread = SkNEW_ARGS(SkThread, (proc, data));
514 if (!thread->start()) {
515 SkDebugf("Could not start %s thread %i.\n", (fUsePipe ? "pipe" : "picture"), i);
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000516 }
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000517 *threads.append() = thread;
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000518 }
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000519 SkASSERT(threads.count() == fNumThreads);
520 for (int i = 0; i < fNumThreads; ++i) {
521 SkThread* thread = threads[i];
522 thread->join();
523 SkDELETE(thread);
524 }
525 threads.reset();
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000526 return success;
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000527 } else {
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000528 // For single thread, we really only need one canvas total.
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000529 SkCanvas* canvas = this->setupCanvas(fTileWidth, fTileHeight);
530 SkAutoUnref aur(canvas);
531
borenet@google.com070d3542012-10-26 13:26:55 +0000532 bool success = true;
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000533 for (int i = 0; i < fTileRects.count(); ++i) {
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000534 DrawTileToCanvas(canvas, fTileRects[i], fPicture);
borenet@google.com070d3542012-10-26 13:26:55 +0000535 if (NULL != path) {
536 success &= writeAppendNumber(canvas, path, i);
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000537 }
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000538 }
borenet@google.com070d3542012-10-26 13:26:55 +0000539 return success;
keyar@chromium.org163b5672012-08-01 17:53:29 +0000540 }
541}
542
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000543SkCanvas* TiledPictureRenderer::setupCanvas(int width, int height) {
544 SkCanvas* canvas = this->INHERITED::setupCanvas(width, height);
545 SkASSERT(fPicture != NULL);
546 // Clip the tile to an area that is completely in what the SkPicture says is the
547 // drawn-to area. This is mostly important for tiles on the right and bottom edges
548 // as they may go over this area and the picture may have some commands that
549 // draw outside of this area and so should not actually be written.
550 SkRect clip = SkRect::MakeWH(SkIntToScalar(fPicture->width()),
551 SkIntToScalar(fPicture->height()));
552 canvas->clipRect(clip);
553 return canvas;
554}
555
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000556///////////////////////////////////////////////////////////////////////////////////////////////
scroggo@google.com9a412522012-09-07 15:21:18 +0000557
558void PlaybackCreationRenderer::setup() {
junov@chromium.org9313ca42012-11-02 18:11:49 +0000559 fReplayer.reset(this->createPicture());
560 SkCanvas* recorder = fReplayer->beginRecording(fPicture->width(), fPicture->height(),
561 this->recordFlags());
scroggo@google.com9a412522012-09-07 15:21:18 +0000562 fPicture->draw(recorder);
563}
564
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000565bool PlaybackCreationRenderer::render(const SkString*) {
junov@chromium.org9313ca42012-11-02 18:11:49 +0000566 fReplayer->endRecording();
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000567 // Since this class does not actually render, return false.
568 return false;
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000569}
570
junov@chromium.org9313ca42012-11-02 18:11:49 +0000571///////////////////////////////////////////////////////////////////////////////////////////////
572// SkPicture variants for each BBoxHierarchy type
573
574class RTreePicture : public SkPicture {
575public:
576 virtual SkBBoxHierarchy* createBBoxHierarchy() const SK_OVERRIDE{
577 static const int kRTreeMinChildren = 6;
578 static const int kRTreeMaxChildren = 11;
579 SkScalar aspectRatio = SkScalarDiv(SkIntToScalar(fWidth),
580 SkIntToScalar(fHeight));
581 return SkRTree::Create(kRTreeMinChildren, kRTreeMaxChildren,
582 aspectRatio);
583 }
584};
585
586SkPicture* PictureRenderer::createPicture() {
587 switch (fBBoxHierarchyType) {
588 case kNone_BBoxHierarchyType:
589 return SkNEW(SkPicture);
590 case kRTree_BBoxHierarchyType:
591 return SkNEW(RTreePicture);
592 }
593 SkASSERT(0); // invalid bbhType
594 return NULL;
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000595}
junov@chromium.org9313ca42012-11-02 18:11:49 +0000596
597} // namespace sk_tools