blob: b162abc3400c60c015976b1a210a298afa333562 [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"
9#include "SamplePipeControllers.h"
10#include "SkCanvas.h"
11#include "SkDevice.h"
keyar@chromium.org9299ede2012-08-21 19:05:08 +000012#include "SkImageEncoder.h"
keyar@chromium.org451bb9f2012-07-26 17:27:57 +000013#include "SkGPipe.h"
keyar@chromium.orgea826952012-08-23 15:24:13 +000014#include "SkMatrix.h"
keyar@chromium.org451bb9f2012-07-26 17:27:57 +000015#include "SkPicture.h"
keyar@chromium.orgea826952012-08-23 15:24:13 +000016#include "SkScalar.h"
keyar@chromium.org9299ede2012-08-21 19:05:08 +000017#include "SkString.h"
keyar@chromium.org451bb9f2012-07-26 17:27:57 +000018#include "SkTDArray.h"
19#include "SkTypes.h"
20#include "picture_utils.h"
21
keyar@chromium.org4ea96c52012-08-20 15:03:29 +000022#if SK_SUPPORT_GPU
keyar@chromium.org4ea96c52012-08-20 15:03:29 +000023#include "SkGpuDevice.h"
keyar@chromium.org4ea96c52012-08-20 15:03:29 +000024#endif
25
keyar@chromium.org451bb9f2012-07-26 17:27:57 +000026namespace sk_tools {
27
28enum {
29 kDefaultTileWidth = 256,
30 kDefaultTileHeight = 256
31};
32
keyar@chromium.org9d696c02012-08-07 17:11:33 +000033void PictureRenderer::init(SkPicture* pict) {
keyar@chromium.org78a35c52012-08-20 15:03:44 +000034 SkASSERT(NULL == fPicture);
35 SkASSERT(NULL == fCanvas.get());
36 if (fPicture != NULL || NULL != fCanvas.get()) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +000037 return;
38 }
39
40 SkASSERT(pict != NULL);
keyar@chromium.org78a35c52012-08-20 15:03:44 +000041 if (NULL == pict) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +000042 return;
43 }
44
45 fPicture = pict;
keyar@chromium.orga474ce32012-08-20 15:03:57 +000046 fCanvas.reset(this->setupCanvas());
47}
48
49SkCanvas* PictureRenderer::setupCanvas() {
50 return this->setupCanvas(fPicture->width(), fPicture->height());
51}
52
53SkCanvas* PictureRenderer::setupCanvas(int width, int height) {
keyar@chromium.org4ea96c52012-08-20 15:03:29 +000054 switch(fDeviceType) {
55 case kBitmap_DeviceType: {
56 SkBitmap bitmap;
keyar@chromium.orga474ce32012-08-20 15:03:57 +000057 sk_tools::setup_bitmap(&bitmap, width, height);
58 return SkNEW_ARGS(SkCanvas, (bitmap));
keyar@chromium.org4ea96c52012-08-20 15:03:29 +000059 break;
60 }
61#if SK_SUPPORT_GPU
62 case kGPU_DeviceType: {
keyar@chromium.org4ea96c52012-08-20 15:03:29 +000063 SkAutoTUnref<SkGpuDevice> device(SkNEW_ARGS(SkGpuDevice,
keyar@chromium.org06125642012-08-20 15:03:33 +000064 (fGrContext, SkBitmap::kARGB_8888_Config,
keyar@chromium.orga474ce32012-08-20 15:03:57 +000065 width, height)));
66 return SkNEW_ARGS(SkCanvas, (device.get()));
keyar@chromium.org4ea96c52012-08-20 15:03:29 +000067 break;
68 }
69#endif
70 default:
71 SkASSERT(0);
72 }
keyar@chromium.orga474ce32012-08-20 15:03:57 +000073
74 return NULL;
keyar@chromium.org9d696c02012-08-07 17:11:33 +000075}
76
77void PictureRenderer::end() {
keyar@chromium.org77a55222012-08-20 15:03:47 +000078 this->resetState();
keyar@chromium.org9d696c02012-08-07 17:11:33 +000079 fPicture = NULL;
80 fCanvas.reset(NULL);
81}
82
keyar@chromium.org77a55222012-08-20 15:03:47 +000083void PictureRenderer::resetState() {
keyar@chromium.org28136b32012-08-20 15:04:15 +000084#if SK_SUPPORT_GPU
85 if (this->isUsingGpuDevice()) {
86 SkGLContext* glContext = fGrContextFactory.getGLContext(
87 GrContextFactory::kNative_GLContextType);
88 SK_GL(*glContext, Finish());
89 }
90#endif
91}
92
93void PictureRenderer::finishDraw() {
keyar@chromium.orgbffacc02012-08-20 15:04:07 +000094 SkASSERT(fCanvas.get() != NULL);
keyar@chromium.org28136b32012-08-20 15:04:15 +000095 if (NULL == fCanvas.get()) {
keyar@chromium.orgbffacc02012-08-20 15:04:07 +000096 return;
97 }
98
keyar@chromium.org77a55222012-08-20 15:03:47 +000099 fCanvas->flush();
100
keyar@chromium.orga40c20d2012-08-20 15:04:12 +0000101#if SK_SUPPORT_GPU
keyar@chromium.org77a55222012-08-20 15:03:47 +0000102 if (this->isUsingGpuDevice()) {
103 SkGLContext* glContext = fGrContextFactory.getGLContext(
104 GrContextFactory::kNative_GLContextType);
keyar@chromium.org28136b32012-08-20 15:04:15 +0000105
106 SkASSERT(glContext != NULL);
107 if (NULL == glContext) {
108 return;
109 }
110
keyar@chromium.org77a55222012-08-20 15:03:47 +0000111 SK_GL(*glContext, Finish());
keyar@chromium.org77a55222012-08-20 15:03:47 +0000112 }
keyar@chromium.orga40c20d2012-08-20 15:04:12 +0000113#endif
keyar@chromium.org77a55222012-08-20 15:03:47 +0000114}
115
keyar@chromium.org9299ede2012-08-21 19:05:08 +0000116bool PictureRenderer::write(const SkString& path) const {
117 SkASSERT(fCanvas.get() != NULL);
118 SkASSERT(fPicture != NULL);
119 if (NULL == fCanvas.get() || NULL == fPicture) {
120 return false;
121 }
122
123 SkBitmap bitmap;
124 sk_tools::setup_bitmap(&bitmap, fPicture->width(), fPicture->height());
125
126 fCanvas->readPixels(&bitmap, 0, 0);
127 sk_tools::force_all_opaque(bitmap);
128
129 return SkImageEncoder::EncodeFile(path.c_str(), bitmap, SkImageEncoder::kPNG_Type, 100);
130}
131
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000132void PipePictureRenderer::render() {
133 SkASSERT(fCanvas.get() != NULL);
134 SkASSERT(fPicture != NULL);
keyar@chromium.org78a35c52012-08-20 15:03:44 +0000135 if (NULL == fCanvas.get() || NULL == fPicture) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000136 return;
137 }
138
139 PipeController pipeController(fCanvas.get());
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000140 SkGPipeWriter writer;
141 SkCanvas* pipeCanvas = writer.startRecording(&pipeController);
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000142 pipeCanvas->drawPicture(*fPicture);
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000143 writer.endRecording();
keyar@chromium.org28136b32012-08-20 15:04:15 +0000144 this->finishDraw();
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000145}
146
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000147void SimplePictureRenderer::render() {
148 SkASSERT(fCanvas.get() != NULL);
149 SkASSERT(fPicture != NULL);
keyar@chromium.org78a35c52012-08-20 15:03:44 +0000150 if (NULL == fCanvas.get() || NULL == fPicture) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000151 return;
152 }
153
154 fCanvas->drawPicture(*fPicture);
keyar@chromium.org28136b32012-08-20 15:04:15 +0000155 this->finishDraw();
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000156}
157
158TiledPictureRenderer::TiledPictureRenderer()
159 : fTileWidth(kDefaultTileWidth)
160 , fTileHeight(kDefaultTileHeight) {}
161
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000162void TiledPictureRenderer::init(SkPicture* pict) {
163 SkASSERT(pict != NULL);
keyar@chromium.org78a35c52012-08-20 15:03:44 +0000164 SkASSERT(0 == fTiles.count());
165 if (NULL == pict || fTiles.count() != 0) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000166 return;
167 }
168
169 this->INHERITED::init(pict);
keyar@chromium.orgcc6e5ef2012-07-27 20:09:26 +0000170
171 if (fTileWidthPercentage > 0) {
robertphillips@google.com5d8d1862012-08-15 14:36:41 +0000172 fTileWidth = sk_float_ceil2int(float(fTileWidthPercentage * fPicture->width() / 100));
keyar@chromium.orgcc6e5ef2012-07-27 20:09:26 +0000173 }
174 if (fTileHeightPercentage > 0) {
robertphillips@google.com5d8d1862012-08-15 14:36:41 +0000175 fTileHeight = sk_float_ceil2int(float(fTileHeightPercentage * fPicture->height() / 100));
keyar@chromium.orgcc6e5ef2012-07-27 20:09:26 +0000176 }
177
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000178 if (fTileMinPowerOf2Width > 0) {
179 this->setupPowerOf2Tiles();
180 } else {
181 this->setupTiles();
182 }
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000183}
184
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000185void TiledPictureRenderer::render() {
186 SkASSERT(fCanvas.get() != NULL);
187 SkASSERT(fPicture != NULL);
keyar@chromium.org78a35c52012-08-20 15:03:44 +0000188 if (NULL == fCanvas.get() || NULL == fPicture) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000189 return;
190 }
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000191
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000192 this->drawTiles();
193 this->copyTilesToCanvas();
keyar@chromium.org28136b32012-08-20 15:04:15 +0000194 this->finishDraw();
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000195}
196
197void TiledPictureRenderer::end() {
198 this->deleteTiles();
199 this->INHERITED::end();
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000200}
201
202TiledPictureRenderer::~TiledPictureRenderer() {
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000203 this->deleteTiles();
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000204}
205
keyar@chromium.orgda652c22012-08-20 22:04:07 +0000206void TiledPictureRenderer::clipTile(SkCanvas* tile) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000207 SkRect clip = SkRect::MakeWH(SkIntToScalar(fPicture->width()),
208 SkIntToScalar(fPicture->height()));
keyar@chromium.orgda652c22012-08-20 22:04:07 +0000209 tile->clipRect(clip);
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000210}
211
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000212void TiledPictureRenderer::addTile(int tile_x_start, int tile_y_start, int width, int height) {
213 SkCanvas* tile = this->setupCanvas(width, height);
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000214
keyar@chromium.orgda652c22012-08-20 22:04:07 +0000215 tile->translate(SkIntToScalar(-tile_x_start), SkIntToScalar(-tile_y_start));
216 this->clipTile(tile);
217
218 fTiles.push(tile);
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000219}
220
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000221void TiledPictureRenderer::setupTiles() {
222 for (int tile_y_start = 0; tile_y_start < fPicture->height();
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000223 tile_y_start += fTileHeight) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000224 for (int tile_x_start = 0; tile_x_start < fPicture->width();
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000225 tile_x_start += fTileWidth) {
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000226 this->addTile(tile_x_start, tile_y_start, fTileWidth, fTileHeight);
227 }
228 }
229}
230
231// The goal of the powers of two tiles is to minimize the amount of wasted tile
232// space in the width-wise direction and then minimize the number of tiles. The
233// constraints are that every tile must have a pixel width that is a power of
234// two and also be of some minimal width (that is also a power of two).
235//
236// This is sovled by first taking our picture size and rounding it up to the
237// multiple of the minimal width. The binary representation of this rounded
238// value gives us the tiles we need: a bit of value one means we need a tile of
239// that size.
240void TiledPictureRenderer::setupPowerOf2Tiles() {
241 int rounded_value = fPicture->width();
242 if (fPicture->width() % fTileMinPowerOf2Width != 0) {
243 rounded_value = fPicture->width() - (fPicture->width() % fTileMinPowerOf2Width)
244 + fTileMinPowerOf2Width;
245 }
246
247 int num_bits = SkScalarCeilToInt(SkScalarLog2(fPicture->width()));
248 int largest_possible_tile_size = 1 << num_bits;
249
250 // The tile height is constant for a particular picture.
251 for (int tile_y_start = 0; tile_y_start < fPicture->height(); tile_y_start += fTileHeight) {
252 int tile_x_start = 0;
253 int current_width = largest_possible_tile_size;
254
255 while (current_width >= fTileMinPowerOf2Width) {
256 // It is very important this is a bitwise AND.
257 if (current_width & rounded_value) {
258 this->addTile(tile_x_start, tile_y_start, current_width, fTileHeight);
259 tile_x_start += current_width;
260 }
261
262 current_width >>= 1;
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000263 }
264 }
265}
266
267void TiledPictureRenderer::deleteTiles() {
268 for (int i = 0; i < fTiles.count(); ++i) {
keyar@chromium.orgda652c22012-08-20 22:04:07 +0000269 SkDELETE(fTiles[i]);
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000270 }
271
272 fTiles.reset();
273}
274
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000275void TiledPictureRenderer::drawTiles() {
keyar@chromium.org163b5672012-08-01 17:53:29 +0000276 for (int i = 0; i < fTiles.count(); ++i) {
keyar@chromium.orgda652c22012-08-20 22:04:07 +0000277 fTiles[i]->drawPicture(*(fPicture));
keyar@chromium.org163b5672012-08-01 17:53:29 +0000278 }
279}
280
keyar@chromium.org28136b32012-08-20 15:04:15 +0000281void TiledPictureRenderer::finishDraw() {
keyar@chromium.org275be532012-08-20 15:04:00 +0000282 for (int i = 0; i < fTiles.count(); ++i) {
keyar@chromium.orgda652c22012-08-20 22:04:07 +0000283 fTiles[i]->flush();
keyar@chromium.org275be532012-08-20 15:04:00 +0000284 }
285
keyar@chromium.org28136b32012-08-20 15:04:15 +0000286#if SK_SUPPORT_GPU
287 if (this->isUsingGpuDevice()) {
288 SkGLContext* glContext = fGrContextFactory.getGLContext(
289 GrContextFactory::kNative_GLContextType);
290
291 SkASSERT(glContext != NULL);
292 if (NULL == glContext) {
293 return;
294 }
295
296 SK_GL(*glContext, Finish());
297 }
298#endif
keyar@chromium.org275be532012-08-20 15:04:00 +0000299}
300
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000301void TiledPictureRenderer::copyTilesToCanvas() {
keyar@chromium.orgea826952012-08-23 15:24:13 +0000302 for (int i = 0; i < fTiles.count(); ++i) {
303 // Since SkPicture performs a save and restore when being drawn to a
304 // canvas, we can be confident that the transform matrix of the canvas
305 // is what we set when creating the tiles.
306 SkMatrix matrix = fTiles[i]->getTotalMatrix();
307 SkScalar tile_x_start = matrix.getTranslateX();
308 SkScalar tile_y_start = matrix.getTranslateY();
309
310 SkBitmap source = fTiles[i]->getDevice()->accessBitmap(false);
311
312 fCanvas->drawBitmap(source, -tile_x_start, -tile_y_start);
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000313 }
314}
315
316}