blob: e7e4b1ad1c2b3f6f2ffd46c01f70e073bc81a3a4 [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
keyar@chromium.org9299ede2012-08-21 19:05:08 +0000102bool PictureRenderer::write(const SkString& path) const {
103 SkASSERT(fCanvas.get() != NULL);
104 SkASSERT(fPicture != NULL);
105 if (NULL == fCanvas.get() || NULL == fPicture) {
106 return false;
107 }
108
109 SkBitmap bitmap;
110 sk_tools::setup_bitmap(&bitmap, fPicture->width(), fPicture->height());
111
112 fCanvas->readPixels(&bitmap, 0, 0);
113 sk_tools::force_all_opaque(bitmap);
114
115 return SkImageEncoder::EncodeFile(path.c_str(), bitmap, SkImageEncoder::kPNG_Type, 100);
116}
117
scroggo@google.com9a412522012-09-07 15:21:18 +0000118void RecordPictureRenderer::render(bool doExtraWorkToDrawToBaseCanvas) {
119 SkPicture replayer;
120 SkCanvas* recorder = replayer.beginRecording(fPicture->width(), fPicture->height());
121 fPicture->draw(recorder);
122 replayer.endRecording();
123}
124
125void PipePictureRenderer::render(bool doExtraWorkToDrawToBaseCanvas) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000126 SkASSERT(fCanvas.get() != NULL);
127 SkASSERT(fPicture != NULL);
keyar@chromium.org78a35c52012-08-20 15:03:44 +0000128 if (NULL == fCanvas.get() || NULL == fPicture) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000129 return;
130 }
131
132 PipeController pipeController(fCanvas.get());
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000133 SkGPipeWriter writer;
134 SkCanvas* pipeCanvas = writer.startRecording(&pipeController);
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000135 pipeCanvas->drawPicture(*fPicture);
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000136 writer.endRecording();
scroggo@google.com9a412522012-09-07 15:21:18 +0000137 fCanvas->flush();
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000138}
139
scroggo@google.com9a412522012-09-07 15:21:18 +0000140void SimplePictureRenderer::render(bool doExtraWorkToDrawToBaseCanvas) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000141 SkASSERT(fCanvas.get() != NULL);
142 SkASSERT(fPicture != NULL);
keyar@chromium.org78a35c52012-08-20 15:03:44 +0000143 if (NULL == fCanvas.get() || NULL == fPicture) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000144 return;
145 }
146
147 fCanvas->drawPicture(*fPicture);
scroggo@google.com9a412522012-09-07 15:21:18 +0000148 fCanvas->flush();
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000149}
150
151TiledPictureRenderer::TiledPictureRenderer()
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000152 : fMultiThreaded(false)
153 , fUsePipe(false)
154 , fTileWidth(kDefaultTileWidth)
rileya@google.comb947b912012-08-29 17:35:07 +0000155 , fTileHeight(kDefaultTileHeight)
156 , fTileMinPowerOf2Width(0)
157 , fTileHeightPercentage(0.0)
158 , fTileWidthPercentage(0.0) {}
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000159
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000160void TiledPictureRenderer::init(SkPicture* pict) {
161 SkASSERT(pict != NULL);
keyar@chromium.org78a35c52012-08-20 15:03:44 +0000162 SkASSERT(0 == fTiles.count());
163 if (NULL == pict || fTiles.count() != 0) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000164 return;
165 }
166
167 this->INHERITED::init(pict);
keyar@chromium.orgcc6e5ef2012-07-27 20:09:26 +0000168
169 if (fTileWidthPercentage > 0) {
robertphillips@google.com5d8d1862012-08-15 14:36:41 +0000170 fTileWidth = sk_float_ceil2int(float(fTileWidthPercentage * fPicture->width() / 100));
keyar@chromium.orgcc6e5ef2012-07-27 20:09:26 +0000171 }
172 if (fTileHeightPercentage > 0) {
robertphillips@google.com5d8d1862012-08-15 14:36:41 +0000173 fTileHeight = sk_float_ceil2int(float(fTileHeightPercentage * fPicture->height() / 100));
keyar@chromium.orgcc6e5ef2012-07-27 20:09:26 +0000174 }
175
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000176 if (fTileMinPowerOf2Width > 0) {
177 this->setupPowerOf2Tiles();
178 } else {
179 this->setupTiles();
180 }
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000181}
182
scroggo@google.com9a412522012-09-07 15:21:18 +0000183void TiledPictureRenderer::render(bool doExtraWorkToDrawToBaseCanvas) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000184 SkASSERT(fCanvas.get() != NULL);
185 SkASSERT(fPicture != NULL);
keyar@chromium.org78a35c52012-08-20 15:03:44 +0000186 if (NULL == fCanvas.get() || NULL == fPicture) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000187 return;
188 }
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000189
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000190 this->drawTiles();
scroggo@google.com9a412522012-09-07 15:21:18 +0000191 if (doExtraWorkToDrawToBaseCanvas) {
192 this->copyTilesToCanvas();
193 }
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000194}
195
196void TiledPictureRenderer::end() {
197 this->deleteTiles();
198 this->INHERITED::end();
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000199}
200
201TiledPictureRenderer::~TiledPictureRenderer() {
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000202 this->deleteTiles();
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000203}
204
keyar@chromium.orgda652c22012-08-20 22:04:07 +0000205void TiledPictureRenderer::clipTile(SkCanvas* tile) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000206 SkRect clip = SkRect::MakeWH(SkIntToScalar(fPicture->width()),
207 SkIntToScalar(fPicture->height()));
keyar@chromium.orgda652c22012-08-20 22:04:07 +0000208 tile->clipRect(clip);
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000209}
210
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000211void TiledPictureRenderer::addTile(int tile_x_start, int tile_y_start, int width, int height) {
212 SkCanvas* tile = this->setupCanvas(width, height);
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000213
keyar@chromium.orgda652c22012-08-20 22:04:07 +0000214 tile->translate(SkIntToScalar(-tile_x_start), SkIntToScalar(-tile_y_start));
215 this->clipTile(tile);
216
217 fTiles.push(tile);
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000218}
219
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000220void TiledPictureRenderer::setupTiles() {
221 for (int tile_y_start = 0; tile_y_start < fPicture->height();
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000222 tile_y_start += fTileHeight) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000223 for (int tile_x_start = 0; tile_x_start < fPicture->width();
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000224 tile_x_start += fTileWidth) {
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000225 this->addTile(tile_x_start, tile_y_start, fTileWidth, fTileHeight);
226 }
227 }
228}
229
230// The goal of the powers of two tiles is to minimize the amount of wasted tile
231// space in the width-wise direction and then minimize the number of tiles. The
232// constraints are that every tile must have a pixel width that is a power of
233// two and also be of some minimal width (that is also a power of two).
234//
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000235// This is solved by first taking our picture size and rounding it up to the
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000236// multiple of the minimal width. The binary representation of this rounded
237// value gives us the tiles we need: a bit of value one means we need a tile of
238// that size.
239void TiledPictureRenderer::setupPowerOf2Tiles() {
240 int rounded_value = fPicture->width();
241 if (fPicture->width() % fTileMinPowerOf2Width != 0) {
242 rounded_value = fPicture->width() - (fPicture->width() % fTileMinPowerOf2Width)
243 + fTileMinPowerOf2Width;
244 }
245
robertphillips@google.com94acc702012-09-06 18:43:21 +0000246 int num_bits = SkScalarCeilToInt(SkScalarLog2(SkIntToScalar(fPicture->width())));
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000247 int largest_possible_tile_size = 1 << num_bits;
248
249 // The tile height is constant for a particular picture.
250 for (int tile_y_start = 0; tile_y_start < fPicture->height(); tile_y_start += fTileHeight) {
251 int tile_x_start = 0;
252 int current_width = largest_possible_tile_size;
253
254 while (current_width >= fTileMinPowerOf2Width) {
255 // It is very important this is a bitwise AND.
256 if (current_width & rounded_value) {
257 this->addTile(tile_x_start, tile_y_start, current_width, fTileHeight);
258 tile_x_start += current_width;
259 }
260
261 current_width >>= 1;
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000262 }
263 }
264}
265
266void TiledPictureRenderer::deleteTiles() {
267 for (int i = 0; i < fTiles.count(); ++i) {
keyar@chromium.orgda652c22012-08-20 22:04:07 +0000268 SkDELETE(fTiles[i]);
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000269 }
270
271 fTiles.reset();
272}
273
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000274///////////////////////////////////////////////////////////////////////////////////////////////
275// Draw using Pipe
276
277struct TileData {
scroggo@google.com8e073ba2012-08-31 16:25:46 +0000278 TileData(SkCanvas* canvas, ThreadSafePipeController* controller);
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000279 SkCanvas* fCanvas;
scroggo@google.com8e073ba2012-08-31 16:25:46 +0000280 ThreadSafePipeController* fController;
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000281 SkThread fThread;
282};
283
284static void DrawTile(void* data) {
285 SkGraphics::SetTLSFontCacheLimit(1 * 1024 * 1024);
286 TileData* tileData = static_cast<TileData*>(data);
287 tileData->fController->playback(tileData->fCanvas);
scroggo@google.com9a412522012-09-07 15:21:18 +0000288 tileData->fCanvas->flush();
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000289}
290
scroggo@google.com8e073ba2012-08-31 16:25:46 +0000291TileData::TileData(SkCanvas* canvas, ThreadSafePipeController* controller)
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000292: fCanvas(canvas)
293, fController(controller)
294, fThread(&DrawTile, static_cast<void*>(this)) {}
295
296///////////////////////////////////////////////////////////////////////////////////////////////
297// Draw using Picture
298
299struct CloneData {
300 CloneData(SkCanvas* target, SkPicture* original);
301 SkCanvas* fCanvas;
302 SkPicture* fClone;
303 SkThread fThread;
304};
305
306static void DrawClonedTile(void* data) {
307 SkGraphics::SetTLSFontCacheLimit(1 * 1024 * 1024);
308 CloneData* cloneData = static_cast<CloneData*>(data);
309 cloneData->fCanvas->drawPicture(*cloneData->fClone);
scroggo@google.com9a412522012-09-07 15:21:18 +0000310 cloneData->fCanvas->flush();
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000311}
312
313CloneData::CloneData(SkCanvas* target, SkPicture* clone)
314: fCanvas(target)
315, fClone(clone)
316, fThread(&DrawClonedTile, static_cast<void*>(this)) {}
317
318///////////////////////////////////////////////////////////////////////////////////////////////
319
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000320void TiledPictureRenderer::drawTiles() {
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000321 if (fMultiThreaded) {
322 if (fUsePipe) {
323 // First, draw into a pipe controller
324 SkGPipeWriter writer;
scroggo@google.com8e073ba2012-08-31 16:25:46 +0000325 ThreadSafePipeController controller(fTiles.count());
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000326 SkCanvas* pipeCanvas = writer.startRecording(&controller,
327 SkGPipeWriter::kSimultaneousReaders_Flag);
328 pipeCanvas->drawPicture(*(fPicture));
329 writer.endRecording();
330
331 // Create and start the threads.
scroggo@google.com6bc8cf82012-08-31 18:34:09 +0000332 TileData** tileData = SkNEW_ARRAY(TileData*, fTiles.count());
333 SkAutoTDeleteArray<TileData*> deleteTileData(tileData);
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000334 for (int i = 0; i < fTiles.count(); i++) {
335 tileData[i] = SkNEW_ARGS(TileData, (fTiles[i], &controller));
336 if (!tileData[i]->fThread.start()) {
337 SkDebugf("could not start thread %i\n", i);
338 }
339 }
340 for (int i = 0; i < fTiles.count(); i++) {
341 tileData[i]->fThread.join();
342 SkDELETE(tileData[i]);
343 }
344 } else {
345 SkPicture* clones = SkNEW_ARRAY(SkPicture, fTiles.count());
346 SkAutoTDeleteArray<SkPicture> autodelete(clones);
347 fPicture->clone(clones, fTiles.count());
scroggo@google.com6bc8cf82012-08-31 18:34:09 +0000348 CloneData** cloneData = SkNEW_ARRAY(CloneData*, fTiles.count());
349 SkAutoTDeleteArray<CloneData*> deleteCloneData(cloneData);
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000350 for (int i = 0; i < fTiles.count(); i++) {
351 cloneData[i] = SkNEW_ARGS(CloneData, (fTiles[i], &clones[i]));
352 if (!cloneData[i]->fThread.start()) {
353 SkDebugf("Could not start picture thread %i\n", i);
354 }
355 }
356 for (int i = 0; i < fTiles.count(); i++) {
357 cloneData[i]->fThread.join();
358 SkDELETE(cloneData[i]);
359 }
360 }
361 } else {
362 for (int i = 0; i < fTiles.count(); ++i) {
363 fTiles[i]->drawPicture(*(fPicture));
scroggo@google.com9a412522012-09-07 15:21:18 +0000364 fTiles[i]->flush();
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000365 }
keyar@chromium.org163b5672012-08-01 17:53:29 +0000366 }
367}
368
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000369void TiledPictureRenderer::copyTilesToCanvas() {
keyar@chromium.orgea826952012-08-23 15:24:13 +0000370 for (int i = 0; i < fTiles.count(); ++i) {
371 // Since SkPicture performs a save and restore when being drawn to a
372 // canvas, we can be confident that the transform matrix of the canvas
373 // is what we set when creating the tiles.
374 SkMatrix matrix = fTiles[i]->getTotalMatrix();
375 SkScalar tile_x_start = matrix.getTranslateX();
376 SkScalar tile_y_start = matrix.getTranslateY();
377
378 SkBitmap source = fTiles[i]->getDevice()->accessBitmap(false);
379
380 fCanvas->drawBitmap(source, -tile_x_start, -tile_y_start);
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000381 }
scroggo@google.com9a412522012-09-07 15:21:18 +0000382 fCanvas->flush();
383}
384
385void PlaybackCreationRenderer::setup() {
386 SkCanvas* recorder = fReplayer.beginRecording(fPicture->width(), fPicture->height());
387 fPicture->draw(recorder);
388}
389
390void PlaybackCreationRenderer::render(bool doExtraWorkToDrawToBaseCanvas) {
391 fReplayer.endRecording();
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000392}
393
394}