blob: ac5e47c124b38b08b1c3095a675e16dd35604f6a [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.coma62da2f2012-11-02 21:28:12 +0000324 , fTileMinPowerOf2Width(0) { }
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000325
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000326void TiledPictureRenderer::init(SkPicture* pict) {
327 SkASSERT(pict != NULL);
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000328 SkASSERT(0 == fTileRects.count());
329 if (NULL == pict || fTileRects.count() != 0) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000330 return;
331 }
332
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000333 // Do not call INHERITED::init(), which would create a (potentially large) canvas which is not
334 // used by bench_pictures.
335 fPicture = pict;
junov@chromium.org9313ca42012-11-02 18:11:49 +0000336 fPicture->ref();
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000337 this->buildBBoxHierarchy();
keyar@chromium.orgcc6e5ef2012-07-27 20:09:26 +0000338
339 if (fTileWidthPercentage > 0) {
robertphillips@google.com5d8d1862012-08-15 14:36:41 +0000340 fTileWidth = sk_float_ceil2int(float(fTileWidthPercentage * fPicture->width() / 100));
keyar@chromium.orgcc6e5ef2012-07-27 20:09:26 +0000341 }
342 if (fTileHeightPercentage > 0) {
robertphillips@google.com5d8d1862012-08-15 14:36:41 +0000343 fTileHeight = sk_float_ceil2int(float(fTileHeightPercentage * fPicture->height() / 100));
keyar@chromium.orgcc6e5ef2012-07-27 20:09:26 +0000344 }
345
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000346 if (fTileMinPowerOf2Width > 0) {
347 this->setupPowerOf2Tiles();
348 } else {
349 this->setupTiles();
350 }
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000351}
352
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000353void TiledPictureRenderer::end() {
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000354 fTileRects.reset();
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000355 this->INHERITED::end();
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000356}
357
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000358void TiledPictureRenderer::setupTiles() {
scroggo@google.comc0d5e542012-12-13 21:40:48 +0000359 // Only use enough tiles to cover the viewport
360 const int width = this->getViewWidth();
361 const int height = this->getViewHeight();
362
363 for (int tile_y_start = 0; tile_y_start < height; tile_y_start += fTileHeight) {
364 for (int tile_x_start = 0; tile_x_start < width; tile_x_start += fTileWidth) {
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000365 *fTileRects.append() = SkRect::MakeXYWH(SkIntToScalar(tile_x_start),
366 SkIntToScalar(tile_y_start),
367 SkIntToScalar(fTileWidth),
368 SkIntToScalar(fTileHeight));
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000369 }
370 }
371}
372
373// The goal of the powers of two tiles is to minimize the amount of wasted tile
374// space in the width-wise direction and then minimize the number of tiles. The
375// constraints are that every tile must have a pixel width that is a power of
376// two and also be of some minimal width (that is also a power of two).
377//
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000378// This is solved by first taking our picture size and rounding it up to the
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000379// multiple of the minimal width. The binary representation of this rounded
380// value gives us the tiles we need: a bit of value one means we need a tile of
381// that size.
382void TiledPictureRenderer::setupPowerOf2Tiles() {
scroggo@google.comc0d5e542012-12-13 21:40:48 +0000383 // Only use enough tiles to cover the viewport
384 const int width = this->getViewWidth();
385 const int height = this->getViewHeight();
386
387 int rounded_value = width;
388 if (width % fTileMinPowerOf2Width != 0) {
389 rounded_value = width - (width % fTileMinPowerOf2Width) + fTileMinPowerOf2Width;
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000390 }
391
scroggo@google.comc0d5e542012-12-13 21:40:48 +0000392 int num_bits = SkScalarCeilToInt(SkScalarLog2(SkIntToScalar(width)));
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000393 int largest_possible_tile_size = 1 << num_bits;
394
395 // The tile height is constant for a particular picture.
scroggo@google.comc0d5e542012-12-13 21:40:48 +0000396 for (int tile_y_start = 0; tile_y_start < height; tile_y_start += fTileHeight) {
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000397 int tile_x_start = 0;
398 int current_width = largest_possible_tile_size;
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000399 // Set fTileWidth to be the width of the widest tile, so that each canvas is large enough
400 // to draw each tile.
401 fTileWidth = current_width;
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000402
403 while (current_width >= fTileMinPowerOf2Width) {
404 // It is very important this is a bitwise AND.
405 if (current_width & rounded_value) {
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000406 *fTileRects.append() = SkRect::MakeXYWH(SkIntToScalar(tile_x_start),
407 SkIntToScalar(tile_y_start),
408 SkIntToScalar(current_width),
409 SkIntToScalar(fTileHeight));
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000410 tile_x_start += current_width;
411 }
412
413 current_width >>= 1;
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000414 }
415 }
416}
417
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000418/**
419 * Draw the specified playback to the canvas translated to rectangle provided, so that this mini
420 * canvas represents the rectangle's portion of the overall picture.
421 * Saves and restores so that the initial clip and matrix return to their state before this function
422 * is called.
423 */
424template<class T>
425static void DrawTileToCanvas(SkCanvas* canvas, const SkRect& tileRect, T* playback) {
426 int saveCount = canvas->save();
427 // Translate so that we draw the correct portion of the picture
428 canvas->translate(-tileRect.fLeft, -tileRect.fTop);
429 playback->draw(canvas);
430 canvas->restoreToCount(saveCount);
431 canvas->flush();
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000432}
433
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000434///////////////////////////////////////////////////////////////////////////////////////////////
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000435
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000436bool TiledPictureRenderer::render(const SkString* path) {
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000437 SkASSERT(fPicture != NULL);
438 if (NULL == fPicture) {
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000439 return false;
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000440 }
441
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000442 // Reuse one canvas for all tiles.
443 SkCanvas* canvas = this->setupCanvas(fTileWidth, fTileHeight);
444 SkAutoUnref aur(canvas);
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000445
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000446 bool success = true;
447 for (int i = 0; i < fTileRects.count(); ++i) {
448 DrawTileToCanvas(canvas, fTileRects[i], fPicture);
449 if (NULL != path) {
450 success &= writeAppendNumber(canvas, path, i);
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000451 }
keyar@chromium.org163b5672012-08-01 17:53:29 +0000452 }
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000453 return success;
keyar@chromium.org163b5672012-08-01 17:53:29 +0000454}
455
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000456SkCanvas* TiledPictureRenderer::setupCanvas(int width, int height) {
457 SkCanvas* canvas = this->INHERITED::setupCanvas(width, height);
458 SkASSERT(fPicture != NULL);
scroggo@google.comc0d5e542012-12-13 21:40:48 +0000459 const int totalWidth = this->getViewWidth();
460 const int totalHeight = this->getViewHeight();
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000461 // Clip the tile to an area that is completely in what the SkPicture says is the
462 // drawn-to area. This is mostly important for tiles on the right and bottom edges
463 // as they may go over this area and the picture may have some commands that
464 // draw outside of this area and so should not actually be written.
scroggo@google.comc0d5e542012-12-13 21:40:48 +0000465 SkRect clip = SkRect::MakeWH(SkIntToScalar(totalWidth), SkIntToScalar(totalHeight));
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000466 canvas->clipRect(clip);
467 return canvas;
468}
scroggo@google.com0a049b82012-11-02 22:01:26 +0000469
470SkString TiledPictureRenderer::getConfigNameInternal() {
471 SkString name;
472 if (fTileMinPowerOf2Width > 0) {
473 name.append("pow2tile_");
474 name.appendf("%i", fTileMinPowerOf2Width);
475 } else {
476 name.append("tile_");
477 if (fTileWidthPercentage > 0) {
478 name.appendf("%.f%%", fTileWidthPercentage);
479 } else {
480 name.appendf("%i", fTileWidth);
481 }
482 }
483 name.append("x");
484 if (fTileHeightPercentage > 0) {
485 name.appendf("%.f%%", fTileHeightPercentage);
486 } else {
487 name.appendf("%i", fTileHeight);
488 }
489 return name;
490}
491
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000492///////////////////////////////////////////////////////////////////////////////////////////////
493
494// Holds all of the information needed to draw a set of tiles.
495class CloneData : public SkRunnable {
496
497public:
498 CloneData(SkPicture* clone, SkCanvas* canvas, SkTDArray<SkRect>& rects, int start, int end,
499 SkRunnable* done)
500 : fClone(clone)
501 , fCanvas(canvas)
502 , fPath(NULL)
503 , fRects(rects)
504 , fStart(start)
505 , fEnd(end)
506 , fSuccess(NULL)
507 , fDone(done) {
508 SkASSERT(fDone != NULL);
509 }
510
511 virtual void run() SK_OVERRIDE {
512 SkGraphics::SetTLSFontCacheLimit(1024 * 1024);
513 for (int i = fStart; i < fEnd; i++) {
514 DrawTileToCanvas(fCanvas, fRects[i], fClone);
515 if (fPath != NULL && !writeAppendNumber(fCanvas, fPath, i)
516 && fSuccess != NULL) {
517 *fSuccess = false;
518 // If one tile fails to write to a file, do not continue drawing the rest.
519 break;
520 }
521 }
522 fDone->run();
523 }
524
525 void setPathAndSuccess(const SkString* path, bool* success) {
526 fPath = path;
527 fSuccess = success;
528 }
529
530private:
531 // All pointers unowned.
532 SkPicture* fClone; // Picture to draw from. Each CloneData has a unique one which
533 // is threadsafe.
534 SkCanvas* fCanvas; // Canvas to draw to. Reused for each tile.
535 const SkString* fPath; // If non-null, path to write the result to as a PNG.
536 SkTDArray<SkRect>& fRects; // All tiles of the picture.
537 const int fStart; // Range of tiles drawn by this thread.
538 const int fEnd;
539 bool* fSuccess; // Only meaningful if path is non-null. Shared by all threads,
540 // and only set to false upon failure to write to a PNG.
541 SkRunnable* fDone;
542};
543
544MultiCorePictureRenderer::MultiCorePictureRenderer(int threadCount)
545: fNumThreads(threadCount)
546, fThreadPool(threadCount)
547, fCountdown(threadCount) {
548 // Only need to create fNumThreads - 1 clones, since one thread will use the base
549 // picture.
550 fPictureClones = SkNEW_ARRAY(SkPicture, fNumThreads - 1);
551 fCloneData = SkNEW_ARRAY(CloneData*, fNumThreads);
552}
553
554void MultiCorePictureRenderer::init(SkPicture *pict) {
555 // Set fPicture and the tiles.
556 this->INHERITED::init(pict);
557 for (int i = 0; i < fNumThreads; ++i) {
558 *fCanvasPool.append() = this->setupCanvas(this->getTileWidth(), this->getTileHeight());
559 }
560 // Only need to create fNumThreads - 1 clones, since one thread will use the base picture.
561 fPicture->clone(fPictureClones, fNumThreads - 1);
562 // Populate each thread with the appropriate data.
563 // Group the tiles into nearly equal size chunks, rounding up so we're sure to cover them all.
564 const int chunkSize = (fTileRects.count() + fNumThreads - 1) / fNumThreads;
565
566 for (int i = 0; i < fNumThreads; i++) {
567 SkPicture* pic;
568 if (i == fNumThreads-1) {
569 // The last set will use the original SkPicture.
570 pic = fPicture;
571 } else {
572 pic = &fPictureClones[i];
573 }
574 const int start = i * chunkSize;
575 const int end = SkMin32(start + chunkSize, fTileRects.count());
576 fCloneData[i] = SkNEW_ARGS(CloneData,
577 (pic, fCanvasPool[i], fTileRects, start, end, &fCountdown));
578 }
579}
580
581bool MultiCorePictureRenderer::render(const SkString *path) {
582 bool success = true;
583 if (path != NULL) {
584 for (int i = 0; i < fNumThreads-1; i++) {
585 fCloneData[i]->setPathAndSuccess(path, &success);
586 }
587 }
588
589 fCountdown.reset(fNumThreads);
590 for (int i = 0; i < fNumThreads; i++) {
591 fThreadPool.add(fCloneData[i]);
592 }
593 fCountdown.wait();
594
595 return success;
596}
597
598void MultiCorePictureRenderer::end() {
599 for (int i = 0; i < fNumThreads - 1; i++) {
600 SkDELETE(fCloneData[i]);
601 fCloneData[i] = NULL;
602 }
603
604 fCanvasPool.unrefAll();
605
606 this->INHERITED::end();
607}
608
609MultiCorePictureRenderer::~MultiCorePictureRenderer() {
610 // Each individual CloneData was deleted in end.
611 SkDELETE_ARRAY(fCloneData);
612 SkDELETE_ARRAY(fPictureClones);
613}
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000614
scroggo@google.com0a049b82012-11-02 22:01:26 +0000615SkString MultiCorePictureRenderer::getConfigNameInternal() {
616 SkString name = this->INHERITED::getConfigNameInternal();
617 name.appendf("_multi_%i_threads", fNumThreads);
618 return name;
619}
620
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000621///////////////////////////////////////////////////////////////////////////////////////////////
scroggo@google.com9a412522012-09-07 15:21:18 +0000622
623void PlaybackCreationRenderer::setup() {
junov@chromium.org9313ca42012-11-02 18:11:49 +0000624 fReplayer.reset(this->createPicture());
625 SkCanvas* recorder = fReplayer->beginRecording(fPicture->width(), fPicture->height(),
626 this->recordFlags());
scroggo@google.com9a412522012-09-07 15:21:18 +0000627 fPicture->draw(recorder);
628}
629
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000630bool PlaybackCreationRenderer::render(const SkString*) {
junov@chromium.org9313ca42012-11-02 18:11:49 +0000631 fReplayer->endRecording();
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000632 // Since this class does not actually render, return false.
633 return false;
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000634}
635
scroggo@google.com0a049b82012-11-02 22:01:26 +0000636SkString PlaybackCreationRenderer::getConfigNameInternal() {
637 return SkString("playback_creation");
638}
639
junov@chromium.org9313ca42012-11-02 18:11:49 +0000640///////////////////////////////////////////////////////////////////////////////////////////////
641// SkPicture variants for each BBoxHierarchy type
642
643class RTreePicture : public SkPicture {
644public:
645 virtual SkBBoxHierarchy* createBBoxHierarchy() const SK_OVERRIDE{
646 static const int kRTreeMinChildren = 6;
647 static const int kRTreeMaxChildren = 11;
648 SkScalar aspectRatio = SkScalarDiv(SkIntToScalar(fWidth),
649 SkIntToScalar(fHeight));
650 return SkRTree::Create(kRTreeMinChildren, kRTreeMaxChildren,
651 aspectRatio);
652 }
653};
654
655SkPicture* PictureRenderer::createPicture() {
656 switch (fBBoxHierarchyType) {
657 case kNone_BBoxHierarchyType:
658 return SkNEW(SkPicture);
659 case kRTree_BBoxHierarchyType:
660 return SkNEW(RTreePicture);
junov@chromium.org7b537062012-11-06 18:58:43 +0000661 case kTileGrid_BBoxHierarchyType:
junov@chromium.org3cb834b2012-12-13 16:39:53 +0000662 return SkNEW_ARGS(SkTileGridPicture, (fGridWidth, fGridHeight, fPicture->width(),
663 fPicture->height()));
junov@chromium.org9313ca42012-11-02 18:11:49 +0000664 }
665 SkASSERT(0); // invalid bbhType
666 return NULL;
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000667}
junov@chromium.org9313ca42012-11-02 18:11:49 +0000668
reed@google.comfe7b1ed2012-11-29 21:00:39 +0000669///////////////////////////////////////////////////////////////////////////////
670
671class GatherRenderer : public PictureRenderer {
672public:
673 virtual bool render(const SkString* path) SK_OVERRIDE {
674 SkRect bounds = SkRect::MakeWH(SkIntToScalar(fPicture->width()),
675 SkIntToScalar(fPicture->height()));
676 SkData* data = SkPictureUtils::GatherPixelRefs(fPicture, bounds);
677 SkSafeUnref(data);
skia.committer@gmail.comc7b4be72012-12-11 02:01:20 +0000678
reed@google.comfe7b1ed2012-11-29 21:00:39 +0000679 return NULL == path; // we don't have anything to write
680 }
skia.committer@gmail.comc7b4be72012-12-11 02:01:20 +0000681
reed@google.comfe7b1ed2012-11-29 21:00:39 +0000682private:
683 virtual SkString getConfigNameInternal() SK_OVERRIDE {
684 return SkString("gather_pixelrefs");
685 }
686};
687
688PictureRenderer* CreateGatherPixelRefsRenderer() {
689 return SkNEW(GatherRenderer);
690}
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +0000691
reed@google.com5a34fd32012-12-10 16:05:09 +0000692///////////////////////////////////////////////////////////////////////////////
693
694class PictureCloneRenderer : public PictureRenderer {
695public:
696 virtual bool render(const SkString* path) SK_OVERRIDE {
697 for (int i = 0; i < 100; ++i) {
698 SkPicture* clone = fPicture->clone();
699 SkSafeUnref(clone);
700 }
skia.committer@gmail.comc7b4be72012-12-11 02:01:20 +0000701
reed@google.com5a34fd32012-12-10 16:05:09 +0000702 return NULL == path; // we don't have anything to write
703 }
skia.committer@gmail.comc7b4be72012-12-11 02:01:20 +0000704
reed@google.com5a34fd32012-12-10 16:05:09 +0000705private:
706 virtual SkString getConfigNameInternal() SK_OVERRIDE {
707 return SkString("picture_clone");
708 }
709};
710
711PictureRenderer* CreatePictureCloneRenderer() {
712 return SkNEW(PictureCloneRenderer);
713}
714
junov@chromium.org9313ca42012-11-02 18:11:49 +0000715} // namespace sk_tools