blob: 102216d595a1a059e1d694485108b1107f4db6f7 [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
scroggo@google.com82ec0b02012-12-17 19:25:54 +000090static void setUpFilter(SkCanvas* canvas, PictureRenderer::DrawFilterFlags* drawFilters) {
caryclark@google.coma3622372012-11-06 21:26:13 +000091 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 }
caryclark@google.coma3622372012-11-06 21:26:13 +000097}
98
keyar@chromium.orga474ce32012-08-20 15:03:57 +000099SkCanvas* PictureRenderer::setupCanvas() {
scroggo@google.comc0d5e542012-12-13 21:40:48 +0000100 const int width = this->getViewWidth();
101 const int height = this->getViewHeight();
102 return this->setupCanvas(width, height);
keyar@chromium.orga474ce32012-08-20 15:03:57 +0000103}
104
105SkCanvas* PictureRenderer::setupCanvas(int width, int height) {
caryclark@google.coma3622372012-11-06 21:26:13 +0000106 SkCanvas* canvas;
keyar@chromium.org4ea96c52012-08-20 15:03:29 +0000107 switch(fDeviceType) {
108 case kBitmap_DeviceType: {
109 SkBitmap bitmap;
keyar@chromium.orga474ce32012-08-20 15:03:57 +0000110 sk_tools::setup_bitmap(&bitmap, width, height);
caryclark@google.coma3622372012-11-06 21:26:13 +0000111 canvas = SkNEW_ARGS(SkCanvas, (bitmap));
keyar@chromium.org4ea96c52012-08-20 15:03:29 +0000112 }
scroggo@google.com82ec0b02012-12-17 19:25:54 +0000113 break;
keyar@chromium.org4ea96c52012-08-20 15:03:29 +0000114#if SK_SUPPORT_GPU
115 case kGPU_DeviceType: {
keyar@chromium.org4ea96c52012-08-20 15:03:29 +0000116 SkAutoTUnref<SkGpuDevice> device(SkNEW_ARGS(SkGpuDevice,
keyar@chromium.org06125642012-08-20 15:03:33 +0000117 (fGrContext, SkBitmap::kARGB_8888_Config,
keyar@chromium.orga474ce32012-08-20 15:03:57 +0000118 width, height)));
caryclark@google.coma3622372012-11-06 21:26:13 +0000119 canvas = SkNEW_ARGS(SkCanvas, (device.get()));
keyar@chromium.org4ea96c52012-08-20 15:03:29 +0000120 }
scroggo@google.com82ec0b02012-12-17 19:25:54 +0000121 break;
keyar@chromium.org4ea96c52012-08-20 15:03:29 +0000122#endif
123 default:
124 SkASSERT(0);
scroggo@google.com82ec0b02012-12-17 19:25:54 +0000125 return NULL;
keyar@chromium.org4ea96c52012-08-20 15:03:29 +0000126 }
scroggo@google.com82ec0b02012-12-17 19:25:54 +0000127 setUpFilter(canvas, fDrawFilters);
128 this->scaleToScaleFactor(canvas);
129 return canvas;
130}
keyar@chromium.orga474ce32012-08-20 15:03:57 +0000131
scroggo@google.com82ec0b02012-12-17 19:25:54 +0000132void PictureRenderer::scaleToScaleFactor(SkCanvas* canvas) {
133 SkASSERT(canvas != NULL);
134 if (fScaleFactor != SK_Scalar1) {
135 canvas->scale(fScaleFactor, fScaleFactor);
136 }
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000137}
138
139void PictureRenderer::end() {
keyar@chromium.org77a55222012-08-20 15:03:47 +0000140 this->resetState();
junov@chromium.org9313ca42012-11-02 18:11:49 +0000141 SkSafeUnref(fPicture);
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000142 fPicture = NULL;
143 fCanvas.reset(NULL);
144}
145
scroggo@google.comc0d5e542012-12-13 21:40:48 +0000146int PictureRenderer::getViewWidth() {
147 SkASSERT(fPicture != NULL);
148 int width = fPicture->width();
149 if (fViewport.width() > 0) {
150 width = SkMin32(width, fViewport.width());
151 }
152 return width;
153}
154
155int PictureRenderer::getViewHeight() {
156 SkASSERT(fPicture != NULL);
157 int height = fPicture->height();
158 if (fViewport.height() > 0) {
159 height = SkMin32(height, fViewport.height());
160 }
161 return height;
162}
163
junov@chromium.org9313ca42012-11-02 18:11:49 +0000164/** Converts fPicture to a picture that uses a BBoxHierarchy.
165 * PictureRenderer subclasses that are used to test picture playback
166 * should call this method during init.
167 */
168void PictureRenderer::buildBBoxHierarchy() {
169 SkASSERT(NULL != fPicture);
170 if (kNone_BBoxHierarchyType != fBBoxHierarchyType && NULL != fPicture) {
171 SkPicture* newPicture = this->createPicture();
172 SkCanvas* recorder = newPicture->beginRecording(fPicture->width(), fPicture->height(),
173 this->recordFlags());
174 fPicture->draw(recorder);
175 newPicture->endRecording();
176 fPicture->unref();
177 fPicture = newPicture;
178 }
179}
180
keyar@chromium.org77a55222012-08-20 15:03:47 +0000181void PictureRenderer::resetState() {
keyar@chromium.org28136b32012-08-20 15:04:15 +0000182#if SK_SUPPORT_GPU
183 if (this->isUsingGpuDevice()) {
184 SkGLContext* glContext = fGrContextFactory.getGLContext(
185 GrContextFactory::kNative_GLContextType);
keyar@chromium.org28136b32012-08-20 15:04:15 +0000186
187 SkASSERT(glContext != NULL);
188 if (NULL == glContext) {
189 return;
190 }
191
scroggo@google.com9a412522012-09-07 15:21:18 +0000192 fGrContext->flush();
keyar@chromium.org77a55222012-08-20 15:03:47 +0000193 SK_GL(*glContext, Finish());
keyar@chromium.org77a55222012-08-20 15:03:47 +0000194 }
keyar@chromium.orga40c20d2012-08-20 15:04:12 +0000195#endif
keyar@chromium.org77a55222012-08-20 15:03:47 +0000196}
197
junov@chromium.org9313ca42012-11-02 18:11:49 +0000198uint32_t PictureRenderer::recordFlags() {
199 return kNone_BBoxHierarchyType == fBBoxHierarchyType ? 0 :
200 SkPicture::kOptimizeForClippedPlayback_RecordingFlag;
201}
202
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000203/**
204 * Write the canvas to the specified path.
205 * @param canvas Must be non-null. Canvas to be written to a file.
206 * @param path Path for the file to be written. Should have no extension; write() will append
207 * an appropriate one. Passed in by value so it can be modified.
208 * @return bool True if the Canvas is written to a file.
209 */
210static bool write(SkCanvas* canvas, SkString path) {
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000211 SkASSERT(canvas != NULL);
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000212 if (NULL == canvas) {
keyar@chromium.org9299ede2012-08-21 19:05:08 +0000213 return false;
214 }
215
216 SkBitmap bitmap;
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000217 SkISize size = canvas->getDeviceSize();
218 sk_tools::setup_bitmap(&bitmap, size.width(), size.height());
keyar@chromium.org9299ede2012-08-21 19:05:08 +0000219
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000220 canvas->readPixels(&bitmap, 0, 0);
keyar@chromium.org9299ede2012-08-21 19:05:08 +0000221 sk_tools::force_all_opaque(bitmap);
222
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000223 // Since path is passed in by value, it is okay to modify it.
224 path.append(".png");
keyar@chromium.org9299ede2012-08-21 19:05:08 +0000225 return SkImageEncoder::EncodeFile(path.c_str(), bitmap, SkImageEncoder::kPNG_Type, 100);
226}
227
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000228/**
229 * If path is non NULL, append number to it, and call write(SkCanvas*, SkString) to write the
230 * provided canvas to a file. Returns true if path is NULL or if write() succeeds.
231 */
232static bool writeAppendNumber(SkCanvas* canvas, const SkString* path, int number) {
233 if (NULL == path) {
234 return true;
235 }
236 SkString pathWithNumber(*path);
237 pathWithNumber.appendf("%i", number);
238 return write(canvas, pathWithNumber);
239}
240
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000241///////////////////////////////////////////////////////////////////////////////////////////////
242
djsollen@google.comfd9720c2012-11-06 16:54:40 +0000243SkCanvas* RecordPictureRenderer::setupCanvas(int width, int height) {
244 // defer the canvas setup until the render step
245 return NULL;
246}
247
scroggo@google.coma9e3a362012-11-07 17:52:48 +0000248static bool PNGEncodeBitmapToStream(SkWStream* wStream, const SkBitmap& bm) {
249 return SkImageEncoder::EncodeStream(wStream, bm, SkImageEncoder::kPNG_Type, 100);
250}
251
252bool RecordPictureRenderer::render(const SkString* path) {
junov@chromium.org9313ca42012-11-02 18:11:49 +0000253 SkAutoTUnref<SkPicture> replayer(this->createPicture());
scroggo@google.com82ec0b02012-12-17 19:25:54 +0000254 SkCanvas* recorder = replayer->beginRecording(this->getViewWidth(), this->getViewHeight(),
junov@chromium.org9313ca42012-11-02 18:11:49 +0000255 this->recordFlags());
scroggo@google.com82ec0b02012-12-17 19:25:54 +0000256 this->scaleToScaleFactor(recorder);
scroggo@google.com9a412522012-09-07 15:21:18 +0000257 fPicture->draw(recorder);
junov@chromium.org9313ca42012-11-02 18:11:49 +0000258 replayer->endRecording();
scroggo@google.coma9e3a362012-11-07 17:52:48 +0000259 if (path != NULL) {
260 // Record the new picture as a new SKP with PNG encoded bitmaps.
261 SkString skpPath(*path);
262 // ".skp" was removed from 'path' before being passed in here.
263 skpPath.append(".skp");
264 SkFILEWStream stream(skpPath.c_str());
265 replayer->serialize(&stream, &PNGEncodeBitmapToStream);
266 return true;
267 }
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000268 return false;
scroggo@google.com9a412522012-09-07 15:21:18 +0000269}
270
scroggo@google.com0a049b82012-11-02 22:01:26 +0000271SkString RecordPictureRenderer::getConfigNameInternal() {
272 return SkString("record");
273}
274
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000275///////////////////////////////////////////////////////////////////////////////////////////////
276
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000277bool PipePictureRenderer::render(const SkString* path) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000278 SkASSERT(fCanvas.get() != NULL);
279 SkASSERT(fPicture != NULL);
keyar@chromium.org78a35c52012-08-20 15:03:44 +0000280 if (NULL == fCanvas.get() || NULL == fPicture) {
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000281 return false;
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000282 }
283
284 PipeController pipeController(fCanvas.get());
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000285 SkGPipeWriter writer;
286 SkCanvas* pipeCanvas = writer.startRecording(&pipeController);
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000287 pipeCanvas->drawPicture(*fPicture);
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000288 writer.endRecording();
scroggo@google.com9a412522012-09-07 15:21:18 +0000289 fCanvas->flush();
borenet@google.com070d3542012-10-26 13:26:55 +0000290 if (NULL != path) {
291 return write(fCanvas, *path);
292 }
293 return true;
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000294}
295
scroggo@google.com0a049b82012-11-02 22:01:26 +0000296SkString PipePictureRenderer::getConfigNameInternal() {
297 return SkString("pipe");
298}
299
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000300///////////////////////////////////////////////////////////////////////////////////////////////
301
junov@chromium.org9313ca42012-11-02 18:11:49 +0000302void SimplePictureRenderer::init(SkPicture* picture) {
303 INHERITED::init(picture);
304 this->buildBBoxHierarchy();
305}
306
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000307bool SimplePictureRenderer::render(const SkString* path) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000308 SkASSERT(fCanvas.get() != NULL);
309 SkASSERT(fPicture != NULL);
keyar@chromium.org78a35c52012-08-20 15:03:44 +0000310 if (NULL == fCanvas.get() || NULL == fPicture) {
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000311 return false;
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000312 }
313
314 fCanvas->drawPicture(*fPicture);
scroggo@google.com9a412522012-09-07 15:21:18 +0000315 fCanvas->flush();
borenet@google.com070d3542012-10-26 13:26:55 +0000316 if (NULL != path) {
317 return write(fCanvas, *path);
318 }
319 return true;
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000320}
321
scroggo@google.com0a049b82012-11-02 22:01:26 +0000322SkString SimplePictureRenderer::getConfigNameInternal() {
323 return SkString("simple");
324}
325
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000326///////////////////////////////////////////////////////////////////////////////////////////////
327
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000328TiledPictureRenderer::TiledPictureRenderer()
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000329 : fTileWidth(kDefaultTileWidth)
rileya@google.comb947b912012-08-29 17:35:07 +0000330 , fTileHeight(kDefaultTileHeight)
rileya@google.coma04dc022012-09-10 19:01:38 +0000331 , fTileWidthPercentage(0.0)
rileya@google.comb947b912012-08-29 17:35:07 +0000332 , fTileHeightPercentage(0.0)
scroggo@google.comcbcef702012-12-13 22:09:28 +0000333 , fTileMinPowerOf2Width(0)
334 , fCurrentTileOffset(-1)
335 , fTilesX(0)
336 , fTilesY(0) { }
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000337
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000338void TiledPictureRenderer::init(SkPicture* pict) {
339 SkASSERT(pict != NULL);
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000340 SkASSERT(0 == fTileRects.count());
341 if (NULL == pict || fTileRects.count() != 0) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000342 return;
343 }
344
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000345 // Do not call INHERITED::init(), which would create a (potentially large) canvas which is not
346 // used by bench_pictures.
347 fPicture = pict;
junov@chromium.org9313ca42012-11-02 18:11:49 +0000348 fPicture->ref();
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000349 this->buildBBoxHierarchy();
keyar@chromium.orgcc6e5ef2012-07-27 20:09:26 +0000350
351 if (fTileWidthPercentage > 0) {
robertphillips@google.com5d8d1862012-08-15 14:36:41 +0000352 fTileWidth = sk_float_ceil2int(float(fTileWidthPercentage * fPicture->width() / 100));
keyar@chromium.orgcc6e5ef2012-07-27 20:09:26 +0000353 }
354 if (fTileHeightPercentage > 0) {
robertphillips@google.com5d8d1862012-08-15 14:36:41 +0000355 fTileHeight = sk_float_ceil2int(float(fTileHeightPercentage * fPicture->height() / 100));
keyar@chromium.orgcc6e5ef2012-07-27 20:09:26 +0000356 }
357
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000358 if (fTileMinPowerOf2Width > 0) {
359 this->setupPowerOf2Tiles();
360 } else {
361 this->setupTiles();
362 }
scroggo@google.comcbcef702012-12-13 22:09:28 +0000363 fCanvas.reset(this->setupCanvas(fTileWidth, fTileHeight));
364 // Initialize to -1 so that the first call to nextTile will set this up to draw tile 0 on the
365 // first call to drawCurrentTile.
366 fCurrentTileOffset = -1;
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000367}
368
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000369void TiledPictureRenderer::end() {
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000370 fTileRects.reset();
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000371 this->INHERITED::end();
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000372}
373
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000374void TiledPictureRenderer::setupTiles() {
scroggo@google.comc0d5e542012-12-13 21:40:48 +0000375 // Only use enough tiles to cover the viewport
376 const int width = this->getViewWidth();
377 const int height = this->getViewHeight();
378
scroggo@google.comcbcef702012-12-13 22:09:28 +0000379 fTilesX = fTilesY = 0;
scroggo@google.comc0d5e542012-12-13 21:40:48 +0000380 for (int tile_y_start = 0; tile_y_start < height; tile_y_start += fTileHeight) {
scroggo@google.comcbcef702012-12-13 22:09:28 +0000381 fTilesY++;
scroggo@google.comc0d5e542012-12-13 21:40:48 +0000382 for (int tile_x_start = 0; tile_x_start < width; tile_x_start += fTileWidth) {
scroggo@google.comcbcef702012-12-13 22:09:28 +0000383 if (0 == tile_y_start) {
384 // Only count tiles in the X direction on the first pass.
385 fTilesX++;
386 }
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000387 *fTileRects.append() = SkRect::MakeXYWH(SkIntToScalar(tile_x_start),
388 SkIntToScalar(tile_y_start),
389 SkIntToScalar(fTileWidth),
390 SkIntToScalar(fTileHeight));
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000391 }
392 }
393}
394
scroggo@google.comcbcef702012-12-13 22:09:28 +0000395bool TiledPictureRenderer::tileDimensions(int &x, int &y) {
396 if (fTileRects.count() == 0 || NULL == fPicture) {
397 return false;
398 }
399 x = fTilesX;
400 y = fTilesY;
401 return true;
402}
403
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000404// The goal of the powers of two tiles is to minimize the amount of wasted tile
405// space in the width-wise direction and then minimize the number of tiles. The
406// constraints are that every tile must have a pixel width that is a power of
407// two and also be of some minimal width (that is also a power of two).
408//
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000409// This is solved by first taking our picture size and rounding it up to the
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000410// multiple of the minimal width. The binary representation of this rounded
411// value gives us the tiles we need: a bit of value one means we need a tile of
412// that size.
413void TiledPictureRenderer::setupPowerOf2Tiles() {
scroggo@google.comc0d5e542012-12-13 21:40:48 +0000414 // Only use enough tiles to cover the viewport
415 const int width = this->getViewWidth();
416 const int height = this->getViewHeight();
417
418 int rounded_value = width;
419 if (width % fTileMinPowerOf2Width != 0) {
420 rounded_value = width - (width % fTileMinPowerOf2Width) + fTileMinPowerOf2Width;
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000421 }
422
scroggo@google.comc0d5e542012-12-13 21:40:48 +0000423 int num_bits = SkScalarCeilToInt(SkScalarLog2(SkIntToScalar(width)));
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000424 int largest_possible_tile_size = 1 << num_bits;
425
scroggo@google.comcbcef702012-12-13 22:09:28 +0000426 fTilesX = fTilesY = 0;
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000427 // The tile height is constant for a particular picture.
scroggo@google.comc0d5e542012-12-13 21:40:48 +0000428 for (int tile_y_start = 0; tile_y_start < height; tile_y_start += fTileHeight) {
scroggo@google.comcbcef702012-12-13 22:09:28 +0000429 fTilesY++;
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000430 int tile_x_start = 0;
431 int current_width = largest_possible_tile_size;
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000432 // Set fTileWidth to be the width of the widest tile, so that each canvas is large enough
433 // to draw each tile.
434 fTileWidth = current_width;
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000435
436 while (current_width >= fTileMinPowerOf2Width) {
437 // It is very important this is a bitwise AND.
438 if (current_width & rounded_value) {
scroggo@google.comcbcef702012-12-13 22:09:28 +0000439 if (0 == tile_y_start) {
440 // Only count tiles in the X direction on the first pass.
441 fTilesX++;
442 }
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000443 *fTileRects.append() = SkRect::MakeXYWH(SkIntToScalar(tile_x_start),
444 SkIntToScalar(tile_y_start),
445 SkIntToScalar(current_width),
446 SkIntToScalar(fTileHeight));
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000447 tile_x_start += current_width;
448 }
449
450 current_width >>= 1;
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000451 }
452 }
453}
454
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000455/**
456 * Draw the specified playback to the canvas translated to rectangle provided, so that this mini
457 * canvas represents the rectangle's portion of the overall picture.
458 * Saves and restores so that the initial clip and matrix return to their state before this function
459 * is called.
460 */
461template<class T>
462static void DrawTileToCanvas(SkCanvas* canvas, const SkRect& tileRect, T* playback) {
463 int saveCount = canvas->save();
scroggo@google.com82ec0b02012-12-17 19:25:54 +0000464 // Translate so that we draw the correct portion of the picture.
465 // Perform a postTranslate so that the scaleFactor does not interfere with the positioning.
466 SkMatrix mat(canvas->getTotalMatrix());
467 mat.postTranslate(-tileRect.fLeft, -tileRect.fTop);
468 canvas->setMatrix(mat);
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000469 playback->draw(canvas);
470 canvas->restoreToCount(saveCount);
471 canvas->flush();
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000472}
473
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000474///////////////////////////////////////////////////////////////////////////////////////////////
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000475
scroggo@google.comcbcef702012-12-13 22:09:28 +0000476bool TiledPictureRenderer::nextTile(int &i, int &j) {
477 if (++fCurrentTileOffset < fTileRects.count()) {
478 i = fCurrentTileOffset % fTilesX;
479 j = fCurrentTileOffset / fTilesX;
480 return true;
481 }
482 return false;
483}
484
485void TiledPictureRenderer::drawCurrentTile() {
486 SkASSERT(fCurrentTileOffset >= 0 && fCurrentTileOffset < fTileRects.count());
487 DrawTileToCanvas(fCanvas, fTileRects[fCurrentTileOffset], fPicture);
488}
489
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000490bool TiledPictureRenderer::render(const SkString* path) {
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000491 SkASSERT(fPicture != NULL);
492 if (NULL == fPicture) {
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000493 return false;
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000494 }
495
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000496 bool success = true;
497 for (int i = 0; i < fTileRects.count(); ++i) {
scroggo@google.comcbcef702012-12-13 22:09:28 +0000498 DrawTileToCanvas(fCanvas, fTileRects[i], fPicture);
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000499 if (NULL != path) {
scroggo@google.comcbcef702012-12-13 22:09:28 +0000500 success &= writeAppendNumber(fCanvas, path, i);
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000501 }
keyar@chromium.org163b5672012-08-01 17:53:29 +0000502 }
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000503 return success;
keyar@chromium.org163b5672012-08-01 17:53:29 +0000504}
505
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000506SkCanvas* TiledPictureRenderer::setupCanvas(int width, int height) {
507 SkCanvas* canvas = this->INHERITED::setupCanvas(width, height);
508 SkASSERT(fPicture != NULL);
scroggo@google.com82ec0b02012-12-17 19:25:54 +0000509 // Clip the tile to an area that is completely inside both the SkPicture and the viewport. This
510 // is mostly important for tiles on the right and bottom edges as they may go over this area and
511 // the picture may have some commands that draw outside of this area and so should not actually
512 // be written.
513 // Uses a clipRegion so that it will be unaffected by the scale factor, which may have been set
514 // by INHERITED::setupCanvas.
515 SkRegion clipRegion;
516 clipRegion.setRect(0, 0, this->getViewWidth(), this->getViewHeight());
517 canvas->clipRegion(clipRegion);
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000518 return canvas;
519}
scroggo@google.com0a049b82012-11-02 22:01:26 +0000520
521SkString TiledPictureRenderer::getConfigNameInternal() {
522 SkString name;
523 if (fTileMinPowerOf2Width > 0) {
524 name.append("pow2tile_");
525 name.appendf("%i", fTileMinPowerOf2Width);
526 } else {
527 name.append("tile_");
528 if (fTileWidthPercentage > 0) {
529 name.appendf("%.f%%", fTileWidthPercentage);
530 } else {
531 name.appendf("%i", fTileWidth);
532 }
533 }
534 name.append("x");
535 if (fTileHeightPercentage > 0) {
536 name.appendf("%.f%%", fTileHeightPercentage);
537 } else {
538 name.appendf("%i", fTileHeight);
539 }
540 return name;
541}
542
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000543///////////////////////////////////////////////////////////////////////////////////////////////
544
545// Holds all of the information needed to draw a set of tiles.
546class CloneData : public SkRunnable {
547
548public:
549 CloneData(SkPicture* clone, SkCanvas* canvas, SkTDArray<SkRect>& rects, int start, int end,
550 SkRunnable* done)
551 : fClone(clone)
552 , fCanvas(canvas)
553 , fPath(NULL)
554 , fRects(rects)
555 , fStart(start)
556 , fEnd(end)
557 , fSuccess(NULL)
558 , fDone(done) {
559 SkASSERT(fDone != NULL);
560 }
561
562 virtual void run() SK_OVERRIDE {
563 SkGraphics::SetTLSFontCacheLimit(1024 * 1024);
564 for (int i = fStart; i < fEnd; i++) {
565 DrawTileToCanvas(fCanvas, fRects[i], fClone);
566 if (fPath != NULL && !writeAppendNumber(fCanvas, fPath, i)
567 && fSuccess != NULL) {
568 *fSuccess = false;
569 // If one tile fails to write to a file, do not continue drawing the rest.
570 break;
571 }
572 }
573 fDone->run();
574 }
575
576 void setPathAndSuccess(const SkString* path, bool* success) {
577 fPath = path;
578 fSuccess = success;
579 }
580
581private:
582 // All pointers unowned.
583 SkPicture* fClone; // Picture to draw from. Each CloneData has a unique one which
584 // is threadsafe.
585 SkCanvas* fCanvas; // Canvas to draw to. Reused for each tile.
586 const SkString* fPath; // If non-null, path to write the result to as a PNG.
587 SkTDArray<SkRect>& fRects; // All tiles of the picture.
588 const int fStart; // Range of tiles drawn by this thread.
589 const int fEnd;
590 bool* fSuccess; // Only meaningful if path is non-null. Shared by all threads,
591 // and only set to false upon failure to write to a PNG.
592 SkRunnable* fDone;
593};
594
595MultiCorePictureRenderer::MultiCorePictureRenderer(int threadCount)
596: fNumThreads(threadCount)
597, fThreadPool(threadCount)
598, fCountdown(threadCount) {
599 // Only need to create fNumThreads - 1 clones, since one thread will use the base
600 // picture.
601 fPictureClones = SkNEW_ARRAY(SkPicture, fNumThreads - 1);
602 fCloneData = SkNEW_ARRAY(CloneData*, fNumThreads);
603}
604
605void MultiCorePictureRenderer::init(SkPicture *pict) {
606 // Set fPicture and the tiles.
607 this->INHERITED::init(pict);
608 for (int i = 0; i < fNumThreads; ++i) {
609 *fCanvasPool.append() = this->setupCanvas(this->getTileWidth(), this->getTileHeight());
610 }
611 // Only need to create fNumThreads - 1 clones, since one thread will use the base picture.
612 fPicture->clone(fPictureClones, fNumThreads - 1);
613 // Populate each thread with the appropriate data.
614 // Group the tiles into nearly equal size chunks, rounding up so we're sure to cover them all.
615 const int chunkSize = (fTileRects.count() + fNumThreads - 1) / fNumThreads;
616
617 for (int i = 0; i < fNumThreads; i++) {
618 SkPicture* pic;
619 if (i == fNumThreads-1) {
620 // The last set will use the original SkPicture.
621 pic = fPicture;
622 } else {
623 pic = &fPictureClones[i];
624 }
625 const int start = i * chunkSize;
626 const int end = SkMin32(start + chunkSize, fTileRects.count());
627 fCloneData[i] = SkNEW_ARGS(CloneData,
628 (pic, fCanvasPool[i], fTileRects, start, end, &fCountdown));
629 }
630}
631
632bool MultiCorePictureRenderer::render(const SkString *path) {
633 bool success = true;
634 if (path != NULL) {
635 for (int i = 0; i < fNumThreads-1; i++) {
636 fCloneData[i]->setPathAndSuccess(path, &success);
637 }
638 }
639
640 fCountdown.reset(fNumThreads);
641 for (int i = 0; i < fNumThreads; i++) {
642 fThreadPool.add(fCloneData[i]);
643 }
644 fCountdown.wait();
645
646 return success;
647}
648
649void MultiCorePictureRenderer::end() {
650 for (int i = 0; i < fNumThreads - 1; i++) {
651 SkDELETE(fCloneData[i]);
652 fCloneData[i] = NULL;
653 }
654
655 fCanvasPool.unrefAll();
656
657 this->INHERITED::end();
658}
659
660MultiCorePictureRenderer::~MultiCorePictureRenderer() {
661 // Each individual CloneData was deleted in end.
662 SkDELETE_ARRAY(fCloneData);
663 SkDELETE_ARRAY(fPictureClones);
664}
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000665
scroggo@google.com0a049b82012-11-02 22:01:26 +0000666SkString MultiCorePictureRenderer::getConfigNameInternal() {
667 SkString name = this->INHERITED::getConfigNameInternal();
668 name.appendf("_multi_%i_threads", fNumThreads);
669 return name;
670}
671
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000672///////////////////////////////////////////////////////////////////////////////////////////////
scroggo@google.com9a412522012-09-07 15:21:18 +0000673
674void PlaybackCreationRenderer::setup() {
junov@chromium.org9313ca42012-11-02 18:11:49 +0000675 fReplayer.reset(this->createPicture());
scroggo@google.com82ec0b02012-12-17 19:25:54 +0000676 SkCanvas* recorder = fReplayer->beginRecording(this->getViewWidth(), this->getViewHeight(),
junov@chromium.org9313ca42012-11-02 18:11:49 +0000677 this->recordFlags());
scroggo@google.com82ec0b02012-12-17 19:25:54 +0000678 this->scaleToScaleFactor(recorder);
scroggo@google.com9a412522012-09-07 15:21:18 +0000679 fPicture->draw(recorder);
680}
681
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000682bool PlaybackCreationRenderer::render(const SkString*) {
junov@chromium.org9313ca42012-11-02 18:11:49 +0000683 fReplayer->endRecording();
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000684 // Since this class does not actually render, return false.
685 return false;
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000686}
687
scroggo@google.com0a049b82012-11-02 22:01:26 +0000688SkString PlaybackCreationRenderer::getConfigNameInternal() {
689 return SkString("playback_creation");
690}
691
junov@chromium.org9313ca42012-11-02 18:11:49 +0000692///////////////////////////////////////////////////////////////////////////////////////////////
693// SkPicture variants for each BBoxHierarchy type
694
695class RTreePicture : public SkPicture {
696public:
697 virtual SkBBoxHierarchy* createBBoxHierarchy() const SK_OVERRIDE{
698 static const int kRTreeMinChildren = 6;
699 static const int kRTreeMaxChildren = 11;
700 SkScalar aspectRatio = SkScalarDiv(SkIntToScalar(fWidth),
701 SkIntToScalar(fHeight));
702 return SkRTree::Create(kRTreeMinChildren, kRTreeMaxChildren,
703 aspectRatio);
704 }
705};
706
707SkPicture* PictureRenderer::createPicture() {
708 switch (fBBoxHierarchyType) {
709 case kNone_BBoxHierarchyType:
710 return SkNEW(SkPicture);
711 case kRTree_BBoxHierarchyType:
712 return SkNEW(RTreePicture);
junov@chromium.org7b537062012-11-06 18:58:43 +0000713 case kTileGrid_BBoxHierarchyType:
junov@chromium.org3cb834b2012-12-13 16:39:53 +0000714 return SkNEW_ARGS(SkTileGridPicture, (fGridWidth, fGridHeight, fPicture->width(),
715 fPicture->height()));
junov@chromium.org9313ca42012-11-02 18:11:49 +0000716 }
717 SkASSERT(0); // invalid bbhType
718 return NULL;
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000719}
junov@chromium.org9313ca42012-11-02 18:11:49 +0000720
reed@google.comfe7b1ed2012-11-29 21:00:39 +0000721///////////////////////////////////////////////////////////////////////////////
722
723class GatherRenderer : public PictureRenderer {
724public:
725 virtual bool render(const SkString* path) SK_OVERRIDE {
726 SkRect bounds = SkRect::MakeWH(SkIntToScalar(fPicture->width()),
727 SkIntToScalar(fPicture->height()));
728 SkData* data = SkPictureUtils::GatherPixelRefs(fPicture, bounds);
729 SkSafeUnref(data);
skia.committer@gmail.comc7b4be72012-12-11 02:01:20 +0000730
reed@google.comfe7b1ed2012-11-29 21:00:39 +0000731 return NULL == path; // we don't have anything to write
732 }
skia.committer@gmail.comc7b4be72012-12-11 02:01:20 +0000733
reed@google.comfe7b1ed2012-11-29 21:00:39 +0000734private:
735 virtual SkString getConfigNameInternal() SK_OVERRIDE {
736 return SkString("gather_pixelrefs");
737 }
738};
739
740PictureRenderer* CreateGatherPixelRefsRenderer() {
741 return SkNEW(GatherRenderer);
742}
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +0000743
reed@google.com5a34fd32012-12-10 16:05:09 +0000744///////////////////////////////////////////////////////////////////////////////
745
746class PictureCloneRenderer : public PictureRenderer {
747public:
748 virtual bool render(const SkString* path) SK_OVERRIDE {
749 for (int i = 0; i < 100; ++i) {
750 SkPicture* clone = fPicture->clone();
751 SkSafeUnref(clone);
752 }
skia.committer@gmail.comc7b4be72012-12-11 02:01:20 +0000753
reed@google.com5a34fd32012-12-10 16:05:09 +0000754 return NULL == path; // we don't have anything to write
755 }
skia.committer@gmail.comc7b4be72012-12-11 02:01:20 +0000756
reed@google.com5a34fd32012-12-10 16:05:09 +0000757private:
758 virtual SkString getConfigNameInternal() SK_OVERRIDE {
759 return SkString("picture_clone");
760 }
761};
762
763PictureRenderer* CreatePictureCloneRenderer() {
764 return SkNEW(PictureCloneRenderer);
765}
766
junov@chromium.org9313ca42012-11-02 18:11:49 +0000767} // namespace sk_tools