blob: 69ce6f86fa2da57a55397def7c6c9967548a8773 [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.org7b537062012-11-06 18:58:43 +000027#include "SkTileGrid.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() {
101 return this->setupCanvas(fPicture->width(), fPicture->height());
102}
103
104SkCanvas* PictureRenderer::setupCanvas(int width, int height) {
caryclark@google.coma3622372012-11-06 21:26:13 +0000105 SkCanvas* canvas;
keyar@chromium.org4ea96c52012-08-20 15:03:29 +0000106 switch(fDeviceType) {
107 case kBitmap_DeviceType: {
108 SkBitmap bitmap;
keyar@chromium.orga474ce32012-08-20 15:03:57 +0000109 sk_tools::setup_bitmap(&bitmap, width, height);
caryclark@google.coma3622372012-11-06 21:26:13 +0000110 canvas = SkNEW_ARGS(SkCanvas, (bitmap));
111 return setUpFilter(canvas, fDrawFilters);
keyar@chromium.org4ea96c52012-08-20 15:03:29 +0000112 }
113#if SK_SUPPORT_GPU
114 case kGPU_DeviceType: {
keyar@chromium.org4ea96c52012-08-20 15:03:29 +0000115 SkAutoTUnref<SkGpuDevice> device(SkNEW_ARGS(SkGpuDevice,
keyar@chromium.org06125642012-08-20 15:03:33 +0000116 (fGrContext, SkBitmap::kARGB_8888_Config,
keyar@chromium.orga474ce32012-08-20 15:03:57 +0000117 width, height)));
caryclark@google.coma3622372012-11-06 21:26:13 +0000118 canvas = SkNEW_ARGS(SkCanvas, (device.get()));
119 return setUpFilter(canvas, fDrawFilters);
keyar@chromium.org4ea96c52012-08-20 15:03:29 +0000120 }
121#endif
122 default:
123 SkASSERT(0);
124 }
keyar@chromium.orga474ce32012-08-20 15:03:57 +0000125
126 return NULL;
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000127}
128
129void PictureRenderer::end() {
keyar@chromium.org77a55222012-08-20 15:03:47 +0000130 this->resetState();
junov@chromium.org9313ca42012-11-02 18:11:49 +0000131 SkSafeUnref(fPicture);
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000132 fPicture = NULL;
133 fCanvas.reset(NULL);
134}
135
junov@chromium.org9313ca42012-11-02 18:11:49 +0000136/** Converts fPicture to a picture that uses a BBoxHierarchy.
137 * PictureRenderer subclasses that are used to test picture playback
138 * should call this method during init.
139 */
140void PictureRenderer::buildBBoxHierarchy() {
141 SkASSERT(NULL != fPicture);
142 if (kNone_BBoxHierarchyType != fBBoxHierarchyType && NULL != fPicture) {
143 SkPicture* newPicture = this->createPicture();
144 SkCanvas* recorder = newPicture->beginRecording(fPicture->width(), fPicture->height(),
145 this->recordFlags());
146 fPicture->draw(recorder);
147 newPicture->endRecording();
148 fPicture->unref();
149 fPicture = newPicture;
150 }
151}
152
keyar@chromium.org77a55222012-08-20 15:03:47 +0000153void PictureRenderer::resetState() {
keyar@chromium.org28136b32012-08-20 15:04:15 +0000154#if SK_SUPPORT_GPU
155 if (this->isUsingGpuDevice()) {
156 SkGLContext* glContext = fGrContextFactory.getGLContext(
157 GrContextFactory::kNative_GLContextType);
keyar@chromium.org28136b32012-08-20 15:04:15 +0000158
159 SkASSERT(glContext != NULL);
160 if (NULL == glContext) {
161 return;
162 }
163
scroggo@google.com9a412522012-09-07 15:21:18 +0000164 fGrContext->flush();
keyar@chromium.org77a55222012-08-20 15:03:47 +0000165 SK_GL(*glContext, Finish());
keyar@chromium.org77a55222012-08-20 15:03:47 +0000166 }
keyar@chromium.orga40c20d2012-08-20 15:04:12 +0000167#endif
keyar@chromium.org77a55222012-08-20 15:03:47 +0000168}
169
junov@chromium.org9313ca42012-11-02 18:11:49 +0000170uint32_t PictureRenderer::recordFlags() {
171 return kNone_BBoxHierarchyType == fBBoxHierarchyType ? 0 :
172 SkPicture::kOptimizeForClippedPlayback_RecordingFlag;
173}
174
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000175/**
176 * Write the canvas to the specified path.
177 * @param canvas Must be non-null. Canvas to be written to a file.
178 * @param path Path for the file to be written. Should have no extension; write() will append
179 * an appropriate one. Passed in by value so it can be modified.
180 * @return bool True if the Canvas is written to a file.
181 */
182static bool write(SkCanvas* canvas, SkString path) {
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000183 SkASSERT(canvas != NULL);
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000184 if (NULL == canvas) {
keyar@chromium.org9299ede2012-08-21 19:05:08 +0000185 return false;
186 }
187
188 SkBitmap bitmap;
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000189 SkISize size = canvas->getDeviceSize();
190 sk_tools::setup_bitmap(&bitmap, size.width(), size.height());
keyar@chromium.org9299ede2012-08-21 19:05:08 +0000191
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000192 canvas->readPixels(&bitmap, 0, 0);
keyar@chromium.org9299ede2012-08-21 19:05:08 +0000193 sk_tools::force_all_opaque(bitmap);
194
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000195 // Since path is passed in by value, it is okay to modify it.
196 path.append(".png");
keyar@chromium.org9299ede2012-08-21 19:05:08 +0000197 return SkImageEncoder::EncodeFile(path.c_str(), bitmap, SkImageEncoder::kPNG_Type, 100);
198}
199
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000200/**
201 * If path is non NULL, append number to it, and call write(SkCanvas*, SkString) to write the
202 * provided canvas to a file. Returns true if path is NULL or if write() succeeds.
203 */
204static bool writeAppendNumber(SkCanvas* canvas, const SkString* path, int number) {
205 if (NULL == path) {
206 return true;
207 }
208 SkString pathWithNumber(*path);
209 pathWithNumber.appendf("%i", number);
210 return write(canvas, pathWithNumber);
211}
212
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000213///////////////////////////////////////////////////////////////////////////////////////////////
214
djsollen@google.comfd9720c2012-11-06 16:54:40 +0000215SkCanvas* RecordPictureRenderer::setupCanvas(int width, int height) {
216 // defer the canvas setup until the render step
217 return NULL;
218}
219
scroggo@google.coma9e3a362012-11-07 17:52:48 +0000220static bool PNGEncodeBitmapToStream(SkWStream* wStream, const SkBitmap& bm) {
221 return SkImageEncoder::EncodeStream(wStream, bm, SkImageEncoder::kPNG_Type, 100);
222}
223
224bool RecordPictureRenderer::render(const SkString* path) {
junov@chromium.org9313ca42012-11-02 18:11:49 +0000225 SkAutoTUnref<SkPicture> replayer(this->createPicture());
226 SkCanvas* recorder = replayer->beginRecording(fPicture->width(), fPicture->height(),
227 this->recordFlags());
scroggo@google.com9a412522012-09-07 15:21:18 +0000228 fPicture->draw(recorder);
junov@chromium.org9313ca42012-11-02 18:11:49 +0000229 replayer->endRecording();
scroggo@google.coma9e3a362012-11-07 17:52:48 +0000230 if (path != NULL) {
231 // Record the new picture as a new SKP with PNG encoded bitmaps.
232 SkString skpPath(*path);
233 // ".skp" was removed from 'path' before being passed in here.
234 skpPath.append(".skp");
235 SkFILEWStream stream(skpPath.c_str());
236 replayer->serialize(&stream, &PNGEncodeBitmapToStream);
237 return true;
238 }
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000239 return false;
scroggo@google.com9a412522012-09-07 15:21:18 +0000240}
241
scroggo@google.com0a049b82012-11-02 22:01:26 +0000242SkString RecordPictureRenderer::getConfigNameInternal() {
243 return SkString("record");
244}
245
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000246///////////////////////////////////////////////////////////////////////////////////////////////
247
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000248bool PipePictureRenderer::render(const SkString* path) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000249 SkASSERT(fCanvas.get() != NULL);
250 SkASSERT(fPicture != NULL);
keyar@chromium.org78a35c52012-08-20 15:03:44 +0000251 if (NULL == fCanvas.get() || NULL == fPicture) {
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000252 return false;
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000253 }
254
255 PipeController pipeController(fCanvas.get());
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000256 SkGPipeWriter writer;
257 SkCanvas* pipeCanvas = writer.startRecording(&pipeController);
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000258 pipeCanvas->drawPicture(*fPicture);
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000259 writer.endRecording();
scroggo@google.com9a412522012-09-07 15:21:18 +0000260 fCanvas->flush();
borenet@google.com070d3542012-10-26 13:26:55 +0000261 if (NULL != path) {
262 return write(fCanvas, *path);
263 }
264 return true;
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000265}
266
scroggo@google.com0a049b82012-11-02 22:01:26 +0000267SkString PipePictureRenderer::getConfigNameInternal() {
268 return SkString("pipe");
269}
270
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000271///////////////////////////////////////////////////////////////////////////////////////////////
272
junov@chromium.org9313ca42012-11-02 18:11:49 +0000273void SimplePictureRenderer::init(SkPicture* picture) {
274 INHERITED::init(picture);
275 this->buildBBoxHierarchy();
276}
277
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000278bool SimplePictureRenderer::render(const SkString* path) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000279 SkASSERT(fCanvas.get() != NULL);
280 SkASSERT(fPicture != NULL);
keyar@chromium.org78a35c52012-08-20 15:03:44 +0000281 if (NULL == fCanvas.get() || NULL == fPicture) {
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000282 return false;
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000283 }
284
285 fCanvas->drawPicture(*fPicture);
scroggo@google.com9a412522012-09-07 15:21:18 +0000286 fCanvas->flush();
borenet@google.com070d3542012-10-26 13:26:55 +0000287 if (NULL != path) {
288 return write(fCanvas, *path);
289 }
290 return true;
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000291}
292
scroggo@google.com0a049b82012-11-02 22:01:26 +0000293SkString SimplePictureRenderer::getConfigNameInternal() {
294 return SkString("simple");
295}
296
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000297///////////////////////////////////////////////////////////////////////////////////////////////
298
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000299TiledPictureRenderer::TiledPictureRenderer()
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000300 : fTileWidth(kDefaultTileWidth)
rileya@google.comb947b912012-08-29 17:35:07 +0000301 , fTileHeight(kDefaultTileHeight)
rileya@google.coma04dc022012-09-10 19:01:38 +0000302 , fTileWidthPercentage(0.0)
rileya@google.comb947b912012-08-29 17:35:07 +0000303 , fTileHeightPercentage(0.0)
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000304 , fTileMinPowerOf2Width(0) { }
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000305
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000306void TiledPictureRenderer::init(SkPicture* pict) {
307 SkASSERT(pict != NULL);
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000308 SkASSERT(0 == fTileRects.count());
309 if (NULL == pict || fTileRects.count() != 0) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000310 return;
311 }
312
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000313 // Do not call INHERITED::init(), which would create a (potentially large) canvas which is not
314 // used by bench_pictures.
315 fPicture = pict;
junov@chromium.org9313ca42012-11-02 18:11:49 +0000316 fPicture->ref();
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000317 this->buildBBoxHierarchy();
keyar@chromium.orgcc6e5ef2012-07-27 20:09:26 +0000318
319 if (fTileWidthPercentage > 0) {
robertphillips@google.com5d8d1862012-08-15 14:36:41 +0000320 fTileWidth = sk_float_ceil2int(float(fTileWidthPercentage * fPicture->width() / 100));
keyar@chromium.orgcc6e5ef2012-07-27 20:09:26 +0000321 }
322 if (fTileHeightPercentage > 0) {
robertphillips@google.com5d8d1862012-08-15 14:36:41 +0000323 fTileHeight = sk_float_ceil2int(float(fTileHeightPercentage * fPicture->height() / 100));
keyar@chromium.orgcc6e5ef2012-07-27 20:09:26 +0000324 }
325
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000326 if (fTileMinPowerOf2Width > 0) {
327 this->setupPowerOf2Tiles();
328 } else {
329 this->setupTiles();
330 }
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000331}
332
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000333void TiledPictureRenderer::end() {
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000334 fTileRects.reset();
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000335 this->INHERITED::end();
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000336}
337
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000338void TiledPictureRenderer::setupTiles() {
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000339 for (int tile_y_start = 0; tile_y_start < fPicture->height(); tile_y_start += fTileHeight) {
340 for (int tile_x_start = 0; tile_x_start < fPicture->width(); tile_x_start += fTileWidth) {
341 *fTileRects.append() = SkRect::MakeXYWH(SkIntToScalar(tile_x_start),
342 SkIntToScalar(tile_y_start),
343 SkIntToScalar(fTileWidth),
344 SkIntToScalar(fTileHeight));
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000345 }
346 }
347}
348
349// The goal of the powers of two tiles is to minimize the amount of wasted tile
350// space in the width-wise direction and then minimize the number of tiles. The
351// constraints are that every tile must have a pixel width that is a power of
352// two and also be of some minimal width (that is also a power of two).
353//
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000354// This is solved by first taking our picture size and rounding it up to the
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000355// multiple of the minimal width. The binary representation of this rounded
356// value gives us the tiles we need: a bit of value one means we need a tile of
357// that size.
358void TiledPictureRenderer::setupPowerOf2Tiles() {
359 int rounded_value = fPicture->width();
360 if (fPicture->width() % fTileMinPowerOf2Width != 0) {
361 rounded_value = fPicture->width() - (fPicture->width() % fTileMinPowerOf2Width)
362 + fTileMinPowerOf2Width;
363 }
364
robertphillips@google.com94acc702012-09-06 18:43:21 +0000365 int num_bits = SkScalarCeilToInt(SkScalarLog2(SkIntToScalar(fPicture->width())));
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000366 int largest_possible_tile_size = 1 << num_bits;
367
368 // The tile height is constant for a particular picture.
369 for (int tile_y_start = 0; tile_y_start < fPicture->height(); tile_y_start += fTileHeight) {
370 int tile_x_start = 0;
371 int current_width = largest_possible_tile_size;
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000372 // Set fTileWidth to be the width of the widest tile, so that each canvas is large enough
373 // to draw each tile.
374 fTileWidth = current_width;
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000375
376 while (current_width >= fTileMinPowerOf2Width) {
377 // It is very important this is a bitwise AND.
378 if (current_width & rounded_value) {
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000379 *fTileRects.append() = SkRect::MakeXYWH(SkIntToScalar(tile_x_start),
380 SkIntToScalar(tile_y_start),
381 SkIntToScalar(current_width),
382 SkIntToScalar(fTileHeight));
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000383 tile_x_start += current_width;
384 }
385
386 current_width >>= 1;
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000387 }
388 }
389}
390
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000391/**
392 * Draw the specified playback to the canvas translated to rectangle provided, so that this mini
393 * canvas represents the rectangle's portion of the overall picture.
394 * Saves and restores so that the initial clip and matrix return to their state before this function
395 * is called.
396 */
397template<class T>
398static void DrawTileToCanvas(SkCanvas* canvas, const SkRect& tileRect, T* playback) {
399 int saveCount = canvas->save();
400 // Translate so that we draw the correct portion of the picture
401 canvas->translate(-tileRect.fLeft, -tileRect.fTop);
402 playback->draw(canvas);
403 canvas->restoreToCount(saveCount);
404 canvas->flush();
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000405}
406
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000407///////////////////////////////////////////////////////////////////////////////////////////////
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000408
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000409bool TiledPictureRenderer::render(const SkString* path) {
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000410 SkASSERT(fPicture != NULL);
411 if (NULL == fPicture) {
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000412 return false;
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000413 }
414
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000415 // Reuse one canvas for all tiles.
416 SkCanvas* canvas = this->setupCanvas(fTileWidth, fTileHeight);
417 SkAutoUnref aur(canvas);
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000418
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000419 bool success = true;
420 for (int i = 0; i < fTileRects.count(); ++i) {
421 DrawTileToCanvas(canvas, fTileRects[i], fPicture);
422 if (NULL != path) {
423 success &= writeAppendNumber(canvas, path, i);
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000424 }
keyar@chromium.org163b5672012-08-01 17:53:29 +0000425 }
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000426 return success;
keyar@chromium.org163b5672012-08-01 17:53:29 +0000427}
428
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000429SkCanvas* TiledPictureRenderer::setupCanvas(int width, int height) {
430 SkCanvas* canvas = this->INHERITED::setupCanvas(width, height);
431 SkASSERT(fPicture != NULL);
432 // Clip the tile to an area that is completely in what the SkPicture says is the
433 // drawn-to area. This is mostly important for tiles on the right and bottom edges
434 // as they may go over this area and the picture may have some commands that
435 // draw outside of this area and so should not actually be written.
436 SkRect clip = SkRect::MakeWH(SkIntToScalar(fPicture->width()),
437 SkIntToScalar(fPicture->height()));
438 canvas->clipRect(clip);
439 return canvas;
440}
scroggo@google.com0a049b82012-11-02 22:01:26 +0000441
442SkString TiledPictureRenderer::getConfigNameInternal() {
443 SkString name;
444 if (fTileMinPowerOf2Width > 0) {
445 name.append("pow2tile_");
446 name.appendf("%i", fTileMinPowerOf2Width);
447 } else {
448 name.append("tile_");
449 if (fTileWidthPercentage > 0) {
450 name.appendf("%.f%%", fTileWidthPercentage);
451 } else {
452 name.appendf("%i", fTileWidth);
453 }
454 }
455 name.append("x");
456 if (fTileHeightPercentage > 0) {
457 name.appendf("%.f%%", fTileHeightPercentage);
458 } else {
459 name.appendf("%i", fTileHeight);
460 }
461 return name;
462}
463
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000464///////////////////////////////////////////////////////////////////////////////////////////////
465
466// Holds all of the information needed to draw a set of tiles.
467class CloneData : public SkRunnable {
468
469public:
470 CloneData(SkPicture* clone, SkCanvas* canvas, SkTDArray<SkRect>& rects, int start, int end,
471 SkRunnable* done)
472 : fClone(clone)
473 , fCanvas(canvas)
474 , fPath(NULL)
475 , fRects(rects)
476 , fStart(start)
477 , fEnd(end)
478 , fSuccess(NULL)
479 , fDone(done) {
480 SkASSERT(fDone != NULL);
481 }
482
483 virtual void run() SK_OVERRIDE {
484 SkGraphics::SetTLSFontCacheLimit(1024 * 1024);
485 for (int i = fStart; i < fEnd; i++) {
486 DrawTileToCanvas(fCanvas, fRects[i], fClone);
487 if (fPath != NULL && !writeAppendNumber(fCanvas, fPath, i)
488 && fSuccess != NULL) {
489 *fSuccess = false;
490 // If one tile fails to write to a file, do not continue drawing the rest.
491 break;
492 }
493 }
494 fDone->run();
495 }
496
497 void setPathAndSuccess(const SkString* path, bool* success) {
498 fPath = path;
499 fSuccess = success;
500 }
501
502private:
503 // All pointers unowned.
504 SkPicture* fClone; // Picture to draw from. Each CloneData has a unique one which
505 // is threadsafe.
506 SkCanvas* fCanvas; // Canvas to draw to. Reused for each tile.
507 const SkString* fPath; // If non-null, path to write the result to as a PNG.
508 SkTDArray<SkRect>& fRects; // All tiles of the picture.
509 const int fStart; // Range of tiles drawn by this thread.
510 const int fEnd;
511 bool* fSuccess; // Only meaningful if path is non-null. Shared by all threads,
512 // and only set to false upon failure to write to a PNG.
513 SkRunnable* fDone;
514};
515
516MultiCorePictureRenderer::MultiCorePictureRenderer(int threadCount)
517: fNumThreads(threadCount)
518, fThreadPool(threadCount)
519, fCountdown(threadCount) {
520 // Only need to create fNumThreads - 1 clones, since one thread will use the base
521 // picture.
522 fPictureClones = SkNEW_ARRAY(SkPicture, fNumThreads - 1);
523 fCloneData = SkNEW_ARRAY(CloneData*, fNumThreads);
524}
525
526void MultiCorePictureRenderer::init(SkPicture *pict) {
527 // Set fPicture and the tiles.
528 this->INHERITED::init(pict);
529 for (int i = 0; i < fNumThreads; ++i) {
530 *fCanvasPool.append() = this->setupCanvas(this->getTileWidth(), this->getTileHeight());
531 }
532 // Only need to create fNumThreads - 1 clones, since one thread will use the base picture.
533 fPicture->clone(fPictureClones, fNumThreads - 1);
534 // Populate each thread with the appropriate data.
535 // Group the tiles into nearly equal size chunks, rounding up so we're sure to cover them all.
536 const int chunkSize = (fTileRects.count() + fNumThreads - 1) / fNumThreads;
537
538 for (int i = 0; i < fNumThreads; i++) {
539 SkPicture* pic;
540 if (i == fNumThreads-1) {
541 // The last set will use the original SkPicture.
542 pic = fPicture;
543 } else {
544 pic = &fPictureClones[i];
545 }
546 const int start = i * chunkSize;
547 const int end = SkMin32(start + chunkSize, fTileRects.count());
548 fCloneData[i] = SkNEW_ARGS(CloneData,
549 (pic, fCanvasPool[i], fTileRects, start, end, &fCountdown));
550 }
551}
552
553bool MultiCorePictureRenderer::render(const SkString *path) {
554 bool success = true;
555 if (path != NULL) {
556 for (int i = 0; i < fNumThreads-1; i++) {
557 fCloneData[i]->setPathAndSuccess(path, &success);
558 }
559 }
560
561 fCountdown.reset(fNumThreads);
562 for (int i = 0; i < fNumThreads; i++) {
563 fThreadPool.add(fCloneData[i]);
564 }
565 fCountdown.wait();
566
567 return success;
568}
569
570void MultiCorePictureRenderer::end() {
571 for (int i = 0; i < fNumThreads - 1; i++) {
572 SkDELETE(fCloneData[i]);
573 fCloneData[i] = NULL;
574 }
575
576 fCanvasPool.unrefAll();
577
578 this->INHERITED::end();
579}
580
581MultiCorePictureRenderer::~MultiCorePictureRenderer() {
582 // Each individual CloneData was deleted in end.
583 SkDELETE_ARRAY(fCloneData);
584 SkDELETE_ARRAY(fPictureClones);
585}
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000586
scroggo@google.com0a049b82012-11-02 22:01:26 +0000587SkString MultiCorePictureRenderer::getConfigNameInternal() {
588 SkString name = this->INHERITED::getConfigNameInternal();
589 name.appendf("_multi_%i_threads", fNumThreads);
590 return name;
591}
592
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000593///////////////////////////////////////////////////////////////////////////////////////////////
scroggo@google.com9a412522012-09-07 15:21:18 +0000594
595void PlaybackCreationRenderer::setup() {
junov@chromium.org9313ca42012-11-02 18:11:49 +0000596 fReplayer.reset(this->createPicture());
597 SkCanvas* recorder = fReplayer->beginRecording(fPicture->width(), fPicture->height(),
598 this->recordFlags());
scroggo@google.com9a412522012-09-07 15:21:18 +0000599 fPicture->draw(recorder);
600}
601
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000602bool PlaybackCreationRenderer::render(const SkString*) {
junov@chromium.org9313ca42012-11-02 18:11:49 +0000603 fReplayer->endRecording();
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000604 // Since this class does not actually render, return false.
605 return false;
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000606}
607
scroggo@google.com0a049b82012-11-02 22:01:26 +0000608SkString PlaybackCreationRenderer::getConfigNameInternal() {
609 return SkString("playback_creation");
610}
611
junov@chromium.org9313ca42012-11-02 18:11:49 +0000612///////////////////////////////////////////////////////////////////////////////////////////////
613// SkPicture variants for each BBoxHierarchy type
614
615class RTreePicture : public SkPicture {
616public:
617 virtual SkBBoxHierarchy* createBBoxHierarchy() const SK_OVERRIDE{
618 static const int kRTreeMinChildren = 6;
619 static const int kRTreeMaxChildren = 11;
620 SkScalar aspectRatio = SkScalarDiv(SkIntToScalar(fWidth),
621 SkIntToScalar(fHeight));
622 return SkRTree::Create(kRTreeMinChildren, kRTreeMaxChildren,
623 aspectRatio);
624 }
625};
626
junov@chromium.org7b537062012-11-06 18:58:43 +0000627class TileGridPicture : public SkPicture {
628public:
629 TileGridPicture(int tileWidth, int tileHeight, int xTileCount, int yTileCount) {
630 fTileWidth = tileWidth;
631 fTileHeight = tileHeight;
632 fXTileCount = xTileCount;
633 fYTileCount = yTileCount;
634 }
635
636 virtual SkBBoxHierarchy* createBBoxHierarchy() const SK_OVERRIDE{
637 return SkNEW_ARGS(SkTileGrid, (fTileWidth, fTileHeight, fXTileCount, fYTileCount));
638 }
639private:
640 int fTileWidth, fTileHeight, fXTileCount, fYTileCount;
641};
642
junov@chromium.org9313ca42012-11-02 18:11:49 +0000643SkPicture* PictureRenderer::createPicture() {
644 switch (fBBoxHierarchyType) {
645 case kNone_BBoxHierarchyType:
646 return SkNEW(SkPicture);
647 case kRTree_BBoxHierarchyType:
648 return SkNEW(RTreePicture);
junov@chromium.org7b537062012-11-06 18:58:43 +0000649 case kTileGrid_BBoxHierarchyType:
650 {
651 int xTileCount = fPicture->width() / fGridWidth +
652 ((fPicture->width() % fGridWidth) ? 1 : 0);
653 int yTileCount = fPicture->height() / fGridHeight +
654 ((fPicture->height() % fGridHeight) ? 1 : 0);
655 return SkNEW_ARGS(TileGridPicture, (fGridWidth, fGridHeight, xTileCount,
656 yTileCount));
657 }
junov@chromium.org9313ca42012-11-02 18:11:49 +0000658 }
659 SkASSERT(0); // invalid bbhType
660 return NULL;
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000661}
junov@chromium.org9313ca42012-11-02 18:11:49 +0000662
reed@google.comfe7b1ed2012-11-29 21:00:39 +0000663///////////////////////////////////////////////////////////////////////////////
664
665class GatherRenderer : public PictureRenderer {
666public:
667 virtual bool render(const SkString* path) SK_OVERRIDE {
668 SkRect bounds = SkRect::MakeWH(SkIntToScalar(fPicture->width()),
669 SkIntToScalar(fPicture->height()));
670 SkData* data = SkPictureUtils::GatherPixelRefs(fPicture, bounds);
671 SkSafeUnref(data);
672
673 return NULL == path; // we don't have anything to write
674 }
675
676private:
677 virtual SkString getConfigNameInternal() SK_OVERRIDE {
678 return SkString("gather_pixelrefs");
679 }
680};
681
682PictureRenderer* CreateGatherPixelRefsRenderer() {
683 return SkNEW(GatherRenderer);
684}
685
junov@chromium.org9313ca42012-11-02 18:11:49 +0000686} // namespace sk_tools