blob: e6d86dda2e1de018a793709e0138510b0612e3ca [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();
borenet@google.com070d3542012-10-26 13:26:55 +0000166 if (NULL != path) {
167 return write(fCanvas, *path);
168 }
169 return true;
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000170}
171
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000172///////////////////////////////////////////////////////////////////////////////////////////////
173
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000174bool SimplePictureRenderer::render(const SkString* path) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000175 SkASSERT(fCanvas.get() != NULL);
176 SkASSERT(fPicture != NULL);
keyar@chromium.org78a35c52012-08-20 15:03:44 +0000177 if (NULL == fCanvas.get() || NULL == fPicture) {
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000178 return false;
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000179 }
180
181 fCanvas->drawPicture(*fPicture);
scroggo@google.com9a412522012-09-07 15:21:18 +0000182 fCanvas->flush();
borenet@google.com070d3542012-10-26 13:26:55 +0000183 if (NULL != path) {
184 return write(fCanvas, *path);
185 }
186 return true;
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000187}
188
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000189///////////////////////////////////////////////////////////////////////////////////////////////
190
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000191TiledPictureRenderer::TiledPictureRenderer()
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000192 : fUsePipe(false)
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000193 , fTileWidth(kDefaultTileWidth)
rileya@google.comb947b912012-08-29 17:35:07 +0000194 , fTileHeight(kDefaultTileHeight)
rileya@google.coma04dc022012-09-10 19:01:38 +0000195 , fTileWidthPercentage(0.0)
rileya@google.comb947b912012-08-29 17:35:07 +0000196 , fTileHeightPercentage(0.0)
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000197 , fTileMinPowerOf2Width(0)
198 , fTileCounter(0)
199 , fNumThreads(1)
200 , fPictureClones(NULL)
201 , fPipeController(NULL) { }
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000202
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000203void TiledPictureRenderer::init(SkPicture* pict) {
204 SkASSERT(pict != NULL);
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000205 SkASSERT(0 == fTileRects.count());
206 if (NULL == pict || fTileRects.count() != 0) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000207 return;
208 }
209
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000210 // Do not call INHERITED::init(), which would create a (potentially large) canvas which is not
211 // used by bench_pictures.
212 fPicture = pict;
keyar@chromium.orgcc6e5ef2012-07-27 20:09:26 +0000213
214 if (fTileWidthPercentage > 0) {
robertphillips@google.com5d8d1862012-08-15 14:36:41 +0000215 fTileWidth = sk_float_ceil2int(float(fTileWidthPercentage * fPicture->width() / 100));
keyar@chromium.orgcc6e5ef2012-07-27 20:09:26 +0000216 }
217 if (fTileHeightPercentage > 0) {
robertphillips@google.com5d8d1862012-08-15 14:36:41 +0000218 fTileHeight = sk_float_ceil2int(float(fTileHeightPercentage * fPicture->height() / 100));
keyar@chromium.orgcc6e5ef2012-07-27 20:09:26 +0000219 }
220
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000221 if (fTileMinPowerOf2Width > 0) {
222 this->setupPowerOf2Tiles();
223 } else {
224 this->setupTiles();
225 }
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000226
227 if (this->multiThreaded()) {
228 for (int i = 0; i < fNumThreads; ++i) {
229 *fCanvasPool.append() = this->setupCanvas(fTileWidth, fTileHeight);
230 }
231 if (!fUsePipe) {
232 SkASSERT(NULL == fPictureClones);
233 // Only need to create fNumThreads - 1 clones, since one thread will use the base
234 // picture.
235 int numberOfClones = fNumThreads - 1;
236 // This will be deleted in end().
237 fPictureClones = SkNEW_ARRAY(SkPicture, numberOfClones);
scroggo@google.comb4773b42012-10-01 20:06:09 +0000238 fPicture->clone(fPictureClones, numberOfClones);
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000239 }
240 }
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000241}
242
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000243void TiledPictureRenderer::end() {
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000244 fTileRects.reset();
245 SkDELETE_ARRAY(fPictureClones);
246 fPictureClones = NULL;
247 fCanvasPool.unrefAll();
248 if (fPipeController != NULL) {
249 SkASSERT(fUsePipe);
250 SkDELETE(fPipeController);
251 fPipeController = NULL;
252 }
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000253 this->INHERITED::end();
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000254}
255
256TiledPictureRenderer::~TiledPictureRenderer() {
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000257 // end() must be called to delete fPictureClones and fPipeController
258 SkASSERT(NULL == fPictureClones);
259 SkASSERT(NULL == fPipeController);
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000260}
261
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000262void TiledPictureRenderer::setupTiles() {
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000263 for (int tile_y_start = 0; tile_y_start < fPicture->height(); tile_y_start += fTileHeight) {
264 for (int tile_x_start = 0; tile_x_start < fPicture->width(); tile_x_start += fTileWidth) {
265 *fTileRects.append() = SkRect::MakeXYWH(SkIntToScalar(tile_x_start),
266 SkIntToScalar(tile_y_start),
267 SkIntToScalar(fTileWidth),
268 SkIntToScalar(fTileHeight));
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000269 }
270 }
271}
272
273// The goal of the powers of two tiles is to minimize the amount of wasted tile
274// space in the width-wise direction and then minimize the number of tiles. The
275// constraints are that every tile must have a pixel width that is a power of
276// two and also be of some minimal width (that is also a power of two).
277//
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000278// This is solved by first taking our picture size and rounding it up to the
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000279// multiple of the minimal width. The binary representation of this rounded
280// value gives us the tiles we need: a bit of value one means we need a tile of
281// that size.
282void TiledPictureRenderer::setupPowerOf2Tiles() {
283 int rounded_value = fPicture->width();
284 if (fPicture->width() % fTileMinPowerOf2Width != 0) {
285 rounded_value = fPicture->width() - (fPicture->width() % fTileMinPowerOf2Width)
286 + fTileMinPowerOf2Width;
287 }
288
robertphillips@google.com94acc702012-09-06 18:43:21 +0000289 int num_bits = SkScalarCeilToInt(SkScalarLog2(SkIntToScalar(fPicture->width())));
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000290 int largest_possible_tile_size = 1 << num_bits;
291
292 // The tile height is constant for a particular picture.
293 for (int tile_y_start = 0; tile_y_start < fPicture->height(); tile_y_start += fTileHeight) {
294 int tile_x_start = 0;
295 int current_width = largest_possible_tile_size;
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000296 // Set fTileWidth to be the width of the widest tile, so that each canvas is large enough
297 // to draw each tile.
298 fTileWidth = current_width;
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000299
300 while (current_width >= fTileMinPowerOf2Width) {
301 // It is very important this is a bitwise AND.
302 if (current_width & rounded_value) {
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000303 *fTileRects.append() = SkRect::MakeXYWH(SkIntToScalar(tile_x_start),
304 SkIntToScalar(tile_y_start),
305 SkIntToScalar(current_width),
306 SkIntToScalar(fTileHeight));
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000307 tile_x_start += current_width;
308 }
309
310 current_width >>= 1;
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000311 }
312 }
313}
314
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000315/**
316 * Draw the specified playback to the canvas translated to rectangle provided, so that this mini
317 * canvas represents the rectangle's portion of the overall picture.
318 * Saves and restores so that the initial clip and matrix return to their state before this function
319 * is called.
320 */
321template<class T>
322static void DrawTileToCanvas(SkCanvas* canvas, const SkRect& tileRect, T* playback) {
323 int saveCount = canvas->save();
324 // Translate so that we draw the correct portion of the picture
325 canvas->translate(-tileRect.fLeft, -tileRect.fTop);
326 playback->draw(canvas);
327 canvas->restoreToCount(saveCount);
328 canvas->flush();
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000329}
330
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000331///////////////////////////////////////////////////////////////////////////////////////////////
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000332// Base class for data used both by pipe and clone picture multi threaded drawing.
333
334struct ThreadData {
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000335 ThreadData(SkCanvas* target, int* tileCounter, SkTDArray<SkRect>* tileRects,
336 const SkString* path, bool* success)
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000337 : fCanvas(target)
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000338 , fPath(path)
scroggo@google.comb8e404a2012-10-11 19:10:33 +0000339 , fSuccess(success)
340 , fTileCounter(tileCounter)
341 , fTileRects(tileRects) {
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000342 SkASSERT(target != NULL && tileCounter != NULL && tileRects != NULL);
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000343 // Success must start off true, and it will be set to false upon failure.
344 SkASSERT(success != NULL && *success);
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000345 }
346
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000347 int32_t nextTile(SkRect* rect) {
scroggo@google.comb4773b42012-10-01 20:06:09 +0000348 int32_t i = sk_atomic_inc(fTileCounter);
349 if (i < fTileRects->count()) {
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000350 SkASSERT(rect != NULL);
351 *rect = fTileRects->operator[](i);
352 return i;
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000353 }
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000354 return -1;
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000355 }
356
357 // All of these are pointers to objects owned elsewhere
358 SkCanvas* fCanvas;
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000359 const SkString* fPath;
360 bool* fSuccess;
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000361private:
362 // Shared by all threads, this states which is the next tile to be drawn.
363 int32_t* fTileCounter;
364 // Points to the array of rectangles. The array is already created before any threads are
365 // started and then it is unmodified, so there is no danger of race conditions.
366 const SkTDArray<SkRect>* fTileRects;
367};
368
369///////////////////////////////////////////////////////////////////////////////////////////////
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000370// Draw using Pipe
371
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000372struct TileData : public ThreadData {
373 TileData(ThreadSafePipeController* controller, SkCanvas* canvas, int* tileCounter,
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000374 SkTDArray<SkRect>* tileRects, const SkString* path, bool* success)
375 : INHERITED(canvas, tileCounter, tileRects, path, success)
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000376 , fController(controller) {}
377
scroggo@google.com8e073ba2012-08-31 16:25:46 +0000378 ThreadSafePipeController* fController;
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000379
380 typedef ThreadData INHERITED;
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000381};
382
383static void DrawTile(void* data) {
384 SkGraphics::SetTLSFontCacheLimit(1 * 1024 * 1024);
385 TileData* tileData = static_cast<TileData*>(data);
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000386
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000387 SkRect tileRect;
388 int32_t i;
389 while ((i = tileData->nextTile(&tileRect)) != -1) {
390 DrawTileToCanvas(tileData->fCanvas, tileRect, tileData->fController);
borenet@google.com070d3542012-10-26 13:26:55 +0000391 if (NULL != tileData->fPath &&
392 !writeAppendNumber(tileData->fCanvas, tileData->fPath, i)) {
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000393 *tileData->fSuccess = false;
394 break;
395 }
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000396 }
397 SkDELETE(tileData);
398}
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000399
400///////////////////////////////////////////////////////////////////////////////////////////////
401// Draw using Picture
402
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000403struct CloneData : public ThreadData {
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000404 CloneData(SkPicture* clone, SkCanvas* target, int* tileCounter, SkTDArray<SkRect>* tileRects,
405 const SkString* path, bool* success)
406 : INHERITED(target, tileCounter, tileRects, path, success)
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000407 , fClone(clone) {}
408
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000409 SkPicture* fClone;
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000410
411 typedef ThreadData INHERITED;
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000412};
413
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000414static void DrawClonedTiles(void* data) {
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000415 SkGraphics::SetTLSFontCacheLimit(1 * 1024 * 1024);
416 CloneData* cloneData = static_cast<CloneData*>(data);
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000417
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000418 SkRect tileRect;
419 int32_t i;
420 while ((i = cloneData->nextTile(&tileRect)) != -1) {
421 DrawTileToCanvas(cloneData->fCanvas, tileRect, cloneData->fClone);
borenet@google.com070d3542012-10-26 13:26:55 +0000422 if (NULL != cloneData->fPath &&
423 !writeAppendNumber(cloneData->fCanvas, cloneData->fPath, i)) {
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000424 *cloneData->fSuccess = false;
425 break;
426 }
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000427 }
428 SkDELETE(cloneData);
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000429}
430
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000431///////////////////////////////////////////////////////////////////////////////////////////////
432
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000433void TiledPictureRenderer::setup() {
434 if (this->multiThreaded()) {
435 // Reset to zero so we start with the first tile.
436 fTileCounter = 0;
437 if (fUsePipe) {
438 // Record the picture into the pipe controller. It is done here because unlike
439 // SkPicture, the pipe is modified (bitmaps can be removed) by drawing.
440 // fPipeController is deleted here after each call to render() except the last one and
441 // in end() for the last one.
442 if (fPipeController != NULL) {
443 SkDELETE(fPipeController);
444 }
445 fPipeController = SkNEW_ARGS(ThreadSafePipeController, (fTileRects.count()));
446 SkGPipeWriter writer;
447 SkCanvas* pipeCanvas = writer.startRecording(fPipeController,
448 SkGPipeWriter::kSimultaneousReaders_Flag);
449 SkASSERT(fPicture != NULL);
450 fPicture->draw(pipeCanvas);
451 writer.endRecording();
452 }
453 }
454}
455
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000456bool TiledPictureRenderer::render(const SkString* path) {
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000457 SkASSERT(fPicture != NULL);
458 if (NULL == fPicture) {
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000459 return false;
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000460 }
461
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000462 if (this->multiThreaded()) {
463 SkASSERT(fCanvasPool.count() == fNumThreads);
464 SkTDArray<SkThread*> threads;
465 SkThread::entryPointProc proc = fUsePipe ? DrawTile : DrawClonedTiles;
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000466 bool success = true;
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000467 for (int i = 0; i < fNumThreads; ++i) {
468 // data will be deleted by the entryPointProc.
469 ThreadData* data;
470 if (fUsePipe) {
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000471 data = SkNEW_ARGS(TileData, (fPipeController, fCanvasPool[i], &fTileCounter,
472 &fTileRects, path, &success));
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000473 } else {
474 SkPicture* pic = (0 == i) ? fPicture : &fPictureClones[i-1];
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000475 data = SkNEW_ARGS(CloneData, (pic, fCanvasPool[i], &fTileCounter, &fTileRects, path,
476 &success));
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000477 }
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000478 SkThread* thread = SkNEW_ARGS(SkThread, (proc, data));
479 if (!thread->start()) {
480 SkDebugf("Could not start %s thread %i.\n", (fUsePipe ? "pipe" : "picture"), i);
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000481 }
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000482 *threads.append() = thread;
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000483 }
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000484 SkASSERT(threads.count() == fNumThreads);
485 for (int i = 0; i < fNumThreads; ++i) {
486 SkThread* thread = threads[i];
487 thread->join();
488 SkDELETE(thread);
489 }
490 threads.reset();
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000491 return success;
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000492 } else {
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000493 // For single thread, we really only need one canvas total.
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000494 SkCanvas* canvas = this->setupCanvas(fTileWidth, fTileHeight);
495 SkAutoUnref aur(canvas);
496
borenet@google.com070d3542012-10-26 13:26:55 +0000497 bool success = true;
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000498 for (int i = 0; i < fTileRects.count(); ++i) {
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000499 DrawTileToCanvas(canvas, fTileRects[i], fPicture);
borenet@google.com070d3542012-10-26 13:26:55 +0000500 if (NULL != path) {
501 success &= writeAppendNumber(canvas, path, i);
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000502 }
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000503 }
borenet@google.com070d3542012-10-26 13:26:55 +0000504 return success;
keyar@chromium.org163b5672012-08-01 17:53:29 +0000505 }
506}
507
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000508SkCanvas* TiledPictureRenderer::setupCanvas(int width, int height) {
509 SkCanvas* canvas = this->INHERITED::setupCanvas(width, height);
510 SkASSERT(fPicture != NULL);
511 // Clip the tile to an area that is completely in what the SkPicture says is the
512 // drawn-to area. This is mostly important for tiles on the right and bottom edges
513 // as they may go over this area and the picture may have some commands that
514 // draw outside of this area and so should not actually be written.
515 SkRect clip = SkRect::MakeWH(SkIntToScalar(fPicture->width()),
516 SkIntToScalar(fPicture->height()));
517 canvas->clipRect(clip);
518 return canvas;
519}
520
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000521///////////////////////////////////////////////////////////////////////////////////////////////
scroggo@google.com9a412522012-09-07 15:21:18 +0000522
523void PlaybackCreationRenderer::setup() {
524 SkCanvas* recorder = fReplayer.beginRecording(fPicture->width(), fPicture->height());
525 fPicture->draw(recorder);
526}
527
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000528bool PlaybackCreationRenderer::render(const SkString*) {
scroggo@google.com9a412522012-09-07 15:21:18 +0000529 fReplayer.endRecording();
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000530 // Since this class does not actually render, return false.
531 return false;
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000532}
533
534}