blob: 247875250d876a68105fdc8b620fabecc53d458b [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"
keyar@chromium.orgea826952012-08-23 15:24:13 +000021#include "SkScalar.h"
keyar@chromium.org9299ede2012-08-21 19:05:08 +000022#include "SkString.h"
scroggo@google.com58b4ead2012-08-31 16:15:22 +000023#include "SkTemplates.h"
keyar@chromium.org451bb9f2012-07-26 17:27:57 +000024#include "SkTDArray.h"
scroggo@google.com58b4ead2012-08-31 16:15:22 +000025#include "SkThreadUtils.h"
keyar@chromium.org451bb9f2012-07-26 17:27:57 +000026#include "SkTypes.h"
keyar@chromium.org4ea96c52012-08-20 15:03:29 +000027
keyar@chromium.org451bb9f2012-07-26 17:27:57 +000028namespace sk_tools {
29
30enum {
31 kDefaultTileWidth = 256,
32 kDefaultTileHeight = 256
33};
34
keyar@chromium.org9d696c02012-08-07 17:11:33 +000035void PictureRenderer::init(SkPicture* pict) {
keyar@chromium.org78a35c52012-08-20 15:03:44 +000036 SkASSERT(NULL == fPicture);
37 SkASSERT(NULL == fCanvas.get());
38 if (fPicture != NULL || NULL != fCanvas.get()) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +000039 return;
40 }
41
42 SkASSERT(pict != NULL);
keyar@chromium.org78a35c52012-08-20 15:03:44 +000043 if (NULL == pict) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +000044 return;
45 }
46
47 fPicture = pict;
keyar@chromium.orga474ce32012-08-20 15:03:57 +000048 fCanvas.reset(this->setupCanvas());
49}
50
51SkCanvas* PictureRenderer::setupCanvas() {
52 return this->setupCanvas(fPicture->width(), fPicture->height());
53}
54
55SkCanvas* PictureRenderer::setupCanvas(int width, int height) {
keyar@chromium.org4ea96c52012-08-20 15:03:29 +000056 switch(fDeviceType) {
57 case kBitmap_DeviceType: {
58 SkBitmap bitmap;
keyar@chromium.orga474ce32012-08-20 15:03:57 +000059 sk_tools::setup_bitmap(&bitmap, width, height);
60 return SkNEW_ARGS(SkCanvas, (bitmap));
keyar@chromium.org4ea96c52012-08-20 15:03:29 +000061 break;
62 }
63#if SK_SUPPORT_GPU
64 case kGPU_DeviceType: {
keyar@chromium.org4ea96c52012-08-20 15:03:29 +000065 SkAutoTUnref<SkGpuDevice> device(SkNEW_ARGS(SkGpuDevice,
keyar@chromium.org06125642012-08-20 15:03:33 +000066 (fGrContext, SkBitmap::kARGB_8888_Config,
keyar@chromium.orga474ce32012-08-20 15:03:57 +000067 width, height)));
68 return SkNEW_ARGS(SkCanvas, (device.get()));
keyar@chromium.org4ea96c52012-08-20 15:03:29 +000069 break;
70 }
71#endif
72 default:
73 SkASSERT(0);
74 }
keyar@chromium.orga474ce32012-08-20 15:03:57 +000075
76 return NULL;
keyar@chromium.org9d696c02012-08-07 17:11:33 +000077}
78
79void PictureRenderer::end() {
keyar@chromium.org77a55222012-08-20 15:03:47 +000080 this->resetState();
keyar@chromium.org9d696c02012-08-07 17:11:33 +000081 fPicture = NULL;
82 fCanvas.reset(NULL);
83}
84
keyar@chromium.org77a55222012-08-20 15:03:47 +000085void PictureRenderer::resetState() {
keyar@chromium.org28136b32012-08-20 15:04:15 +000086#if SK_SUPPORT_GPU
87 if (this->isUsingGpuDevice()) {
88 SkGLContext* glContext = fGrContextFactory.getGLContext(
89 GrContextFactory::kNative_GLContextType);
keyar@chromium.org28136b32012-08-20 15:04:15 +000090
91 SkASSERT(glContext != NULL);
92 if (NULL == glContext) {
93 return;
94 }
95
scroggo@google.com9a412522012-09-07 15:21:18 +000096 fGrContext->flush();
keyar@chromium.org77a55222012-08-20 15:03:47 +000097 SK_GL(*glContext, Finish());
keyar@chromium.org77a55222012-08-20 15:03:47 +000098 }
keyar@chromium.orga40c20d2012-08-20 15:04:12 +000099#endif
keyar@chromium.org77a55222012-08-20 15:03:47 +0000100}
101
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000102/**
103 * Write the canvas to the specified path.
104 * @param canvas Must be non-null. Canvas to be written to a file.
105 * @param path Path for the file to be written. Should have no extension; write() will append
106 * an appropriate one. Passed in by value so it can be modified.
107 * @return bool True if the Canvas is written to a file.
108 */
109static bool write(SkCanvas* canvas, SkString path) {
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000110 SkASSERT(canvas != NULL);
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000111 if (NULL == canvas) {
keyar@chromium.org9299ede2012-08-21 19:05:08 +0000112 return false;
113 }
114
115 SkBitmap bitmap;
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000116 SkISize size = canvas->getDeviceSize();
117 sk_tools::setup_bitmap(&bitmap, size.width(), size.height());
keyar@chromium.org9299ede2012-08-21 19:05:08 +0000118
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000119 canvas->readPixels(&bitmap, 0, 0);
keyar@chromium.org9299ede2012-08-21 19:05:08 +0000120 sk_tools::force_all_opaque(bitmap);
121
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000122 // Since path is passed in by value, it is okay to modify it.
123 path.append(".png");
keyar@chromium.org9299ede2012-08-21 19:05:08 +0000124 return SkImageEncoder::EncodeFile(path.c_str(), bitmap, SkImageEncoder::kPNG_Type, 100);
125}
126
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000127/**
128 * If path is non NULL, append number to it, and call write(SkCanvas*, SkString) to write the
129 * provided canvas to a file. Returns true if path is NULL or if write() succeeds.
130 */
131static bool writeAppendNumber(SkCanvas* canvas, const SkString* path, int number) {
132 if (NULL == path) {
133 return true;
134 }
135 SkString pathWithNumber(*path);
136 pathWithNumber.appendf("%i", number);
137 return write(canvas, pathWithNumber);
138}
139
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000140///////////////////////////////////////////////////////////////////////////////////////////////
141
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000142bool RecordPictureRenderer::render(const SkString*) {
scroggo@google.com9a412522012-09-07 15:21:18 +0000143 SkPicture replayer;
144 SkCanvas* recorder = replayer.beginRecording(fPicture->width(), fPicture->height());
145 fPicture->draw(recorder);
146 replayer.endRecording();
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000147 // Since this class does not actually render, return false.
148 return false;
scroggo@google.com9a412522012-09-07 15:21:18 +0000149}
150
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000151///////////////////////////////////////////////////////////////////////////////////////////////
152
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000153bool PipePictureRenderer::render(const SkString* path) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000154 SkASSERT(fCanvas.get() != NULL);
155 SkASSERT(fPicture != NULL);
keyar@chromium.org78a35c52012-08-20 15:03:44 +0000156 if (NULL == fCanvas.get() || NULL == fPicture) {
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000157 return false;
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000158 }
159
160 PipeController pipeController(fCanvas.get());
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000161 SkGPipeWriter writer;
162 SkCanvas* pipeCanvas = writer.startRecording(&pipeController);
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000163 pipeCanvas->drawPicture(*fPicture);
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000164 writer.endRecording();
scroggo@google.com9a412522012-09-07 15:21:18 +0000165 fCanvas->flush();
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000166 return path != NULL && write(fCanvas, *path);
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000167}
168
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000169///////////////////////////////////////////////////////////////////////////////////////////////
170
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000171bool SimplePictureRenderer::render(const SkString* path) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000172 SkASSERT(fCanvas.get() != NULL);
173 SkASSERT(fPicture != NULL);
keyar@chromium.org78a35c52012-08-20 15:03:44 +0000174 if (NULL == fCanvas.get() || NULL == fPicture) {
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000175 return false;
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000176 }
177
178 fCanvas->drawPicture(*fPicture);
scroggo@google.com9a412522012-09-07 15:21:18 +0000179 fCanvas->flush();
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000180 return path != NULL && write(fCanvas, *path);
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000181}
182
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000183///////////////////////////////////////////////////////////////////////////////////////////////
184
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000185TiledPictureRenderer::TiledPictureRenderer()
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000186 : fUsePipe(false)
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000187 , fTileWidth(kDefaultTileWidth)
rileya@google.comb947b912012-08-29 17:35:07 +0000188 , fTileHeight(kDefaultTileHeight)
rileya@google.coma04dc022012-09-10 19:01:38 +0000189 , fTileWidthPercentage(0.0)
rileya@google.comb947b912012-08-29 17:35:07 +0000190 , fTileHeightPercentage(0.0)
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000191 , fTileMinPowerOf2Width(0)
192 , fTileCounter(0)
193 , fNumThreads(1)
194 , fPictureClones(NULL)
195 , fPipeController(NULL) { }
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000196
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000197void TiledPictureRenderer::init(SkPicture* pict) {
198 SkASSERT(pict != NULL);
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000199 SkASSERT(0 == fTileRects.count());
200 if (NULL == pict || fTileRects.count() != 0) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000201 return;
202 }
203
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000204 // Do not call INHERITED::init(), which would create a (potentially large) canvas which is not
205 // used by bench_pictures.
206 fPicture = pict;
keyar@chromium.orgcc6e5ef2012-07-27 20:09:26 +0000207
208 if (fTileWidthPercentage > 0) {
robertphillips@google.com5d8d1862012-08-15 14:36:41 +0000209 fTileWidth = sk_float_ceil2int(float(fTileWidthPercentage * fPicture->width() / 100));
keyar@chromium.orgcc6e5ef2012-07-27 20:09:26 +0000210 }
211 if (fTileHeightPercentage > 0) {
robertphillips@google.com5d8d1862012-08-15 14:36:41 +0000212 fTileHeight = sk_float_ceil2int(float(fTileHeightPercentage * fPicture->height() / 100));
keyar@chromium.orgcc6e5ef2012-07-27 20:09:26 +0000213 }
214
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000215 if (fTileMinPowerOf2Width > 0) {
216 this->setupPowerOf2Tiles();
217 } else {
218 this->setupTiles();
219 }
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000220
221 if (this->multiThreaded()) {
222 for (int i = 0; i < fNumThreads; ++i) {
223 *fCanvasPool.append() = this->setupCanvas(fTileWidth, fTileHeight);
224 }
225 if (!fUsePipe) {
226 SkASSERT(NULL == fPictureClones);
227 // Only need to create fNumThreads - 1 clones, since one thread will use the base
228 // picture.
229 int numberOfClones = fNumThreads - 1;
230 // This will be deleted in end().
231 fPictureClones = SkNEW_ARRAY(SkPicture, numberOfClones);
scroggo@google.comb4773b42012-10-01 20:06:09 +0000232 fPicture->clone(fPictureClones, numberOfClones);
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000233 }
234 }
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000235}
236
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000237void TiledPictureRenderer::end() {
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000238 fTileRects.reset();
239 SkDELETE_ARRAY(fPictureClones);
240 fPictureClones = NULL;
241 fCanvasPool.unrefAll();
242 if (fPipeController != NULL) {
243 SkASSERT(fUsePipe);
244 SkDELETE(fPipeController);
245 fPipeController = NULL;
246 }
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000247 this->INHERITED::end();
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000248}
249
250TiledPictureRenderer::~TiledPictureRenderer() {
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000251 // end() must be called to delete fPictureClones and fPipeController
252 SkASSERT(NULL == fPictureClones);
253 SkASSERT(NULL == fPipeController);
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000254}
255
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000256void TiledPictureRenderer::setupTiles() {
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000257 for (int tile_y_start = 0; tile_y_start < fPicture->height(); tile_y_start += fTileHeight) {
258 for (int tile_x_start = 0; tile_x_start < fPicture->width(); tile_x_start += fTileWidth) {
259 *fTileRects.append() = SkRect::MakeXYWH(SkIntToScalar(tile_x_start),
260 SkIntToScalar(tile_y_start),
261 SkIntToScalar(fTileWidth),
262 SkIntToScalar(fTileHeight));
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000263 }
264 }
265}
266
267// The goal of the powers of two tiles is to minimize the amount of wasted tile
268// space in the width-wise direction and then minimize the number of tiles. The
269// constraints are that every tile must have a pixel width that is a power of
270// two and also be of some minimal width (that is also a power of two).
271//
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000272// This is solved by first taking our picture size and rounding it up to the
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000273// multiple of the minimal width. The binary representation of this rounded
274// value gives us the tiles we need: a bit of value one means we need a tile of
275// that size.
276void TiledPictureRenderer::setupPowerOf2Tiles() {
277 int rounded_value = fPicture->width();
278 if (fPicture->width() % fTileMinPowerOf2Width != 0) {
279 rounded_value = fPicture->width() - (fPicture->width() % fTileMinPowerOf2Width)
280 + fTileMinPowerOf2Width;
281 }
282
robertphillips@google.com94acc702012-09-06 18:43:21 +0000283 int num_bits = SkScalarCeilToInt(SkScalarLog2(SkIntToScalar(fPicture->width())));
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000284 int largest_possible_tile_size = 1 << num_bits;
285
286 // The tile height is constant for a particular picture.
287 for (int tile_y_start = 0; tile_y_start < fPicture->height(); tile_y_start += fTileHeight) {
288 int tile_x_start = 0;
289 int current_width = largest_possible_tile_size;
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000290 // Set fTileWidth to be the width of the widest tile, so that each canvas is large enough
291 // to draw each tile.
292 fTileWidth = current_width;
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000293
294 while (current_width >= fTileMinPowerOf2Width) {
295 // It is very important this is a bitwise AND.
296 if (current_width & rounded_value) {
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000297 *fTileRects.append() = SkRect::MakeXYWH(SkIntToScalar(tile_x_start),
298 SkIntToScalar(tile_y_start),
299 SkIntToScalar(current_width),
300 SkIntToScalar(fTileHeight));
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000301 tile_x_start += current_width;
302 }
303
304 current_width >>= 1;
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000305 }
306 }
307}
308
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000309/**
310 * Draw the specified playback to the canvas translated to rectangle provided, so that this mini
311 * canvas represents the rectangle's portion of the overall picture.
312 * Saves and restores so that the initial clip and matrix return to their state before this function
313 * is called.
314 */
315template<class T>
316static void DrawTileToCanvas(SkCanvas* canvas, const SkRect& tileRect, T* playback) {
317 int saveCount = canvas->save();
318 // Translate so that we draw the correct portion of the picture
319 canvas->translate(-tileRect.fLeft, -tileRect.fTop);
320 playback->draw(canvas);
321 canvas->restoreToCount(saveCount);
322 canvas->flush();
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000323}
324
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000325///////////////////////////////////////////////////////////////////////////////////////////////
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000326// Base class for data used both by pipe and clone picture multi threaded drawing.
327
328struct ThreadData {
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000329 ThreadData(SkCanvas* target, int* tileCounter, SkTDArray<SkRect>* tileRects,
330 const SkString* path, bool* success)
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000331 : fCanvas(target)
332 , fTileCounter(tileCounter)
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000333 , fTileRects(tileRects)
334 , fPath(path)
335 , fSuccess(success) {
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000336 SkASSERT(target != NULL && tileCounter != NULL && tileRects != NULL);
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000337 // Success must start off true, and it will be set to false upon failure.
338 SkASSERT(success != NULL && *success);
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000339 }
340
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000341 int32_t nextTile(SkRect* rect) {
scroggo@google.comb4773b42012-10-01 20:06:09 +0000342 int32_t i = sk_atomic_inc(fTileCounter);
343 if (i < fTileRects->count()) {
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000344 SkASSERT(rect != NULL);
345 *rect = fTileRects->operator[](i);
346 return i;
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000347 }
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000348 return -1;
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000349 }
350
351 // All of these are pointers to objects owned elsewhere
352 SkCanvas* fCanvas;
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000353 const SkString* fPath;
354 bool* fSuccess;
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000355private:
356 // Shared by all threads, this states which is the next tile to be drawn.
357 int32_t* fTileCounter;
358 // Points to the array of rectangles. The array is already created before any threads are
359 // started and then it is unmodified, so there is no danger of race conditions.
360 const SkTDArray<SkRect>* fTileRects;
361};
362
363///////////////////////////////////////////////////////////////////////////////////////////////
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000364// Draw using Pipe
365
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000366struct TileData : public ThreadData {
367 TileData(ThreadSafePipeController* controller, SkCanvas* canvas, int* tileCounter,
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000368 SkTDArray<SkRect>* tileRects, const SkString* path, bool* success)
369 : INHERITED(canvas, tileCounter, tileRects, path, success)
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000370 , fController(controller) {}
371
scroggo@google.com8e073ba2012-08-31 16:25:46 +0000372 ThreadSafePipeController* fController;
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000373
374 typedef ThreadData INHERITED;
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000375};
376
377static void DrawTile(void* data) {
378 SkGraphics::SetTLSFontCacheLimit(1 * 1024 * 1024);
379 TileData* tileData = static_cast<TileData*>(data);
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000380
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000381 SkRect tileRect;
382 int32_t i;
383 while ((i = tileData->nextTile(&tileRect)) != -1) {
384 DrawTileToCanvas(tileData->fCanvas, tileRect, tileData->fController);
385 if (!writeAppendNumber(tileData->fCanvas, tileData->fPath, i)) {
386 *tileData->fSuccess = false;
387 break;
388 }
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000389 }
390 SkDELETE(tileData);
391}
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000392
393///////////////////////////////////////////////////////////////////////////////////////////////
394// Draw using Picture
395
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000396struct CloneData : public ThreadData {
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000397 CloneData(SkPicture* clone, SkCanvas* target, int* tileCounter, SkTDArray<SkRect>* tileRects,
398 const SkString* path, bool* success)
399 : INHERITED(target, tileCounter, tileRects, path, success)
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000400 , fClone(clone) {}
401
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000402 SkPicture* fClone;
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000403
404 typedef ThreadData INHERITED;
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000405};
406
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000407static void DrawClonedTiles(void* data) {
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000408 SkGraphics::SetTLSFontCacheLimit(1 * 1024 * 1024);
409 CloneData* cloneData = static_cast<CloneData*>(data);
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000410
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000411 SkRect tileRect;
412 int32_t i;
413 while ((i = cloneData->nextTile(&tileRect)) != -1) {
414 DrawTileToCanvas(cloneData->fCanvas, tileRect, cloneData->fClone);
415 if (!writeAppendNumber(cloneData->fCanvas, cloneData->fPath, i)) {
416 *cloneData->fSuccess = false;
417 break;
418 }
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000419 }
420 SkDELETE(cloneData);
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000421}
422
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000423///////////////////////////////////////////////////////////////////////////////////////////////
424
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000425void TiledPictureRenderer::setup() {
426 if (this->multiThreaded()) {
427 // Reset to zero so we start with the first tile.
428 fTileCounter = 0;
429 if (fUsePipe) {
430 // Record the picture into the pipe controller. It is done here because unlike
431 // SkPicture, the pipe is modified (bitmaps can be removed) by drawing.
432 // fPipeController is deleted here after each call to render() except the last one and
433 // in end() for the last one.
434 if (fPipeController != NULL) {
435 SkDELETE(fPipeController);
436 }
437 fPipeController = SkNEW_ARGS(ThreadSafePipeController, (fTileRects.count()));
438 SkGPipeWriter writer;
439 SkCanvas* pipeCanvas = writer.startRecording(fPipeController,
440 SkGPipeWriter::kSimultaneousReaders_Flag);
441 SkASSERT(fPicture != NULL);
442 fPicture->draw(pipeCanvas);
443 writer.endRecording();
444 }
445 }
446}
447
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000448bool TiledPictureRenderer::render(const SkString* path) {
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000449 SkASSERT(fPicture != NULL);
450 if (NULL == fPicture) {
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000451 return false;
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000452 }
453
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000454 if (this->multiThreaded()) {
455 SkASSERT(fCanvasPool.count() == fNumThreads);
456 SkTDArray<SkThread*> threads;
457 SkThread::entryPointProc proc = fUsePipe ? DrawTile : DrawClonedTiles;
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000458 bool success = true;
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000459 for (int i = 0; i < fNumThreads; ++i) {
460 // data will be deleted by the entryPointProc.
461 ThreadData* data;
462 if (fUsePipe) {
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000463 data = SkNEW_ARGS(TileData, (fPipeController, fCanvasPool[i], &fTileCounter,
464 &fTileRects, path, &success));
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000465 } else {
466 SkPicture* pic = (0 == i) ? fPicture : &fPictureClones[i-1];
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000467 data = SkNEW_ARGS(CloneData, (pic, fCanvasPool[i], &fTileCounter, &fTileRects, path,
468 &success));
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000469 }
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000470 SkThread* thread = SkNEW_ARGS(SkThread, (proc, data));
471 if (!thread->start()) {
472 SkDebugf("Could not start %s thread %i.\n", (fUsePipe ? "pipe" : "picture"), i);
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000473 }
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000474 *threads.append() = thread;
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000475 }
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000476 SkASSERT(threads.count() == fNumThreads);
477 for (int i = 0; i < fNumThreads; ++i) {
478 SkThread* thread = threads[i];
479 thread->join();
480 SkDELETE(thread);
481 }
482 threads.reset();
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000483 return success;
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000484 } else {
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000485 // For single thread, we really only need one canvas total.
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000486 SkCanvas* canvas = this->setupCanvas(fTileWidth, fTileHeight);
487 SkAutoUnref aur(canvas);
488
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000489 for (int i = 0; i < fTileRects.count(); ++i) {
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000490 DrawTileToCanvas(canvas, fTileRects[i], fPicture);
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000491 if (!writeAppendNumber(canvas, path, i)) {
492 return false;
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000493 }
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000494 }
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000495 return path != NULL;
keyar@chromium.org163b5672012-08-01 17:53:29 +0000496 }
497}
498
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000499SkCanvas* TiledPictureRenderer::setupCanvas(int width, int height) {
500 SkCanvas* canvas = this->INHERITED::setupCanvas(width, height);
501 SkASSERT(fPicture != NULL);
502 // Clip the tile to an area that is completely in what the SkPicture says is the
503 // drawn-to area. This is mostly important for tiles on the right and bottom edges
504 // as they may go over this area and the picture may have some commands that
505 // draw outside of this area and so should not actually be written.
506 SkRect clip = SkRect::MakeWH(SkIntToScalar(fPicture->width()),
507 SkIntToScalar(fPicture->height()));
508 canvas->clipRect(clip);
509 return canvas;
510}
511
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000512///////////////////////////////////////////////////////////////////////////////////////////////
scroggo@google.com9a412522012-09-07 15:21:18 +0000513
514void PlaybackCreationRenderer::setup() {
515 SkCanvas* recorder = fReplayer.beginRecording(fPicture->width(), fPicture->height());
516 fPicture->draw(recorder);
517}
518
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000519bool PlaybackCreationRenderer::render(const SkString*) {
scroggo@google.com9a412522012-09-07 15:21:18 +0000520 fReplayer.endRecording();
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000521 // Since this class does not actually render, return false.
522 return false;
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000523}
524
525}