blob: d5b72ab7ceebf2492d0e9a38a1cb1a1d5f57316d [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);
90 SK_GL(*glContext, Finish());
91 }
92#endif
93}
94
95void PictureRenderer::finishDraw() {
keyar@chromium.orgbffacc02012-08-20 15:04:07 +000096 SkASSERT(fCanvas.get() != NULL);
keyar@chromium.org28136b32012-08-20 15:04:15 +000097 if (NULL == fCanvas.get()) {
keyar@chromium.orgbffacc02012-08-20 15:04:07 +000098 return;
99 }
100
keyar@chromium.org77a55222012-08-20 15:03:47 +0000101 fCanvas->flush();
102
keyar@chromium.orga40c20d2012-08-20 15:04:12 +0000103#if SK_SUPPORT_GPU
keyar@chromium.org77a55222012-08-20 15:03:47 +0000104 if (this->isUsingGpuDevice()) {
105 SkGLContext* glContext = fGrContextFactory.getGLContext(
106 GrContextFactory::kNative_GLContextType);
keyar@chromium.org28136b32012-08-20 15:04:15 +0000107
108 SkASSERT(glContext != NULL);
109 if (NULL == glContext) {
110 return;
111 }
112
keyar@chromium.org77a55222012-08-20 15:03:47 +0000113 SK_GL(*glContext, Finish());
keyar@chromium.org77a55222012-08-20 15:03:47 +0000114 }
keyar@chromium.orga40c20d2012-08-20 15:04:12 +0000115#endif
keyar@chromium.org77a55222012-08-20 15:03:47 +0000116}
117
keyar@chromium.org9299ede2012-08-21 19:05:08 +0000118bool PictureRenderer::write(const SkString& path) const {
119 SkASSERT(fCanvas.get() != NULL);
120 SkASSERT(fPicture != NULL);
121 if (NULL == fCanvas.get() || NULL == fPicture) {
122 return false;
123 }
124
125 SkBitmap bitmap;
126 sk_tools::setup_bitmap(&bitmap, fPicture->width(), fPicture->height());
127
128 fCanvas->readPixels(&bitmap, 0, 0);
129 sk_tools::force_all_opaque(bitmap);
130
131 return SkImageEncoder::EncodeFile(path.c_str(), bitmap, SkImageEncoder::kPNG_Type, 100);
132}
133
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000134void PipePictureRenderer::render() {
135 SkASSERT(fCanvas.get() != NULL);
136 SkASSERT(fPicture != NULL);
keyar@chromium.org78a35c52012-08-20 15:03:44 +0000137 if (NULL == fCanvas.get() || NULL == fPicture) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000138 return;
139 }
140
141 PipeController pipeController(fCanvas.get());
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000142 SkGPipeWriter writer;
143 SkCanvas* pipeCanvas = writer.startRecording(&pipeController);
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000144 pipeCanvas->drawPicture(*fPicture);
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000145 writer.endRecording();
keyar@chromium.org28136b32012-08-20 15:04:15 +0000146 this->finishDraw();
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000147}
148
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000149void SimplePictureRenderer::render() {
150 SkASSERT(fCanvas.get() != NULL);
151 SkASSERT(fPicture != NULL);
keyar@chromium.org78a35c52012-08-20 15:03:44 +0000152 if (NULL == fCanvas.get() || NULL == fPicture) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000153 return;
154 }
155
156 fCanvas->drawPicture(*fPicture);
keyar@chromium.org28136b32012-08-20 15:04:15 +0000157 this->finishDraw();
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000158}
159
160TiledPictureRenderer::TiledPictureRenderer()
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000161 : fMultiThreaded(false)
162 , fUsePipe(false)
163 , fTileWidth(kDefaultTileWidth)
rileya@google.comb947b912012-08-29 17:35:07 +0000164 , fTileHeight(kDefaultTileHeight)
165 , fTileMinPowerOf2Width(0)
166 , fTileHeightPercentage(0.0)
167 , fTileWidthPercentage(0.0) {}
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000168
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000169void TiledPictureRenderer::init(SkPicture* pict) {
170 SkASSERT(pict != NULL);
keyar@chromium.org78a35c52012-08-20 15:03:44 +0000171 SkASSERT(0 == fTiles.count());
172 if (NULL == pict || fTiles.count() != 0) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000173 return;
174 }
175
176 this->INHERITED::init(pict);
keyar@chromium.orgcc6e5ef2012-07-27 20:09:26 +0000177
178 if (fTileWidthPercentage > 0) {
robertphillips@google.com5d8d1862012-08-15 14:36:41 +0000179 fTileWidth = sk_float_ceil2int(float(fTileWidthPercentage * fPicture->width() / 100));
keyar@chromium.orgcc6e5ef2012-07-27 20:09:26 +0000180 }
181 if (fTileHeightPercentage > 0) {
robertphillips@google.com5d8d1862012-08-15 14:36:41 +0000182 fTileHeight = sk_float_ceil2int(float(fTileHeightPercentage * fPicture->height() / 100));
keyar@chromium.orgcc6e5ef2012-07-27 20:09:26 +0000183 }
184
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000185 if (fTileMinPowerOf2Width > 0) {
186 this->setupPowerOf2Tiles();
187 } else {
188 this->setupTiles();
189 }
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000190}
191
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000192void TiledPictureRenderer::render() {
193 SkASSERT(fCanvas.get() != NULL);
194 SkASSERT(fPicture != NULL);
keyar@chromium.org78a35c52012-08-20 15:03:44 +0000195 if (NULL == fCanvas.get() || NULL == fPicture) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000196 return;
197 }
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000198
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000199 this->drawTiles();
200 this->copyTilesToCanvas();
keyar@chromium.org28136b32012-08-20 15:04:15 +0000201 this->finishDraw();
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000202}
203
204void TiledPictureRenderer::end() {
205 this->deleteTiles();
206 this->INHERITED::end();
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000207}
208
209TiledPictureRenderer::~TiledPictureRenderer() {
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000210 this->deleteTiles();
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000211}
212
keyar@chromium.orgda652c22012-08-20 22:04:07 +0000213void TiledPictureRenderer::clipTile(SkCanvas* tile) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000214 SkRect clip = SkRect::MakeWH(SkIntToScalar(fPicture->width()),
215 SkIntToScalar(fPicture->height()));
keyar@chromium.orgda652c22012-08-20 22:04:07 +0000216 tile->clipRect(clip);
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000217}
218
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000219void TiledPictureRenderer::addTile(int tile_x_start, int tile_y_start, int width, int height) {
220 SkCanvas* tile = this->setupCanvas(width, height);
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000221
keyar@chromium.orgda652c22012-08-20 22:04:07 +0000222 tile->translate(SkIntToScalar(-tile_x_start), SkIntToScalar(-tile_y_start));
223 this->clipTile(tile);
224
225 fTiles.push(tile);
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000226}
227
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000228void TiledPictureRenderer::setupTiles() {
229 for (int tile_y_start = 0; tile_y_start < fPicture->height();
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000230 tile_y_start += fTileHeight) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000231 for (int tile_x_start = 0; tile_x_start < fPicture->width();
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000232 tile_x_start += fTileWidth) {
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000233 this->addTile(tile_x_start, tile_y_start, fTileWidth, fTileHeight);
234 }
235 }
236}
237
238// The goal of the powers of two tiles is to minimize the amount of wasted tile
239// space in the width-wise direction and then minimize the number of tiles. The
240// constraints are that every tile must have a pixel width that is a power of
241// two and also be of some minimal width (that is also a power of two).
242//
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000243// This is solved by first taking our picture size and rounding it up to the
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000244// multiple of the minimal width. The binary representation of this rounded
245// value gives us the tiles we need: a bit of value one means we need a tile of
246// that size.
247void TiledPictureRenderer::setupPowerOf2Tiles() {
248 int rounded_value = fPicture->width();
249 if (fPicture->width() % fTileMinPowerOf2Width != 0) {
250 rounded_value = fPicture->width() - (fPicture->width() % fTileMinPowerOf2Width)
251 + fTileMinPowerOf2Width;
252 }
253
254 int num_bits = SkScalarCeilToInt(SkScalarLog2(fPicture->width()));
255 int largest_possible_tile_size = 1 << num_bits;
256
257 // The tile height is constant for a particular picture.
258 for (int tile_y_start = 0; tile_y_start < fPicture->height(); tile_y_start += fTileHeight) {
259 int tile_x_start = 0;
260 int current_width = largest_possible_tile_size;
261
262 while (current_width >= fTileMinPowerOf2Width) {
263 // It is very important this is a bitwise AND.
264 if (current_width & rounded_value) {
265 this->addTile(tile_x_start, tile_y_start, current_width, fTileHeight);
266 tile_x_start += current_width;
267 }
268
269 current_width >>= 1;
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000270 }
271 }
272}
273
274void TiledPictureRenderer::deleteTiles() {
275 for (int i = 0; i < fTiles.count(); ++i) {
keyar@chromium.orgda652c22012-08-20 22:04:07 +0000276 SkDELETE(fTiles[i]);
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000277 }
278
279 fTiles.reset();
280}
281
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000282///////////////////////////////////////////////////////////////////////////////////////////////
283// Draw using Pipe
284
285struct TileData {
286 TileData(SkCanvas* canvas, DeferredPipeController* controller);
287 SkCanvas* fCanvas;
288 DeferredPipeController* fController;
289 SkThread fThread;
290};
291
292static void DrawTile(void* data) {
293 SkGraphics::SetTLSFontCacheLimit(1 * 1024 * 1024);
294 TileData* tileData = static_cast<TileData*>(data);
295 tileData->fController->playback(tileData->fCanvas);
296}
297
298TileData::TileData(SkCanvas* canvas, DeferredPipeController* controller)
299: fCanvas(canvas)
300, fController(controller)
301, fThread(&DrawTile, static_cast<void*>(this)) {}
302
303///////////////////////////////////////////////////////////////////////////////////////////////
304// Draw using Picture
305
306struct CloneData {
307 CloneData(SkCanvas* target, SkPicture* original);
308 SkCanvas* fCanvas;
309 SkPicture* fClone;
310 SkThread fThread;
311};
312
313static void DrawClonedTile(void* data) {
314 SkGraphics::SetTLSFontCacheLimit(1 * 1024 * 1024);
315 CloneData* cloneData = static_cast<CloneData*>(data);
316 cloneData->fCanvas->drawPicture(*cloneData->fClone);
317}
318
319CloneData::CloneData(SkCanvas* target, SkPicture* clone)
320: fCanvas(target)
321, fClone(clone)
322, fThread(&DrawClonedTile, static_cast<void*>(this)) {}
323
324///////////////////////////////////////////////////////////////////////////////////////////////
325
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000326void TiledPictureRenderer::drawTiles() {
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000327 if (fMultiThreaded) {
328 if (fUsePipe) {
329 // First, draw into a pipe controller
330 SkGPipeWriter writer;
331 DeferredPipeController controller(fTiles.count());
332 SkCanvas* pipeCanvas = writer.startRecording(&controller,
333 SkGPipeWriter::kSimultaneousReaders_Flag);
334 pipeCanvas->drawPicture(*(fPicture));
335 writer.endRecording();
336
337 // Create and start the threads.
338 TileData* tileData[fTiles.count()];
339 for (int i = 0; i < fTiles.count(); i++) {
340 tileData[i] = SkNEW_ARGS(TileData, (fTiles[i], &controller));
341 if (!tileData[i]->fThread.start()) {
342 SkDebugf("could not start thread %i\n", i);
343 }
344 }
345 for (int i = 0; i < fTiles.count(); i++) {
346 tileData[i]->fThread.join();
347 SkDELETE(tileData[i]);
348 }
349 } else {
350 SkPicture* clones = SkNEW_ARRAY(SkPicture, fTiles.count());
351 SkAutoTDeleteArray<SkPicture> autodelete(clones);
352 fPicture->clone(clones, fTiles.count());
353 CloneData* cloneData[fTiles.count()];
354 for (int i = 0; i < fTiles.count(); i++) {
355 cloneData[i] = SkNEW_ARGS(CloneData, (fTiles[i], &clones[i]));
356 if (!cloneData[i]->fThread.start()) {
357 SkDebugf("Could not start picture thread %i\n", i);
358 }
359 }
360 for (int i = 0; i < fTiles.count(); i++) {
361 cloneData[i]->fThread.join();
362 SkDELETE(cloneData[i]);
363 }
364 }
365 } else {
366 for (int i = 0; i < fTiles.count(); ++i) {
367 fTiles[i]->drawPicture(*(fPicture));
368 }
keyar@chromium.org163b5672012-08-01 17:53:29 +0000369 }
370}
371
keyar@chromium.org28136b32012-08-20 15:04:15 +0000372void TiledPictureRenderer::finishDraw() {
keyar@chromium.org275be532012-08-20 15:04:00 +0000373 for (int i = 0; i < fTiles.count(); ++i) {
keyar@chromium.orgda652c22012-08-20 22:04:07 +0000374 fTiles[i]->flush();
keyar@chromium.org275be532012-08-20 15:04:00 +0000375 }
376
keyar@chromium.org28136b32012-08-20 15:04:15 +0000377#if SK_SUPPORT_GPU
378 if (this->isUsingGpuDevice()) {
379 SkGLContext* glContext = fGrContextFactory.getGLContext(
380 GrContextFactory::kNative_GLContextType);
381
382 SkASSERT(glContext != NULL);
383 if (NULL == glContext) {
384 return;
385 }
386
387 SK_GL(*glContext, Finish());
388 }
389#endif
keyar@chromium.org275be532012-08-20 15:04:00 +0000390}
391
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000392void TiledPictureRenderer::copyTilesToCanvas() {
keyar@chromium.orgea826952012-08-23 15:24:13 +0000393 for (int i = 0; i < fTiles.count(); ++i) {
394 // Since SkPicture performs a save and restore when being drawn to a
395 // canvas, we can be confident that the transform matrix of the canvas
396 // is what we set when creating the tiles.
397 SkMatrix matrix = fTiles[i]->getTotalMatrix();
398 SkScalar tile_x_start = matrix.getTranslateX();
399 SkScalar tile_y_start = matrix.getTranslateY();
400
401 SkBitmap source = fTiles[i]->getDevice()->accessBitmap(false);
402
403 fCanvas->drawBitmap(source, -tile_x_start, -tile_y_start);
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000404 }
405}
406
407}