blob: 32c19e87709aa92af31f80bdd5e4ed90e24dc23f [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"
caryclark@google.coma3622372012-11-06 21:26:13 +000019#include "SkMaskFilter.h"
keyar@chromium.orgea826952012-08-23 15:24:13 +000020#include "SkMatrix.h"
keyar@chromium.org451bb9f2012-07-26 17:27:57 +000021#include "SkPicture.h"
junov@chromium.org9313ca42012-11-02 18:11:49 +000022#include "SkRTree.h"
keyar@chromium.orgea826952012-08-23 15:24:13 +000023#include "SkScalar.h"
scroggo@google.coma9e3a362012-11-07 17:52:48 +000024#include "SkStream.h"
keyar@chromium.org9299ede2012-08-21 19:05:08 +000025#include "SkString.h"
scroggo@google.com58b4ead2012-08-31 16:15:22 +000026#include "SkTemplates.h"
junov@chromium.org3cb834b2012-12-13 16:39:53 +000027#include "SkTileGridPicture.h"
keyar@chromium.org451bb9f2012-07-26 17:27:57 +000028#include "SkTDArray.h"
scroggo@google.com58b4ead2012-08-31 16:15:22 +000029#include "SkThreadUtils.h"
keyar@chromium.org451bb9f2012-07-26 17:27:57 +000030#include "SkTypes.h"
reed@google.comfe7b1ed2012-11-29 21:00:39 +000031#include "SkData.h"
32#include "SkPictureUtils.h"
keyar@chromium.org4ea96c52012-08-20 15:03:29 +000033
keyar@chromium.org451bb9f2012-07-26 17:27:57 +000034namespace sk_tools {
35
36enum {
37 kDefaultTileWidth = 256,
38 kDefaultTileHeight = 256
39};
40
keyar@chromium.org9d696c02012-08-07 17:11:33 +000041void PictureRenderer::init(SkPicture* pict) {
keyar@chromium.org78a35c52012-08-20 15:03:44 +000042 SkASSERT(NULL == fPicture);
43 SkASSERT(NULL == fCanvas.get());
44 if (fPicture != NULL || NULL != fCanvas.get()) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +000045 return;
46 }
47
48 SkASSERT(pict != NULL);
keyar@chromium.org78a35c52012-08-20 15:03:44 +000049 if (NULL == pict) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +000050 return;
51 }
52
53 fPicture = pict;
junov@chromium.org9313ca42012-11-02 18:11:49 +000054 fPicture->ref();
keyar@chromium.orga474ce32012-08-20 15:03:57 +000055 fCanvas.reset(this->setupCanvas());
56}
57
caryclark@google.coma3622372012-11-06 21:26:13 +000058class FlagsDrawFilter : public SkDrawFilter {
59public:
60 FlagsDrawFilter(PictureRenderer::DrawFilterFlags* flags) :
61 fFlags(flags) {}
62
reed@google.com971aca72012-11-26 20:26:54 +000063 virtual bool filter(SkPaint* paint, Type t) {
caryclark@google.coma3622372012-11-06 21:26:13 +000064 paint->setFlags(paint->getFlags() & ~fFlags[t] & SkPaint::kAllFlags);
caryclark@google.come3e940c2012-11-07 16:42:17 +000065 if ((PictureRenderer::kBlur_DrawFilterFlag | PictureRenderer::kLowBlur_DrawFilterFlag)
66 & fFlags[t]) {
caryclark@google.coma3622372012-11-06 21:26:13 +000067 SkMaskFilter* maskFilter = paint->getMaskFilter();
68 SkMaskFilter::BlurInfo blurInfo;
69 if (maskFilter && maskFilter->asABlur(&blurInfo)) {
caryclark@google.come3e940c2012-11-07 16:42:17 +000070 if (PictureRenderer::kBlur_DrawFilterFlag & fFlags[t]) {
71 paint->setMaskFilter(NULL);
72 } else {
73 blurInfo.fHighQuality = false;
74 maskFilter->setAsABlur(blurInfo);
75 }
caryclark@google.coma3622372012-11-06 21:26:13 +000076 }
77 }
78 if (PictureRenderer::kHinting_DrawFilterFlag & fFlags[t]) {
79 paint->setHinting(SkPaint::kNo_Hinting);
80 } else if (PictureRenderer::kSlightHinting_DrawFilterFlag & fFlags[t]) {
81 paint->setHinting(SkPaint::kSlight_Hinting);
82 }
reed@google.com971aca72012-11-26 20:26:54 +000083 return true;
caryclark@google.coma3622372012-11-06 21:26:13 +000084 }
85
86private:
87 PictureRenderer::DrawFilterFlags* fFlags;
88};
89
90static SkCanvas* setUpFilter(SkCanvas* canvas, PictureRenderer::DrawFilterFlags* drawFilters) {
91 if (drawFilters && !canvas->getDrawFilter()) {
92 canvas->setDrawFilter(SkNEW_ARGS(FlagsDrawFilter, (drawFilters)))->unref();
caryclark@google.come3e940c2012-11-07 16:42:17 +000093 if (drawFilters[0] & PictureRenderer::kAAClip_DrawFilterFlag) {
94 canvas->setAllowSoftClip(false);
95 }
caryclark@google.coma3622372012-11-06 21:26:13 +000096 }
97 return canvas;
98}
99
keyar@chromium.orga474ce32012-08-20 15:03:57 +0000100SkCanvas* PictureRenderer::setupCanvas() {
scroggo@google.comc0d5e542012-12-13 21:40:48 +0000101 const int width = this->getViewWidth();
102 const int height = this->getViewHeight();
103 return this->setupCanvas(width, height);
keyar@chromium.orga474ce32012-08-20 15:03:57 +0000104}
105
106SkCanvas* PictureRenderer::setupCanvas(int width, int height) {
caryclark@google.coma3622372012-11-06 21:26:13 +0000107 SkCanvas* canvas;
keyar@chromium.org4ea96c52012-08-20 15:03:29 +0000108 switch(fDeviceType) {
109 case kBitmap_DeviceType: {
110 SkBitmap bitmap;
keyar@chromium.orga474ce32012-08-20 15:03:57 +0000111 sk_tools::setup_bitmap(&bitmap, width, height);
caryclark@google.coma3622372012-11-06 21:26:13 +0000112 canvas = SkNEW_ARGS(SkCanvas, (bitmap));
113 return setUpFilter(canvas, fDrawFilters);
keyar@chromium.org4ea96c52012-08-20 15:03:29 +0000114 }
115#if SK_SUPPORT_GPU
116 case kGPU_DeviceType: {
keyar@chromium.org4ea96c52012-08-20 15:03:29 +0000117 SkAutoTUnref<SkGpuDevice> device(SkNEW_ARGS(SkGpuDevice,
keyar@chromium.org06125642012-08-20 15:03:33 +0000118 (fGrContext, SkBitmap::kARGB_8888_Config,
keyar@chromium.orga474ce32012-08-20 15:03:57 +0000119 width, height)));
caryclark@google.coma3622372012-11-06 21:26:13 +0000120 canvas = SkNEW_ARGS(SkCanvas, (device.get()));
121 return setUpFilter(canvas, fDrawFilters);
keyar@chromium.org4ea96c52012-08-20 15:03:29 +0000122 }
123#endif
124 default:
125 SkASSERT(0);
126 }
keyar@chromium.orga474ce32012-08-20 15:03:57 +0000127
128 return NULL;
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000129}
130
131void PictureRenderer::end() {
keyar@chromium.org77a55222012-08-20 15:03:47 +0000132 this->resetState();
junov@chromium.org9313ca42012-11-02 18:11:49 +0000133 SkSafeUnref(fPicture);
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000134 fPicture = NULL;
135 fCanvas.reset(NULL);
136}
137
scroggo@google.comc0d5e542012-12-13 21:40:48 +0000138int PictureRenderer::getViewWidth() {
139 SkASSERT(fPicture != NULL);
140 int width = fPicture->width();
141 if (fViewport.width() > 0) {
142 width = SkMin32(width, fViewport.width());
143 }
144 return width;
145}
146
147int PictureRenderer::getViewHeight() {
148 SkASSERT(fPicture != NULL);
149 int height = fPicture->height();
150 if (fViewport.height() > 0) {
151 height = SkMin32(height, fViewport.height());
152 }
153 return height;
154}
155
junov@chromium.org9313ca42012-11-02 18:11:49 +0000156/** Converts fPicture to a picture that uses a BBoxHierarchy.
157 * PictureRenderer subclasses that are used to test picture playback
158 * should call this method during init.
159 */
160void PictureRenderer::buildBBoxHierarchy() {
161 SkASSERT(NULL != fPicture);
162 if (kNone_BBoxHierarchyType != fBBoxHierarchyType && NULL != fPicture) {
163 SkPicture* newPicture = this->createPicture();
164 SkCanvas* recorder = newPicture->beginRecording(fPicture->width(), fPicture->height(),
165 this->recordFlags());
166 fPicture->draw(recorder);
167 newPicture->endRecording();
168 fPicture->unref();
169 fPicture = newPicture;
170 }
171}
172
keyar@chromium.org77a55222012-08-20 15:03:47 +0000173void PictureRenderer::resetState() {
keyar@chromium.org28136b32012-08-20 15:04:15 +0000174#if SK_SUPPORT_GPU
175 if (this->isUsingGpuDevice()) {
176 SkGLContext* glContext = fGrContextFactory.getGLContext(
177 GrContextFactory::kNative_GLContextType);
keyar@chromium.org28136b32012-08-20 15:04:15 +0000178
179 SkASSERT(glContext != NULL);
180 if (NULL == glContext) {
181 return;
182 }
183
scroggo@google.com9a412522012-09-07 15:21:18 +0000184 fGrContext->flush();
keyar@chromium.org77a55222012-08-20 15:03:47 +0000185 SK_GL(*glContext, Finish());
keyar@chromium.org77a55222012-08-20 15:03:47 +0000186 }
keyar@chromium.orga40c20d2012-08-20 15:04:12 +0000187#endif
keyar@chromium.org77a55222012-08-20 15:03:47 +0000188}
189
junov@chromium.org9313ca42012-11-02 18:11:49 +0000190uint32_t PictureRenderer::recordFlags() {
191 return kNone_BBoxHierarchyType == fBBoxHierarchyType ? 0 :
192 SkPicture::kOptimizeForClippedPlayback_RecordingFlag;
193}
194
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000195/**
196 * Write the canvas to the specified path.
197 * @param canvas Must be non-null. Canvas to be written to a file.
198 * @param path Path for the file to be written. Should have no extension; write() will append
199 * an appropriate one. Passed in by value so it can be modified.
200 * @return bool True if the Canvas is written to a file.
201 */
202static bool write(SkCanvas* canvas, SkString path) {
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000203 SkASSERT(canvas != NULL);
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000204 if (NULL == canvas) {
keyar@chromium.org9299ede2012-08-21 19:05:08 +0000205 return false;
206 }
207
208 SkBitmap bitmap;
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000209 SkISize size = canvas->getDeviceSize();
210 sk_tools::setup_bitmap(&bitmap, size.width(), size.height());
keyar@chromium.org9299ede2012-08-21 19:05:08 +0000211
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000212 canvas->readPixels(&bitmap, 0, 0);
keyar@chromium.org9299ede2012-08-21 19:05:08 +0000213 sk_tools::force_all_opaque(bitmap);
214
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000215 // Since path is passed in by value, it is okay to modify it.
216 path.append(".png");
keyar@chromium.org9299ede2012-08-21 19:05:08 +0000217 return SkImageEncoder::EncodeFile(path.c_str(), bitmap, SkImageEncoder::kPNG_Type, 100);
218}
219
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000220/**
221 * If path is non NULL, append number to it, and call write(SkCanvas*, SkString) to write the
222 * provided canvas to a file. Returns true if path is NULL or if write() succeeds.
223 */
224static bool writeAppendNumber(SkCanvas* canvas, const SkString* path, int number) {
225 if (NULL == path) {
226 return true;
227 }
228 SkString pathWithNumber(*path);
229 pathWithNumber.appendf("%i", number);
230 return write(canvas, pathWithNumber);
231}
232
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000233///////////////////////////////////////////////////////////////////////////////////////////////
234
djsollen@google.comfd9720c2012-11-06 16:54:40 +0000235SkCanvas* RecordPictureRenderer::setupCanvas(int width, int height) {
236 // defer the canvas setup until the render step
237 return NULL;
238}
239
scroggo@google.coma9e3a362012-11-07 17:52:48 +0000240static bool PNGEncodeBitmapToStream(SkWStream* wStream, const SkBitmap& bm) {
241 return SkImageEncoder::EncodeStream(wStream, bm, SkImageEncoder::kPNG_Type, 100);
242}
243
244bool RecordPictureRenderer::render(const SkString* path) {
junov@chromium.org9313ca42012-11-02 18:11:49 +0000245 SkAutoTUnref<SkPicture> replayer(this->createPicture());
246 SkCanvas* recorder = replayer->beginRecording(fPicture->width(), fPicture->height(),
247 this->recordFlags());
scroggo@google.com9a412522012-09-07 15:21:18 +0000248 fPicture->draw(recorder);
junov@chromium.org9313ca42012-11-02 18:11:49 +0000249 replayer->endRecording();
scroggo@google.coma9e3a362012-11-07 17:52:48 +0000250 if (path != NULL) {
251 // Record the new picture as a new SKP with PNG encoded bitmaps.
252 SkString skpPath(*path);
253 // ".skp" was removed from 'path' before being passed in here.
254 skpPath.append(".skp");
255 SkFILEWStream stream(skpPath.c_str());
256 replayer->serialize(&stream, &PNGEncodeBitmapToStream);
257 return true;
258 }
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000259 return false;
scroggo@google.com9a412522012-09-07 15:21:18 +0000260}
261
scroggo@google.com0a049b82012-11-02 22:01:26 +0000262SkString RecordPictureRenderer::getConfigNameInternal() {
263 return SkString("record");
264}
265
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000266///////////////////////////////////////////////////////////////////////////////////////////////
267
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000268bool PipePictureRenderer::render(const SkString* path) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000269 SkASSERT(fCanvas.get() != NULL);
270 SkASSERT(fPicture != NULL);
keyar@chromium.org78a35c52012-08-20 15:03:44 +0000271 if (NULL == fCanvas.get() || NULL == fPicture) {
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000272 return false;
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000273 }
274
275 PipeController pipeController(fCanvas.get());
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000276 SkGPipeWriter writer;
277 SkCanvas* pipeCanvas = writer.startRecording(&pipeController);
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000278 pipeCanvas->drawPicture(*fPicture);
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000279 writer.endRecording();
scroggo@google.com9a412522012-09-07 15:21:18 +0000280 fCanvas->flush();
borenet@google.com070d3542012-10-26 13:26:55 +0000281 if (NULL != path) {
282 return write(fCanvas, *path);
283 }
284 return true;
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000285}
286
scroggo@google.com0a049b82012-11-02 22:01:26 +0000287SkString PipePictureRenderer::getConfigNameInternal() {
288 return SkString("pipe");
289}
290
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000291///////////////////////////////////////////////////////////////////////////////////////////////
292
junov@chromium.org9313ca42012-11-02 18:11:49 +0000293void SimplePictureRenderer::init(SkPicture* picture) {
294 INHERITED::init(picture);
295 this->buildBBoxHierarchy();
296}
297
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000298bool SimplePictureRenderer::render(const SkString* path) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000299 SkASSERT(fCanvas.get() != NULL);
300 SkASSERT(fPicture != NULL);
keyar@chromium.org78a35c52012-08-20 15:03:44 +0000301 if (NULL == fCanvas.get() || NULL == fPicture) {
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000302 return false;
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000303 }
304
305 fCanvas->drawPicture(*fPicture);
scroggo@google.com9a412522012-09-07 15:21:18 +0000306 fCanvas->flush();
borenet@google.com070d3542012-10-26 13:26:55 +0000307 if (NULL != path) {
308 return write(fCanvas, *path);
309 }
310 return true;
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000311}
312
scroggo@google.com0a049b82012-11-02 22:01:26 +0000313SkString SimplePictureRenderer::getConfigNameInternal() {
314 return SkString("simple");
315}
316
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000317///////////////////////////////////////////////////////////////////////////////////////////////
318
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000319TiledPictureRenderer::TiledPictureRenderer()
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000320 : fTileWidth(kDefaultTileWidth)
rileya@google.comb947b912012-08-29 17:35:07 +0000321 , fTileHeight(kDefaultTileHeight)
rileya@google.coma04dc022012-09-10 19:01:38 +0000322 , fTileWidthPercentage(0.0)
rileya@google.comb947b912012-08-29 17:35:07 +0000323 , fTileHeightPercentage(0.0)
scroggo@google.comcbcef702012-12-13 22:09:28 +0000324 , fTileMinPowerOf2Width(0)
325 , fCurrentTileOffset(-1)
326 , fTilesX(0)
327 , fTilesY(0) { }
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000328
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000329void TiledPictureRenderer::init(SkPicture* pict) {
330 SkASSERT(pict != NULL);
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000331 SkASSERT(0 == fTileRects.count());
332 if (NULL == pict || fTileRects.count() != 0) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000333 return;
334 }
335
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000336 // Do not call INHERITED::init(), which would create a (potentially large) canvas which is not
337 // used by bench_pictures.
338 fPicture = pict;
junov@chromium.org9313ca42012-11-02 18:11:49 +0000339 fPicture->ref();
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000340 this->buildBBoxHierarchy();
keyar@chromium.orgcc6e5ef2012-07-27 20:09:26 +0000341
342 if (fTileWidthPercentage > 0) {
robertphillips@google.com5d8d1862012-08-15 14:36:41 +0000343 fTileWidth = sk_float_ceil2int(float(fTileWidthPercentage * fPicture->width() / 100));
keyar@chromium.orgcc6e5ef2012-07-27 20:09:26 +0000344 }
345 if (fTileHeightPercentage > 0) {
robertphillips@google.com5d8d1862012-08-15 14:36:41 +0000346 fTileHeight = sk_float_ceil2int(float(fTileHeightPercentage * fPicture->height() / 100));
keyar@chromium.orgcc6e5ef2012-07-27 20:09:26 +0000347 }
348
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000349 if (fTileMinPowerOf2Width > 0) {
350 this->setupPowerOf2Tiles();
351 } else {
352 this->setupTiles();
353 }
scroggo@google.comcbcef702012-12-13 22:09:28 +0000354 fCanvas.reset(this->setupCanvas(fTileWidth, fTileHeight));
355 // Initialize to -1 so that the first call to nextTile will set this up to draw tile 0 on the
356 // first call to drawCurrentTile.
357 fCurrentTileOffset = -1;
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000358}
359
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000360void TiledPictureRenderer::end() {
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000361 fTileRects.reset();
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000362 this->INHERITED::end();
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000363}
364
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000365void TiledPictureRenderer::setupTiles() {
scroggo@google.comc0d5e542012-12-13 21:40:48 +0000366 // Only use enough tiles to cover the viewport
367 const int width = this->getViewWidth();
368 const int height = this->getViewHeight();
369
scroggo@google.comcbcef702012-12-13 22:09:28 +0000370 fTilesX = fTilesY = 0;
scroggo@google.comc0d5e542012-12-13 21:40:48 +0000371 for (int tile_y_start = 0; tile_y_start < height; tile_y_start += fTileHeight) {
scroggo@google.comcbcef702012-12-13 22:09:28 +0000372 fTilesY++;
scroggo@google.comc0d5e542012-12-13 21:40:48 +0000373 for (int tile_x_start = 0; tile_x_start < width; tile_x_start += fTileWidth) {
scroggo@google.comcbcef702012-12-13 22:09:28 +0000374 if (0 == tile_y_start) {
375 // Only count tiles in the X direction on the first pass.
376 fTilesX++;
377 }
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000378 *fTileRects.append() = SkRect::MakeXYWH(SkIntToScalar(tile_x_start),
379 SkIntToScalar(tile_y_start),
380 SkIntToScalar(fTileWidth),
381 SkIntToScalar(fTileHeight));
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000382 }
383 }
384}
385
scroggo@google.comcbcef702012-12-13 22:09:28 +0000386bool TiledPictureRenderer::tileDimensions(int &x, int &y) {
387 if (fTileRects.count() == 0 || NULL == fPicture) {
388 return false;
389 }
390 x = fTilesX;
391 y = fTilesY;
392 return true;
393}
394
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000395// The goal of the powers of two tiles is to minimize the amount of wasted tile
396// space in the width-wise direction and then minimize the number of tiles. The
397// constraints are that every tile must have a pixel width that is a power of
398// two and also be of some minimal width (that is also a power of two).
399//
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000400// This is solved by first taking our picture size and rounding it up to the
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000401// multiple of the minimal width. The binary representation of this rounded
402// value gives us the tiles we need: a bit of value one means we need a tile of
403// that size.
404void TiledPictureRenderer::setupPowerOf2Tiles() {
scroggo@google.comc0d5e542012-12-13 21:40:48 +0000405 // Only use enough tiles to cover the viewport
406 const int width = this->getViewWidth();
407 const int height = this->getViewHeight();
408
409 int rounded_value = width;
410 if (width % fTileMinPowerOf2Width != 0) {
411 rounded_value = width - (width % fTileMinPowerOf2Width) + fTileMinPowerOf2Width;
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000412 }
413
scroggo@google.comc0d5e542012-12-13 21:40:48 +0000414 int num_bits = SkScalarCeilToInt(SkScalarLog2(SkIntToScalar(width)));
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000415 int largest_possible_tile_size = 1 << num_bits;
416
scroggo@google.comcbcef702012-12-13 22:09:28 +0000417 fTilesX = fTilesY = 0;
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000418 // The tile height is constant for a particular picture.
scroggo@google.comc0d5e542012-12-13 21:40:48 +0000419 for (int tile_y_start = 0; tile_y_start < height; tile_y_start += fTileHeight) {
scroggo@google.comcbcef702012-12-13 22:09:28 +0000420 fTilesY++;
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000421 int tile_x_start = 0;
422 int current_width = largest_possible_tile_size;
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000423 // Set fTileWidth to be the width of the widest tile, so that each canvas is large enough
424 // to draw each tile.
425 fTileWidth = current_width;
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000426
427 while (current_width >= fTileMinPowerOf2Width) {
428 // It is very important this is a bitwise AND.
429 if (current_width & rounded_value) {
scroggo@google.comcbcef702012-12-13 22:09:28 +0000430 if (0 == tile_y_start) {
431 // Only count tiles in the X direction on the first pass.
432 fTilesX++;
433 }
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000434 *fTileRects.append() = SkRect::MakeXYWH(SkIntToScalar(tile_x_start),
435 SkIntToScalar(tile_y_start),
436 SkIntToScalar(current_width),
437 SkIntToScalar(fTileHeight));
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000438 tile_x_start += current_width;
439 }
440
441 current_width >>= 1;
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000442 }
443 }
444}
445
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000446/**
447 * Draw the specified playback to the canvas translated to rectangle provided, so that this mini
448 * canvas represents the rectangle's portion of the overall picture.
449 * Saves and restores so that the initial clip and matrix return to their state before this function
450 * is called.
451 */
452template<class T>
453static void DrawTileToCanvas(SkCanvas* canvas, const SkRect& tileRect, T* playback) {
454 int saveCount = canvas->save();
455 // Translate so that we draw the correct portion of the picture
456 canvas->translate(-tileRect.fLeft, -tileRect.fTop);
457 playback->draw(canvas);
458 canvas->restoreToCount(saveCount);
459 canvas->flush();
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000460}
461
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000462///////////////////////////////////////////////////////////////////////////////////////////////
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000463
scroggo@google.comcbcef702012-12-13 22:09:28 +0000464bool TiledPictureRenderer::nextTile(int &i, int &j) {
465 if (++fCurrentTileOffset < fTileRects.count()) {
466 i = fCurrentTileOffset % fTilesX;
467 j = fCurrentTileOffset / fTilesX;
468 return true;
469 }
470 return false;
471}
472
473void TiledPictureRenderer::drawCurrentTile() {
474 SkASSERT(fCurrentTileOffset >= 0 && fCurrentTileOffset < fTileRects.count());
475 DrawTileToCanvas(fCanvas, fTileRects[fCurrentTileOffset], fPicture);
476}
477
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000478bool TiledPictureRenderer::render(const SkString* path) {
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000479 SkASSERT(fPicture != NULL);
480 if (NULL == fPicture) {
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000481 return false;
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000482 }
483
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000484 bool success = true;
485 for (int i = 0; i < fTileRects.count(); ++i) {
scroggo@google.comcbcef702012-12-13 22:09:28 +0000486 DrawTileToCanvas(fCanvas, fTileRects[i], fPicture);
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000487 if (NULL != path) {
scroggo@google.comcbcef702012-12-13 22:09:28 +0000488 success &= writeAppendNumber(fCanvas, path, i);
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000489 }
keyar@chromium.org163b5672012-08-01 17:53:29 +0000490 }
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000491 return success;
keyar@chromium.org163b5672012-08-01 17:53:29 +0000492}
493
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000494SkCanvas* TiledPictureRenderer::setupCanvas(int width, int height) {
495 SkCanvas* canvas = this->INHERITED::setupCanvas(width, height);
496 SkASSERT(fPicture != NULL);
scroggo@google.comc0d5e542012-12-13 21:40:48 +0000497 const int totalWidth = this->getViewWidth();
498 const int totalHeight = this->getViewHeight();
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000499 // Clip the tile to an area that is completely in what the SkPicture says is the
500 // drawn-to area. This is mostly important for tiles on the right and bottom edges
501 // as they may go over this area and the picture may have some commands that
502 // draw outside of this area and so should not actually be written.
scroggo@google.comc0d5e542012-12-13 21:40:48 +0000503 SkRect clip = SkRect::MakeWH(SkIntToScalar(totalWidth), SkIntToScalar(totalHeight));
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000504 canvas->clipRect(clip);
505 return canvas;
506}
scroggo@google.com0a049b82012-11-02 22:01:26 +0000507
508SkString TiledPictureRenderer::getConfigNameInternal() {
509 SkString name;
510 if (fTileMinPowerOf2Width > 0) {
511 name.append("pow2tile_");
512 name.appendf("%i", fTileMinPowerOf2Width);
513 } else {
514 name.append("tile_");
515 if (fTileWidthPercentage > 0) {
516 name.appendf("%.f%%", fTileWidthPercentage);
517 } else {
518 name.appendf("%i", fTileWidth);
519 }
520 }
521 name.append("x");
522 if (fTileHeightPercentage > 0) {
523 name.appendf("%.f%%", fTileHeightPercentage);
524 } else {
525 name.appendf("%i", fTileHeight);
526 }
527 return name;
528}
529
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000530///////////////////////////////////////////////////////////////////////////////////////////////
531
532// Holds all of the information needed to draw a set of tiles.
533class CloneData : public SkRunnable {
534
535public:
536 CloneData(SkPicture* clone, SkCanvas* canvas, SkTDArray<SkRect>& rects, int start, int end,
537 SkRunnable* done)
538 : fClone(clone)
539 , fCanvas(canvas)
540 , fPath(NULL)
541 , fRects(rects)
542 , fStart(start)
543 , fEnd(end)
544 , fSuccess(NULL)
545 , fDone(done) {
546 SkASSERT(fDone != NULL);
547 }
548
549 virtual void run() SK_OVERRIDE {
550 SkGraphics::SetTLSFontCacheLimit(1024 * 1024);
551 for (int i = fStart; i < fEnd; i++) {
552 DrawTileToCanvas(fCanvas, fRects[i], fClone);
553 if (fPath != NULL && !writeAppendNumber(fCanvas, fPath, i)
554 && fSuccess != NULL) {
555 *fSuccess = false;
556 // If one tile fails to write to a file, do not continue drawing the rest.
557 break;
558 }
559 }
560 fDone->run();
561 }
562
563 void setPathAndSuccess(const SkString* path, bool* success) {
564 fPath = path;
565 fSuccess = success;
566 }
567
568private:
569 // All pointers unowned.
570 SkPicture* fClone; // Picture to draw from. Each CloneData has a unique one which
571 // is threadsafe.
572 SkCanvas* fCanvas; // Canvas to draw to. Reused for each tile.
573 const SkString* fPath; // If non-null, path to write the result to as a PNG.
574 SkTDArray<SkRect>& fRects; // All tiles of the picture.
575 const int fStart; // Range of tiles drawn by this thread.
576 const int fEnd;
577 bool* fSuccess; // Only meaningful if path is non-null. Shared by all threads,
578 // and only set to false upon failure to write to a PNG.
579 SkRunnable* fDone;
580};
581
582MultiCorePictureRenderer::MultiCorePictureRenderer(int threadCount)
583: fNumThreads(threadCount)
584, fThreadPool(threadCount)
585, fCountdown(threadCount) {
586 // Only need to create fNumThreads - 1 clones, since one thread will use the base
587 // picture.
588 fPictureClones = SkNEW_ARRAY(SkPicture, fNumThreads - 1);
589 fCloneData = SkNEW_ARRAY(CloneData*, fNumThreads);
590}
591
592void MultiCorePictureRenderer::init(SkPicture *pict) {
593 // Set fPicture and the tiles.
594 this->INHERITED::init(pict);
595 for (int i = 0; i < fNumThreads; ++i) {
596 *fCanvasPool.append() = this->setupCanvas(this->getTileWidth(), this->getTileHeight());
597 }
598 // Only need to create fNumThreads - 1 clones, since one thread will use the base picture.
599 fPicture->clone(fPictureClones, fNumThreads - 1);
600 // Populate each thread with the appropriate data.
601 // Group the tiles into nearly equal size chunks, rounding up so we're sure to cover them all.
602 const int chunkSize = (fTileRects.count() + fNumThreads - 1) / fNumThreads;
603
604 for (int i = 0; i < fNumThreads; i++) {
605 SkPicture* pic;
606 if (i == fNumThreads-1) {
607 // The last set will use the original SkPicture.
608 pic = fPicture;
609 } else {
610 pic = &fPictureClones[i];
611 }
612 const int start = i * chunkSize;
613 const int end = SkMin32(start + chunkSize, fTileRects.count());
614 fCloneData[i] = SkNEW_ARGS(CloneData,
615 (pic, fCanvasPool[i], fTileRects, start, end, &fCountdown));
616 }
617}
618
619bool MultiCorePictureRenderer::render(const SkString *path) {
620 bool success = true;
621 if (path != NULL) {
622 for (int i = 0; i < fNumThreads-1; i++) {
623 fCloneData[i]->setPathAndSuccess(path, &success);
624 }
625 }
626
627 fCountdown.reset(fNumThreads);
628 for (int i = 0; i < fNumThreads; i++) {
629 fThreadPool.add(fCloneData[i]);
630 }
631 fCountdown.wait();
632
633 return success;
634}
635
636void MultiCorePictureRenderer::end() {
637 for (int i = 0; i < fNumThreads - 1; i++) {
638 SkDELETE(fCloneData[i]);
639 fCloneData[i] = NULL;
640 }
641
642 fCanvasPool.unrefAll();
643
644 this->INHERITED::end();
645}
646
647MultiCorePictureRenderer::~MultiCorePictureRenderer() {
648 // Each individual CloneData was deleted in end.
649 SkDELETE_ARRAY(fCloneData);
650 SkDELETE_ARRAY(fPictureClones);
651}
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000652
scroggo@google.com0a049b82012-11-02 22:01:26 +0000653SkString MultiCorePictureRenderer::getConfigNameInternal() {
654 SkString name = this->INHERITED::getConfigNameInternal();
655 name.appendf("_multi_%i_threads", fNumThreads);
656 return name;
657}
658
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000659///////////////////////////////////////////////////////////////////////////////////////////////
scroggo@google.com9a412522012-09-07 15:21:18 +0000660
661void PlaybackCreationRenderer::setup() {
junov@chromium.org9313ca42012-11-02 18:11:49 +0000662 fReplayer.reset(this->createPicture());
663 SkCanvas* recorder = fReplayer->beginRecording(fPicture->width(), fPicture->height(),
664 this->recordFlags());
scroggo@google.com9a412522012-09-07 15:21:18 +0000665 fPicture->draw(recorder);
666}
667
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000668bool PlaybackCreationRenderer::render(const SkString*) {
junov@chromium.org9313ca42012-11-02 18:11:49 +0000669 fReplayer->endRecording();
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000670 // Since this class does not actually render, return false.
671 return false;
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000672}
673
scroggo@google.com0a049b82012-11-02 22:01:26 +0000674SkString PlaybackCreationRenderer::getConfigNameInternal() {
675 return SkString("playback_creation");
676}
677
junov@chromium.org9313ca42012-11-02 18:11:49 +0000678///////////////////////////////////////////////////////////////////////////////////////////////
679// SkPicture variants for each BBoxHierarchy type
680
681class RTreePicture : public SkPicture {
682public:
683 virtual SkBBoxHierarchy* createBBoxHierarchy() const SK_OVERRIDE{
684 static const int kRTreeMinChildren = 6;
685 static const int kRTreeMaxChildren = 11;
686 SkScalar aspectRatio = SkScalarDiv(SkIntToScalar(fWidth),
687 SkIntToScalar(fHeight));
688 return SkRTree::Create(kRTreeMinChildren, kRTreeMaxChildren,
689 aspectRatio);
690 }
691};
692
693SkPicture* PictureRenderer::createPicture() {
694 switch (fBBoxHierarchyType) {
695 case kNone_BBoxHierarchyType:
696 return SkNEW(SkPicture);
697 case kRTree_BBoxHierarchyType:
698 return SkNEW(RTreePicture);
junov@chromium.org7b537062012-11-06 18:58:43 +0000699 case kTileGrid_BBoxHierarchyType:
junov@chromium.org3cb834b2012-12-13 16:39:53 +0000700 return SkNEW_ARGS(SkTileGridPicture, (fGridWidth, fGridHeight, fPicture->width(),
701 fPicture->height()));
junov@chromium.org9313ca42012-11-02 18:11:49 +0000702 }
703 SkASSERT(0); // invalid bbhType
704 return NULL;
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000705}
junov@chromium.org9313ca42012-11-02 18:11:49 +0000706
reed@google.comfe7b1ed2012-11-29 21:00:39 +0000707///////////////////////////////////////////////////////////////////////////////
708
709class GatherRenderer : public PictureRenderer {
710public:
711 virtual bool render(const SkString* path) SK_OVERRIDE {
712 SkRect bounds = SkRect::MakeWH(SkIntToScalar(fPicture->width()),
713 SkIntToScalar(fPicture->height()));
714 SkData* data = SkPictureUtils::GatherPixelRefs(fPicture, bounds);
715 SkSafeUnref(data);
skia.committer@gmail.comc7b4be72012-12-11 02:01:20 +0000716
reed@google.comfe7b1ed2012-11-29 21:00:39 +0000717 return NULL == path; // we don't have anything to write
718 }
skia.committer@gmail.comc7b4be72012-12-11 02:01:20 +0000719
reed@google.comfe7b1ed2012-11-29 21:00:39 +0000720private:
721 virtual SkString getConfigNameInternal() SK_OVERRIDE {
722 return SkString("gather_pixelrefs");
723 }
724};
725
726PictureRenderer* CreateGatherPixelRefsRenderer() {
727 return SkNEW(GatherRenderer);
728}
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +0000729
reed@google.com5a34fd32012-12-10 16:05:09 +0000730///////////////////////////////////////////////////////////////////////////////
731
732class PictureCloneRenderer : public PictureRenderer {
733public:
734 virtual bool render(const SkString* path) SK_OVERRIDE {
735 for (int i = 0; i < 100; ++i) {
736 SkPicture* clone = fPicture->clone();
737 SkSafeUnref(clone);
738 }
skia.committer@gmail.comc7b4be72012-12-11 02:01:20 +0000739
reed@google.com5a34fd32012-12-10 16:05:09 +0000740 return NULL == path; // we don't have anything to write
741 }
skia.committer@gmail.comc7b4be72012-12-11 02:01:20 +0000742
reed@google.com5a34fd32012-12-10 16:05:09 +0000743private:
744 virtual SkString getConfigNameInternal() SK_OVERRIDE {
745 return SkString("picture_clone");
746 }
747};
748
749PictureRenderer* CreatePictureCloneRenderer() {
750 return SkNEW(PictureCloneRenderer);
751}
752
junov@chromium.org9313ca42012-11-02 18:11:49 +0000753} // namespace sk_tools