blob: b7958e2d2e63d92c63d8636a7cb062043fd2daab [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)
rileya@google.comb947b912012-08-29 17:35:07 +0000160 , fTileHeight(kDefaultTileHeight)
161 , fTileMinPowerOf2Width(0)
162 , fTileHeightPercentage(0.0)
163 , fTileWidthPercentage(0.0) {}
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000164
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000165void TiledPictureRenderer::init(SkPicture* pict) {
166 SkASSERT(pict != NULL);
keyar@chromium.org78a35c52012-08-20 15:03:44 +0000167 SkASSERT(0 == fTiles.count());
168 if (NULL == pict || fTiles.count() != 0) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000169 return;
170 }
171
172 this->INHERITED::init(pict);
keyar@chromium.orgcc6e5ef2012-07-27 20:09:26 +0000173
174 if (fTileWidthPercentage > 0) {
robertphillips@google.com5d8d1862012-08-15 14:36:41 +0000175 fTileWidth = sk_float_ceil2int(float(fTileWidthPercentage * fPicture->width() / 100));
keyar@chromium.orgcc6e5ef2012-07-27 20:09:26 +0000176 }
177 if (fTileHeightPercentage > 0) {
robertphillips@google.com5d8d1862012-08-15 14:36:41 +0000178 fTileHeight = sk_float_ceil2int(float(fTileHeightPercentage * fPicture->height() / 100));
keyar@chromium.orgcc6e5ef2012-07-27 20:09:26 +0000179 }
180
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000181 if (fTileMinPowerOf2Width > 0) {
182 this->setupPowerOf2Tiles();
183 } else {
184 this->setupTiles();
185 }
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000186}
187
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000188void TiledPictureRenderer::render() {
189 SkASSERT(fCanvas.get() != NULL);
190 SkASSERT(fPicture != NULL);
keyar@chromium.org78a35c52012-08-20 15:03:44 +0000191 if (NULL == fCanvas.get() || NULL == fPicture) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000192 return;
193 }
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000194
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000195 this->drawTiles();
196 this->copyTilesToCanvas();
keyar@chromium.org28136b32012-08-20 15:04:15 +0000197 this->finishDraw();
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000198}
199
200void TiledPictureRenderer::end() {
201 this->deleteTiles();
202 this->INHERITED::end();
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000203}
204
205TiledPictureRenderer::~TiledPictureRenderer() {
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000206 this->deleteTiles();
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000207}
208
keyar@chromium.orgda652c22012-08-20 22:04:07 +0000209void TiledPictureRenderer::clipTile(SkCanvas* tile) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000210 SkRect clip = SkRect::MakeWH(SkIntToScalar(fPicture->width()),
211 SkIntToScalar(fPicture->height()));
keyar@chromium.orgda652c22012-08-20 22:04:07 +0000212 tile->clipRect(clip);
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000213}
214
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000215void TiledPictureRenderer::addTile(int tile_x_start, int tile_y_start, int width, int height) {
216 SkCanvas* tile = this->setupCanvas(width, height);
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000217
keyar@chromium.orgda652c22012-08-20 22:04:07 +0000218 tile->translate(SkIntToScalar(-tile_x_start), SkIntToScalar(-tile_y_start));
219 this->clipTile(tile);
220
221 fTiles.push(tile);
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000222}
223
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000224void TiledPictureRenderer::setupTiles() {
225 for (int tile_y_start = 0; tile_y_start < fPicture->height();
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000226 tile_y_start += fTileHeight) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000227 for (int tile_x_start = 0; tile_x_start < fPicture->width();
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000228 tile_x_start += fTileWidth) {
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000229 this->addTile(tile_x_start, tile_y_start, fTileWidth, fTileHeight);
230 }
231 }
232}
233
234// The goal of the powers of two tiles is to minimize the amount of wasted tile
235// space in the width-wise direction and then minimize the number of tiles. The
236// constraints are that every tile must have a pixel width that is a power of
237// two and also be of some minimal width (that is also a power of two).
238//
239// This is sovled by first taking our picture size and rounding it up to the
240// multiple of the minimal width. The binary representation of this rounded
241// value gives us the tiles we need: a bit of value one means we need a tile of
242// that size.
243void TiledPictureRenderer::setupPowerOf2Tiles() {
244 int rounded_value = fPicture->width();
245 if (fPicture->width() % fTileMinPowerOf2Width != 0) {
246 rounded_value = fPicture->width() - (fPicture->width() % fTileMinPowerOf2Width)
247 + fTileMinPowerOf2Width;
248 }
249
250 int num_bits = SkScalarCeilToInt(SkScalarLog2(fPicture->width()));
251 int largest_possible_tile_size = 1 << num_bits;
252
253 // The tile height is constant for a particular picture.
254 for (int tile_y_start = 0; tile_y_start < fPicture->height(); tile_y_start += fTileHeight) {
255 int tile_x_start = 0;
256 int current_width = largest_possible_tile_size;
257
258 while (current_width >= fTileMinPowerOf2Width) {
259 // It is very important this is a bitwise AND.
260 if (current_width & rounded_value) {
261 this->addTile(tile_x_start, tile_y_start, current_width, fTileHeight);
262 tile_x_start += current_width;
263 }
264
265 current_width >>= 1;
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000266 }
267 }
268}
269
270void TiledPictureRenderer::deleteTiles() {
271 for (int i = 0; i < fTiles.count(); ++i) {
keyar@chromium.orgda652c22012-08-20 22:04:07 +0000272 SkDELETE(fTiles[i]);
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000273 }
274
275 fTiles.reset();
276}
277
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000278void TiledPictureRenderer::drawTiles() {
keyar@chromium.org163b5672012-08-01 17:53:29 +0000279 for (int i = 0; i < fTiles.count(); ++i) {
keyar@chromium.orgda652c22012-08-20 22:04:07 +0000280 fTiles[i]->drawPicture(*(fPicture));
keyar@chromium.org163b5672012-08-01 17:53:29 +0000281 }
282}
283
keyar@chromium.org28136b32012-08-20 15:04:15 +0000284void TiledPictureRenderer::finishDraw() {
keyar@chromium.org275be532012-08-20 15:04:00 +0000285 for (int i = 0; i < fTiles.count(); ++i) {
keyar@chromium.orgda652c22012-08-20 22:04:07 +0000286 fTiles[i]->flush();
keyar@chromium.org275be532012-08-20 15:04:00 +0000287 }
288
keyar@chromium.org28136b32012-08-20 15:04:15 +0000289#if SK_SUPPORT_GPU
290 if (this->isUsingGpuDevice()) {
291 SkGLContext* glContext = fGrContextFactory.getGLContext(
292 GrContextFactory::kNative_GLContextType);
293
294 SkASSERT(glContext != NULL);
295 if (NULL == glContext) {
296 return;
297 }
298
299 SK_GL(*glContext, Finish());
300 }
301#endif
keyar@chromium.org275be532012-08-20 15:04:00 +0000302}
303
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000304void TiledPictureRenderer::copyTilesToCanvas() {
keyar@chromium.orgea826952012-08-23 15:24:13 +0000305 for (int i = 0; i < fTiles.count(); ++i) {
306 // Since SkPicture performs a save and restore when being drawn to a
307 // canvas, we can be confident that the transform matrix of the canvas
308 // is what we set when creating the tiles.
309 SkMatrix matrix = fTiles[i]->getTotalMatrix();
310 SkScalar tile_x_start = matrix.getTranslateX();
311 SkScalar tile_y_start = matrix.getTranslateY();
312
313 SkBitmap source = fTiles[i]->getDevice()->accessBitmap(false);
314
315 fCanvas->drawBitmap(source, -tile_x_start, -tile_y_start);
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000316 }
317}
318
319}