blob: 274768b11866f7b9538b4f97091a964a93048c2a [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
robertphillips@google.comfe1b5362013-02-07 19:45:46 +000015#include "gl/GrGLDefines.h"
scroggo@google.com58b4ead2012-08-31 16:15:22 +000016#include "SkGpuDevice.h"
17#endif
18#include "SkGraphics.h"
19#include "SkImageEncoder.h"
caryclark@google.coma3622372012-11-06 21:26:13 +000020#include "SkMaskFilter.h"
keyar@chromium.orgea826952012-08-23 15:24:13 +000021#include "SkMatrix.h"
keyar@chromium.org451bb9f2012-07-26 17:27:57 +000022#include "SkPicture.h"
junov@chromium.org9313ca42012-11-02 18:11:49 +000023#include "SkRTree.h"
keyar@chromium.orgea826952012-08-23 15:24:13 +000024#include "SkScalar.h"
scroggo@google.coma9e3a362012-11-07 17:52:48 +000025#include "SkStream.h"
keyar@chromium.org9299ede2012-08-21 19:05:08 +000026#include "SkString.h"
scroggo@google.com58b4ead2012-08-31 16:15:22 +000027#include "SkTemplates.h"
junov@chromium.org3cb834b2012-12-13 16:39:53 +000028#include "SkTileGridPicture.h"
keyar@chromium.org451bb9f2012-07-26 17:27:57 +000029#include "SkTDArray.h"
scroggo@google.com58b4ead2012-08-31 16:15:22 +000030#include "SkThreadUtils.h"
keyar@chromium.org451bb9f2012-07-26 17:27:57 +000031#include "SkTypes.h"
reed@google.comfe7b1ed2012-11-29 21:00:39 +000032#include "SkData.h"
33#include "SkPictureUtils.h"
keyar@chromium.org4ea96c52012-08-20 15:03:29 +000034
keyar@chromium.org451bb9f2012-07-26 17:27:57 +000035namespace sk_tools {
36
37enum {
38 kDefaultTileWidth = 256,
39 kDefaultTileHeight = 256
40};
41
keyar@chromium.org9d696c02012-08-07 17:11:33 +000042void PictureRenderer::init(SkPicture* pict) {
keyar@chromium.org78a35c52012-08-20 15:03:44 +000043 SkASSERT(NULL == fPicture);
44 SkASSERT(NULL == fCanvas.get());
45 if (fPicture != NULL || NULL != fCanvas.get()) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +000046 return;
47 }
48
49 SkASSERT(pict != NULL);
keyar@chromium.org78a35c52012-08-20 15:03:44 +000050 if (NULL == pict) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +000051 return;
52 }
53
54 fPicture = pict;
junov@chromium.org9313ca42012-11-02 18:11:49 +000055 fPicture->ref();
keyar@chromium.orga474ce32012-08-20 15:03:57 +000056 fCanvas.reset(this->setupCanvas());
57}
58
caryclark@google.coma3622372012-11-06 21:26:13 +000059class FlagsDrawFilter : public SkDrawFilter {
60public:
61 FlagsDrawFilter(PictureRenderer::DrawFilterFlags* flags) :
62 fFlags(flags) {}
63
reed@google.com971aca72012-11-26 20:26:54 +000064 virtual bool filter(SkPaint* paint, Type t) {
caryclark@google.coma3622372012-11-06 21:26:13 +000065 paint->setFlags(paint->getFlags() & ~fFlags[t] & SkPaint::kAllFlags);
reed@google.com457d8a72012-12-18 18:20:44 +000066 if (PictureRenderer::kBlur_DrawFilterFlag & 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)) {
reed@google.com457d8a72012-12-18 18:20:44 +000070 paint->setMaskFilter(NULL);
caryclark@google.coma3622372012-11-06 21:26:13 +000071 }
72 }
73 if (PictureRenderer::kHinting_DrawFilterFlag & fFlags[t]) {
74 paint->setHinting(SkPaint::kNo_Hinting);
75 } else if (PictureRenderer::kSlightHinting_DrawFilterFlag & fFlags[t]) {
76 paint->setHinting(SkPaint::kSlight_Hinting);
77 }
reed@google.com971aca72012-11-26 20:26:54 +000078 return true;
caryclark@google.coma3622372012-11-06 21:26:13 +000079 }
80
81private:
82 PictureRenderer::DrawFilterFlags* fFlags;
83};
84
scroggo@google.com82ec0b02012-12-17 19:25:54 +000085static void setUpFilter(SkCanvas* canvas, PictureRenderer::DrawFilterFlags* drawFilters) {
caryclark@google.coma3622372012-11-06 21:26:13 +000086 if (drawFilters && !canvas->getDrawFilter()) {
87 canvas->setDrawFilter(SkNEW_ARGS(FlagsDrawFilter, (drawFilters)))->unref();
caryclark@google.come3e940c2012-11-07 16:42:17 +000088 if (drawFilters[0] & PictureRenderer::kAAClip_DrawFilterFlag) {
89 canvas->setAllowSoftClip(false);
90 }
caryclark@google.coma3622372012-11-06 21:26:13 +000091 }
caryclark@google.coma3622372012-11-06 21:26:13 +000092}
93
keyar@chromium.orga474ce32012-08-20 15:03:57 +000094SkCanvas* PictureRenderer::setupCanvas() {
scroggo@google.comc0d5e542012-12-13 21:40:48 +000095 const int width = this->getViewWidth();
96 const int height = this->getViewHeight();
97 return this->setupCanvas(width, height);
keyar@chromium.orga474ce32012-08-20 15:03:57 +000098}
99
100SkCanvas* PictureRenderer::setupCanvas(int width, int height) {
caryclark@google.coma3622372012-11-06 21:26:13 +0000101 SkCanvas* canvas;
keyar@chromium.org4ea96c52012-08-20 15:03:29 +0000102 switch(fDeviceType) {
103 case kBitmap_DeviceType: {
104 SkBitmap bitmap;
keyar@chromium.orga474ce32012-08-20 15:03:57 +0000105 sk_tools::setup_bitmap(&bitmap, width, height);
caryclark@google.coma3622372012-11-06 21:26:13 +0000106 canvas = SkNEW_ARGS(SkCanvas, (bitmap));
keyar@chromium.org4ea96c52012-08-20 15:03:29 +0000107 }
scroggo@google.com82ec0b02012-12-17 19:25:54 +0000108 break;
keyar@chromium.org4ea96c52012-08-20 15:03:29 +0000109#if SK_SUPPORT_GPU
scroggo@google.com0556ea02013-02-08 19:38:21 +0000110#if SK_ANGLE
111 case kAngle_DeviceType:
112 // fall through
113#endif
keyar@chromium.org4ea96c52012-08-20 15:03:29 +0000114 case kGPU_DeviceType: {
commit-bot@chromium.orgae403b92013-04-10 17:27:30 +0000115 SkAutoTUnref<GrSurface> target;
scroggo@google.com0556ea02013-02-08 19:38:21 +0000116 if (fGrContext) {
117 // create a render target to back the device
118 GrTextureDesc desc;
119 desc.fConfig = kSkia8888_GrPixelConfig;
120 desc.fFlags = kRenderTarget_GrTextureFlagBit;
121 desc.fWidth = width;
122 desc.fHeight = height;
123 desc.fSampleCnt = 0;
commit-bot@chromium.orgae403b92013-04-10 17:27:30 +0000124 target.reset(fGrContext->createUncachedTexture(desc, NULL, 0));
scroggo@google.com0556ea02013-02-08 19:38:21 +0000125 }
commit-bot@chromium.orgae403b92013-04-10 17:27:30 +0000126 if (NULL == target.get()) {
scroggo@google.com0556ea02013-02-08 19:38:21 +0000127 SkASSERT(0);
128 return NULL;
129 }
130
commit-bot@chromium.orgae403b92013-04-10 17:27:30 +0000131 SkAutoTUnref<SkGpuDevice> device(SkGpuDevice::Create(target));
caryclark@google.coma3622372012-11-06 21:26:13 +0000132 canvas = SkNEW_ARGS(SkCanvas, (device.get()));
scroggo@google.com0556ea02013-02-08 19:38:21 +0000133 break;
keyar@chromium.org4ea96c52012-08-20 15:03:29 +0000134 }
135#endif
136 default:
137 SkASSERT(0);
scroggo@google.com82ec0b02012-12-17 19:25:54 +0000138 return NULL;
keyar@chromium.org4ea96c52012-08-20 15:03:29 +0000139 }
scroggo@google.com82ec0b02012-12-17 19:25:54 +0000140 setUpFilter(canvas, fDrawFilters);
141 this->scaleToScaleFactor(canvas);
142 return canvas;
143}
keyar@chromium.orga474ce32012-08-20 15:03:57 +0000144
scroggo@google.com82ec0b02012-12-17 19:25:54 +0000145void PictureRenderer::scaleToScaleFactor(SkCanvas* canvas) {
146 SkASSERT(canvas != NULL);
147 if (fScaleFactor != SK_Scalar1) {
148 canvas->scale(fScaleFactor, fScaleFactor);
149 }
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000150}
151
152void PictureRenderer::end() {
scroggo@google.com08085f82013-01-28 20:40:24 +0000153 this->resetState(true);
junov@chromium.org9313ca42012-11-02 18:11:49 +0000154 SkSafeUnref(fPicture);
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000155 fPicture = NULL;
156 fCanvas.reset(NULL);
157}
158
scroggo@google.comc0d5e542012-12-13 21:40:48 +0000159int PictureRenderer::getViewWidth() {
160 SkASSERT(fPicture != NULL);
robertphillips@google.com8ac811e2013-02-07 00:13:34 +0000161 int width = SkScalarCeilToInt(fPicture->width() * fScaleFactor);
scroggo@google.comc0d5e542012-12-13 21:40:48 +0000162 if (fViewport.width() > 0) {
163 width = SkMin32(width, fViewport.width());
164 }
165 return width;
166}
167
168int PictureRenderer::getViewHeight() {
169 SkASSERT(fPicture != NULL);
robertphillips@google.com8ac811e2013-02-07 00:13:34 +0000170 int height = SkScalarCeilToInt(fPicture->height() * fScaleFactor);
scroggo@google.comc0d5e542012-12-13 21:40:48 +0000171 if (fViewport.height() > 0) {
172 height = SkMin32(height, fViewport.height());
173 }
174 return height;
175}
176
junov@chromium.org9313ca42012-11-02 18:11:49 +0000177/** Converts fPicture to a picture that uses a BBoxHierarchy.
178 * PictureRenderer subclasses that are used to test picture playback
179 * should call this method during init.
180 */
181void PictureRenderer::buildBBoxHierarchy() {
182 SkASSERT(NULL != fPicture);
183 if (kNone_BBoxHierarchyType != fBBoxHierarchyType && NULL != fPicture) {
184 SkPicture* newPicture = this->createPicture();
185 SkCanvas* recorder = newPicture->beginRecording(fPicture->width(), fPicture->height(),
186 this->recordFlags());
187 fPicture->draw(recorder);
188 newPicture->endRecording();
189 fPicture->unref();
190 fPicture = newPicture;
191 }
192}
193
scroggo@google.com08085f82013-01-28 20:40:24 +0000194void PictureRenderer::resetState(bool callFinish) {
keyar@chromium.org28136b32012-08-20 15:04:15 +0000195#if SK_SUPPORT_GPU
robertphillips@google.com6177e692013-02-28 20:16:25 +0000196 SkGLContextHelper* glContext = this->getGLContext();
scroggo@google.com0556ea02013-02-08 19:38:21 +0000197 if (NULL == glContext) {
198 SkASSERT(kBitmap_DeviceType == fDeviceType);
199 return;
200 }
keyar@chromium.org28136b32012-08-20 15:04:15 +0000201
scroggo@google.com0556ea02013-02-08 19:38:21 +0000202 fGrContext->flush();
203 if (callFinish) {
204 SK_GL(*glContext, Finish());
keyar@chromium.org77a55222012-08-20 15:03:47 +0000205 }
keyar@chromium.orga40c20d2012-08-20 15:04:12 +0000206#endif
keyar@chromium.org77a55222012-08-20 15:03:47 +0000207}
208
junov@chromium.org9313ca42012-11-02 18:11:49 +0000209uint32_t PictureRenderer::recordFlags() {
junov@chromium.org100b1c52013-01-16 20:12:22 +0000210 return ((kNone_BBoxHierarchyType == fBBoxHierarchyType) ? 0 :
211 SkPicture::kOptimizeForClippedPlayback_RecordingFlag) |
212 SkPicture::kUsePathBoundsForClip_RecordingFlag;
junov@chromium.org9313ca42012-11-02 18:11:49 +0000213}
214
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000215/**
216 * Write the canvas to the specified path.
217 * @param canvas Must be non-null. Canvas to be written to a file.
218 * @param path Path for the file to be written. Should have no extension; write() will append
219 * an appropriate one. Passed in by value so it can be modified.
220 * @return bool True if the Canvas is written to a file.
221 */
222static bool write(SkCanvas* canvas, SkString path) {
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000223 SkASSERT(canvas != NULL);
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000224 if (NULL == canvas) {
keyar@chromium.org9299ede2012-08-21 19:05:08 +0000225 return false;
226 }
227
228 SkBitmap bitmap;
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000229 SkISize size = canvas->getDeviceSize();
230 sk_tools::setup_bitmap(&bitmap, size.width(), size.height());
keyar@chromium.org9299ede2012-08-21 19:05:08 +0000231
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000232 canvas->readPixels(&bitmap, 0, 0);
keyar@chromium.org9299ede2012-08-21 19:05:08 +0000233 sk_tools::force_all_opaque(bitmap);
234
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000235 // Since path is passed in by value, it is okay to modify it.
236 path.append(".png");
keyar@chromium.org9299ede2012-08-21 19:05:08 +0000237 return SkImageEncoder::EncodeFile(path.c_str(), bitmap, SkImageEncoder::kPNG_Type, 100);
238}
239
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000240/**
241 * If path is non NULL, append number to it, and call write(SkCanvas*, SkString) to write the
242 * provided canvas to a file. Returns true if path is NULL or if write() succeeds.
243 */
244static bool writeAppendNumber(SkCanvas* canvas, const SkString* path, int number) {
245 if (NULL == path) {
246 return true;
247 }
248 SkString pathWithNumber(*path);
249 pathWithNumber.appendf("%i", number);
250 return write(canvas, pathWithNumber);
251}
252
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000253///////////////////////////////////////////////////////////////////////////////////////////////
254
djsollen@google.comfd9720c2012-11-06 16:54:40 +0000255SkCanvas* RecordPictureRenderer::setupCanvas(int width, int height) {
256 // defer the canvas setup until the render step
257 return NULL;
258}
259
scroggo@google.coma9e3a362012-11-07 17:52:48 +0000260static bool PNGEncodeBitmapToStream(SkWStream* wStream, const SkBitmap& bm) {
261 return SkImageEncoder::EncodeStream(wStream, bm, SkImageEncoder::kPNG_Type, 100);
262}
263
edisonn@google.com84f548c2012-12-18 22:24:03 +0000264bool RecordPictureRenderer::render(const SkString* path, SkBitmap** out) {
junov@chromium.org9313ca42012-11-02 18:11:49 +0000265 SkAutoTUnref<SkPicture> replayer(this->createPicture());
scroggo@google.com82ec0b02012-12-17 19:25:54 +0000266 SkCanvas* recorder = replayer->beginRecording(this->getViewWidth(), this->getViewHeight(),
junov@chromium.org9313ca42012-11-02 18:11:49 +0000267 this->recordFlags());
scroggo@google.com82ec0b02012-12-17 19:25:54 +0000268 this->scaleToScaleFactor(recorder);
scroggo@google.com9a412522012-09-07 15:21:18 +0000269 fPicture->draw(recorder);
junov@chromium.org9313ca42012-11-02 18:11:49 +0000270 replayer->endRecording();
scroggo@google.coma9e3a362012-11-07 17:52:48 +0000271 if (path != NULL) {
272 // Record the new picture as a new SKP with PNG encoded bitmaps.
273 SkString skpPath(*path);
274 // ".skp" was removed from 'path' before being passed in here.
275 skpPath.append(".skp");
276 SkFILEWStream stream(skpPath.c_str());
277 replayer->serialize(&stream, &PNGEncodeBitmapToStream);
278 return true;
279 }
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000280 return false;
scroggo@google.com9a412522012-09-07 15:21:18 +0000281}
282
scroggo@google.com0a049b82012-11-02 22:01:26 +0000283SkString RecordPictureRenderer::getConfigNameInternal() {
284 return SkString("record");
285}
286
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000287///////////////////////////////////////////////////////////////////////////////////////////////
288
edisonn@google.com84f548c2012-12-18 22:24:03 +0000289bool PipePictureRenderer::render(const SkString* path, SkBitmap** out) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000290 SkASSERT(fCanvas.get() != NULL);
291 SkASSERT(fPicture != NULL);
keyar@chromium.org78a35c52012-08-20 15:03:44 +0000292 if (NULL == fCanvas.get() || NULL == fPicture) {
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000293 return false;
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000294 }
295
296 PipeController pipeController(fCanvas.get());
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000297 SkGPipeWriter writer;
298 SkCanvas* pipeCanvas = writer.startRecording(&pipeController);
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000299 pipeCanvas->drawPicture(*fPicture);
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000300 writer.endRecording();
scroggo@google.com9a412522012-09-07 15:21:18 +0000301 fCanvas->flush();
borenet@google.com070d3542012-10-26 13:26:55 +0000302 if (NULL != path) {
303 return write(fCanvas, *path);
304 }
edisonn@google.com84f548c2012-12-18 22:24:03 +0000305 if (NULL != out) {
306 *out = SkNEW(SkBitmap);
307 setup_bitmap(*out, fPicture->width(), fPicture->height());
308 fCanvas->readPixels(*out, 0, 0);
skia.committer@gmail.coma7d8e3e2012-12-19 02:01:38 +0000309 }
borenet@google.com070d3542012-10-26 13:26:55 +0000310 return true;
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000311}
312
scroggo@google.com0a049b82012-11-02 22:01:26 +0000313SkString PipePictureRenderer::getConfigNameInternal() {
314 return SkString("pipe");
315}
316
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000317///////////////////////////////////////////////////////////////////////////////////////////////
318
junov@chromium.org9313ca42012-11-02 18:11:49 +0000319void SimplePictureRenderer::init(SkPicture* picture) {
320 INHERITED::init(picture);
321 this->buildBBoxHierarchy();
322}
323
edisonn@google.com84f548c2012-12-18 22:24:03 +0000324bool SimplePictureRenderer::render(const SkString* path, SkBitmap** out) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000325 SkASSERT(fCanvas.get() != NULL);
326 SkASSERT(fPicture != NULL);
keyar@chromium.org78a35c52012-08-20 15:03:44 +0000327 if (NULL == fCanvas.get() || NULL == fPicture) {
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000328 return false;
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000329 }
330
331 fCanvas->drawPicture(*fPicture);
scroggo@google.com9a412522012-09-07 15:21:18 +0000332 fCanvas->flush();
borenet@google.com070d3542012-10-26 13:26:55 +0000333 if (NULL != path) {
334 return write(fCanvas, *path);
335 }
skia.committer@gmail.coma7d8e3e2012-12-19 02:01:38 +0000336
edisonn@google.com84f548c2012-12-18 22:24:03 +0000337 if (NULL != out) {
338 *out = SkNEW(SkBitmap);
339 setup_bitmap(*out, fPicture->width(), fPicture->height());
340 fCanvas->readPixels(*out, 0, 0);
341 }
342
borenet@google.com070d3542012-10-26 13:26:55 +0000343 return true;
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000344}
345
scroggo@google.com0a049b82012-11-02 22:01:26 +0000346SkString SimplePictureRenderer::getConfigNameInternal() {
347 return SkString("simple");
348}
349
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000350///////////////////////////////////////////////////////////////////////////////////////////////
351
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000352TiledPictureRenderer::TiledPictureRenderer()
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000353 : fTileWidth(kDefaultTileWidth)
rileya@google.comb947b912012-08-29 17:35:07 +0000354 , fTileHeight(kDefaultTileHeight)
rileya@google.coma04dc022012-09-10 19:01:38 +0000355 , fTileWidthPercentage(0.0)
rileya@google.comb947b912012-08-29 17:35:07 +0000356 , fTileHeightPercentage(0.0)
scroggo@google.comcbcef702012-12-13 22:09:28 +0000357 , fTileMinPowerOf2Width(0)
358 , fCurrentTileOffset(-1)
359 , fTilesX(0)
360 , fTilesY(0) { }
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000361
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000362void TiledPictureRenderer::init(SkPicture* pict) {
363 SkASSERT(pict != NULL);
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000364 SkASSERT(0 == fTileRects.count());
365 if (NULL == pict || fTileRects.count() != 0) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000366 return;
367 }
368
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000369 // Do not call INHERITED::init(), which would create a (potentially large) canvas which is not
370 // used by bench_pictures.
371 fPicture = pict;
junov@chromium.org9313ca42012-11-02 18:11:49 +0000372 fPicture->ref();
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000373 this->buildBBoxHierarchy();
keyar@chromium.orgcc6e5ef2012-07-27 20:09:26 +0000374
375 if (fTileWidthPercentage > 0) {
robertphillips@google.com5d8d1862012-08-15 14:36:41 +0000376 fTileWidth = sk_float_ceil2int(float(fTileWidthPercentage * fPicture->width() / 100));
keyar@chromium.orgcc6e5ef2012-07-27 20:09:26 +0000377 }
378 if (fTileHeightPercentage > 0) {
robertphillips@google.com5d8d1862012-08-15 14:36:41 +0000379 fTileHeight = sk_float_ceil2int(float(fTileHeightPercentage * fPicture->height() / 100));
keyar@chromium.orgcc6e5ef2012-07-27 20:09:26 +0000380 }
381
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000382 if (fTileMinPowerOf2Width > 0) {
383 this->setupPowerOf2Tiles();
384 } else {
385 this->setupTiles();
386 }
scroggo@google.comcbcef702012-12-13 22:09:28 +0000387 fCanvas.reset(this->setupCanvas(fTileWidth, fTileHeight));
388 // Initialize to -1 so that the first call to nextTile will set this up to draw tile 0 on the
389 // first call to drawCurrentTile.
390 fCurrentTileOffset = -1;
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000391}
392
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000393void TiledPictureRenderer::end() {
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000394 fTileRects.reset();
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000395 this->INHERITED::end();
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000396}
397
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000398void TiledPictureRenderer::setupTiles() {
scroggo@google.comc0d5e542012-12-13 21:40:48 +0000399 // Only use enough tiles to cover the viewport
400 const int width = this->getViewWidth();
401 const int height = this->getViewHeight();
402
scroggo@google.comcbcef702012-12-13 22:09:28 +0000403 fTilesX = fTilesY = 0;
scroggo@google.comc0d5e542012-12-13 21:40:48 +0000404 for (int tile_y_start = 0; tile_y_start < height; tile_y_start += fTileHeight) {
scroggo@google.comcbcef702012-12-13 22:09:28 +0000405 fTilesY++;
scroggo@google.comc0d5e542012-12-13 21:40:48 +0000406 for (int tile_x_start = 0; tile_x_start < width; tile_x_start += fTileWidth) {
scroggo@google.comcbcef702012-12-13 22:09:28 +0000407 if (0 == tile_y_start) {
408 // Only count tiles in the X direction on the first pass.
409 fTilesX++;
410 }
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000411 *fTileRects.append() = SkRect::MakeXYWH(SkIntToScalar(tile_x_start),
412 SkIntToScalar(tile_y_start),
413 SkIntToScalar(fTileWidth),
414 SkIntToScalar(fTileHeight));
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000415 }
416 }
417}
418
scroggo@google.comcbcef702012-12-13 22:09:28 +0000419bool TiledPictureRenderer::tileDimensions(int &x, int &y) {
420 if (fTileRects.count() == 0 || NULL == fPicture) {
421 return false;
422 }
423 x = fTilesX;
424 y = fTilesY;
425 return true;
426}
427
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000428// The goal of the powers of two tiles is to minimize the amount of wasted tile
429// space in the width-wise direction and then minimize the number of tiles. The
430// constraints are that every tile must have a pixel width that is a power of
431// two and also be of some minimal width (that is also a power of two).
432//
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000433// This is solved by first taking our picture size and rounding it up to the
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000434// multiple of the minimal width. The binary representation of this rounded
435// value gives us the tiles we need: a bit of value one means we need a tile of
436// that size.
437void TiledPictureRenderer::setupPowerOf2Tiles() {
scroggo@google.comc0d5e542012-12-13 21:40:48 +0000438 // Only use enough tiles to cover the viewport
439 const int width = this->getViewWidth();
440 const int height = this->getViewHeight();
441
442 int rounded_value = width;
443 if (width % fTileMinPowerOf2Width != 0) {
444 rounded_value = width - (width % fTileMinPowerOf2Width) + fTileMinPowerOf2Width;
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000445 }
446
scroggo@google.comc0d5e542012-12-13 21:40:48 +0000447 int num_bits = SkScalarCeilToInt(SkScalarLog2(SkIntToScalar(width)));
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000448 int largest_possible_tile_size = 1 << num_bits;
449
scroggo@google.comcbcef702012-12-13 22:09:28 +0000450 fTilesX = fTilesY = 0;
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000451 // The tile height is constant for a particular picture.
scroggo@google.comc0d5e542012-12-13 21:40:48 +0000452 for (int tile_y_start = 0; tile_y_start < height; tile_y_start += fTileHeight) {
scroggo@google.comcbcef702012-12-13 22:09:28 +0000453 fTilesY++;
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000454 int tile_x_start = 0;
455 int current_width = largest_possible_tile_size;
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000456 // Set fTileWidth to be the width of the widest tile, so that each canvas is large enough
457 // to draw each tile.
458 fTileWidth = current_width;
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000459
460 while (current_width >= fTileMinPowerOf2Width) {
461 // It is very important this is a bitwise AND.
462 if (current_width & rounded_value) {
scroggo@google.comcbcef702012-12-13 22:09:28 +0000463 if (0 == tile_y_start) {
464 // Only count tiles in the X direction on the first pass.
465 fTilesX++;
466 }
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000467 *fTileRects.append() = SkRect::MakeXYWH(SkIntToScalar(tile_x_start),
468 SkIntToScalar(tile_y_start),
469 SkIntToScalar(current_width),
470 SkIntToScalar(fTileHeight));
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000471 tile_x_start += current_width;
472 }
473
474 current_width >>= 1;
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000475 }
476 }
477}
478
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000479/**
480 * Draw the specified playback to the canvas translated to rectangle provided, so that this mini
481 * canvas represents the rectangle's portion of the overall picture.
482 * Saves and restores so that the initial clip and matrix return to their state before this function
483 * is called.
484 */
485template<class T>
486static void DrawTileToCanvas(SkCanvas* canvas, const SkRect& tileRect, T* playback) {
487 int saveCount = canvas->save();
scroggo@google.com82ec0b02012-12-17 19:25:54 +0000488 // Translate so that we draw the correct portion of the picture.
489 // Perform a postTranslate so that the scaleFactor does not interfere with the positioning.
490 SkMatrix mat(canvas->getTotalMatrix());
491 mat.postTranslate(-tileRect.fLeft, -tileRect.fTop);
492 canvas->setMatrix(mat);
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000493 playback->draw(canvas);
494 canvas->restoreToCount(saveCount);
495 canvas->flush();
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000496}
497
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000498///////////////////////////////////////////////////////////////////////////////////////////////
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000499
edisonn@google.com84f548c2012-12-18 22:24:03 +0000500static void bitmapCopySubset(const SkBitmap& src, SkBitmap* dst, int xDst,
501 int yDst) {
502 for (int y = 0; y <src.height() && y + yDst < dst->height() ; y++) {
503 for (int x = 0; x < src.width() && x + xDst < dst->width() ; x++) {
504 *dst->getAddr32(xDst + x, yDst + y) = *src.getAddr32(x, y);
505 }
506 }
507}
508
scroggo@google.comcbcef702012-12-13 22:09:28 +0000509bool TiledPictureRenderer::nextTile(int &i, int &j) {
510 if (++fCurrentTileOffset < fTileRects.count()) {
511 i = fCurrentTileOffset % fTilesX;
512 j = fCurrentTileOffset / fTilesX;
513 return true;
514 }
515 return false;
516}
517
518void TiledPictureRenderer::drawCurrentTile() {
519 SkASSERT(fCurrentTileOffset >= 0 && fCurrentTileOffset < fTileRects.count());
520 DrawTileToCanvas(fCanvas, fTileRects[fCurrentTileOffset], fPicture);
521}
522
edisonn@google.com84f548c2012-12-18 22:24:03 +0000523bool TiledPictureRenderer::render(const SkString* path, SkBitmap** out) {
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000524 SkASSERT(fPicture != NULL);
525 if (NULL == fPicture) {
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000526 return false;
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000527 }
528
edisonn@google.com84f548c2012-12-18 22:24:03 +0000529 SkBitmap bitmap;
530 if (out){
531 *out = SkNEW(SkBitmap);
532 setup_bitmap(*out, fPicture->width(), fPicture->height());
533 setup_bitmap(&bitmap, fTileWidth, fTileHeight);
534 }
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000535 bool success = true;
536 for (int i = 0; i < fTileRects.count(); ++i) {
scroggo@google.comcbcef702012-12-13 22:09:28 +0000537 DrawTileToCanvas(fCanvas, fTileRects[i], fPicture);
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000538 if (NULL != path) {
scroggo@google.comcbcef702012-12-13 22:09:28 +0000539 success &= writeAppendNumber(fCanvas, path, i);
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000540 }
edisonn@google.com84f548c2012-12-18 22:24:03 +0000541 if (NULL != out) {
542 if (fCanvas->readPixels(&bitmap, 0, 0)) {
jvanverth@google.com9c4e5ac2013-01-07 18:41:28 +0000543 bitmapCopySubset(bitmap, *out, SkScalarFloorToInt(fTileRects[i].left()),
544 SkScalarFloorToInt(fTileRects[i].top()));
edisonn@google.com84f548c2012-12-18 22:24:03 +0000545 } else {
546 success = false;
547 }
548 }
keyar@chromium.org163b5672012-08-01 17:53:29 +0000549 }
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000550 return success;
keyar@chromium.org163b5672012-08-01 17:53:29 +0000551}
552
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000553SkCanvas* TiledPictureRenderer::setupCanvas(int width, int height) {
554 SkCanvas* canvas = this->INHERITED::setupCanvas(width, height);
555 SkASSERT(fPicture != NULL);
scroggo@google.com82ec0b02012-12-17 19:25:54 +0000556 // Clip the tile to an area that is completely inside both the SkPicture and the viewport. This
557 // is mostly important for tiles on the right and bottom edges as they may go over this area and
558 // the picture may have some commands that draw outside of this area and so should not actually
559 // be written.
560 // Uses a clipRegion so that it will be unaffected by the scale factor, which may have been set
561 // by INHERITED::setupCanvas.
562 SkRegion clipRegion;
563 clipRegion.setRect(0, 0, this->getViewWidth(), this->getViewHeight());
564 canvas->clipRegion(clipRegion);
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000565 return canvas;
566}
scroggo@google.com0a049b82012-11-02 22:01:26 +0000567
568SkString TiledPictureRenderer::getConfigNameInternal() {
569 SkString name;
570 if (fTileMinPowerOf2Width > 0) {
571 name.append("pow2tile_");
572 name.appendf("%i", fTileMinPowerOf2Width);
573 } else {
574 name.append("tile_");
575 if (fTileWidthPercentage > 0) {
576 name.appendf("%.f%%", fTileWidthPercentage);
577 } else {
578 name.appendf("%i", fTileWidth);
579 }
580 }
581 name.append("x");
582 if (fTileHeightPercentage > 0) {
583 name.appendf("%.f%%", fTileHeightPercentage);
584 } else {
585 name.appendf("%i", fTileHeight);
586 }
587 return name;
588}
589
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000590///////////////////////////////////////////////////////////////////////////////////////////////
591
592// Holds all of the information needed to draw a set of tiles.
593class CloneData : public SkRunnable {
594
595public:
596 CloneData(SkPicture* clone, SkCanvas* canvas, SkTDArray<SkRect>& rects, int start, int end,
597 SkRunnable* done)
598 : fClone(clone)
599 , fCanvas(canvas)
600 , fPath(NULL)
601 , fRects(rects)
602 , fStart(start)
603 , fEnd(end)
604 , fSuccess(NULL)
605 , fDone(done) {
606 SkASSERT(fDone != NULL);
607 }
608
609 virtual void run() SK_OVERRIDE {
610 SkGraphics::SetTLSFontCacheLimit(1024 * 1024);
edisonn@google.com84f548c2012-12-18 22:24:03 +0000611
612 SkBitmap bitmap;
613 if (fBitmap != NULL) {
614 // All tiles are the same size.
jvanverth@google.com9c4e5ac2013-01-07 18:41:28 +0000615 setup_bitmap(&bitmap, SkScalarFloorToInt(fRects[0].width()), SkScalarFloorToInt(fRects[0].height()));
edisonn@google.com84f548c2012-12-18 22:24:03 +0000616 }
skia.committer@gmail.coma7d8e3e2012-12-19 02:01:38 +0000617
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000618 for (int i = fStart; i < fEnd; i++) {
619 DrawTileToCanvas(fCanvas, fRects[i], fClone);
620 if (fPath != NULL && !writeAppendNumber(fCanvas, fPath, i)
621 && fSuccess != NULL) {
622 *fSuccess = false;
623 // If one tile fails to write to a file, do not continue drawing the rest.
624 break;
625 }
edisonn@google.com84f548c2012-12-18 22:24:03 +0000626 if (fBitmap != NULL) {
627 if (fCanvas->readPixels(&bitmap, 0, 0)) {
628 SkAutoLockPixels alp(*fBitmap);
jvanverth@google.com9c4e5ac2013-01-07 18:41:28 +0000629 bitmapCopySubset(bitmap, fBitmap, SkScalarFloorToInt(fRects[i].left()),
630 SkScalarFloorToInt(fRects[i].top()));
edisonn@google.com84f548c2012-12-18 22:24:03 +0000631 } else {
632 *fSuccess = false;
633 // If one tile fails to read pixels, do not continue drawing the rest.
634 break;
635 }
636 }
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000637 }
638 fDone->run();
639 }
640
641 void setPathAndSuccess(const SkString* path, bool* success) {
642 fPath = path;
643 fSuccess = success;
644 }
645
edisonn@google.com84f548c2012-12-18 22:24:03 +0000646 void setBitmap(SkBitmap* bitmap) {
647 fBitmap = bitmap;
648 }
649
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000650private:
651 // All pointers unowned.
652 SkPicture* fClone; // Picture to draw from. Each CloneData has a unique one which
653 // is threadsafe.
654 SkCanvas* fCanvas; // Canvas to draw to. Reused for each tile.
655 const SkString* fPath; // If non-null, path to write the result to as a PNG.
656 SkTDArray<SkRect>& fRects; // All tiles of the picture.
657 const int fStart; // Range of tiles drawn by this thread.
658 const int fEnd;
659 bool* fSuccess; // Only meaningful if path is non-null. Shared by all threads,
660 // and only set to false upon failure to write to a PNG.
661 SkRunnable* fDone;
edisonn@google.com84f548c2012-12-18 22:24:03 +0000662 SkBitmap* fBitmap;
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000663};
664
665MultiCorePictureRenderer::MultiCorePictureRenderer(int threadCount)
666: fNumThreads(threadCount)
667, fThreadPool(threadCount)
668, fCountdown(threadCount) {
669 // Only need to create fNumThreads - 1 clones, since one thread will use the base
670 // picture.
671 fPictureClones = SkNEW_ARRAY(SkPicture, fNumThreads - 1);
672 fCloneData = SkNEW_ARRAY(CloneData*, fNumThreads);
673}
674
675void MultiCorePictureRenderer::init(SkPicture *pict) {
676 // Set fPicture and the tiles.
677 this->INHERITED::init(pict);
678 for (int i = 0; i < fNumThreads; ++i) {
679 *fCanvasPool.append() = this->setupCanvas(this->getTileWidth(), this->getTileHeight());
680 }
681 // Only need to create fNumThreads - 1 clones, since one thread will use the base picture.
682 fPicture->clone(fPictureClones, fNumThreads - 1);
683 // Populate each thread with the appropriate data.
684 // Group the tiles into nearly equal size chunks, rounding up so we're sure to cover them all.
685 const int chunkSize = (fTileRects.count() + fNumThreads - 1) / fNumThreads;
686
687 for (int i = 0; i < fNumThreads; i++) {
688 SkPicture* pic;
689 if (i == fNumThreads-1) {
690 // The last set will use the original SkPicture.
691 pic = fPicture;
692 } else {
693 pic = &fPictureClones[i];
694 }
695 const int start = i * chunkSize;
696 const int end = SkMin32(start + chunkSize, fTileRects.count());
697 fCloneData[i] = SkNEW_ARGS(CloneData,
698 (pic, fCanvasPool[i], fTileRects, start, end, &fCountdown));
699 }
700}
701
edisonn@google.com84f548c2012-12-18 22:24:03 +0000702bool MultiCorePictureRenderer::render(const SkString *path, SkBitmap** out) {
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000703 bool success = true;
704 if (path != NULL) {
705 for (int i = 0; i < fNumThreads-1; i++) {
706 fCloneData[i]->setPathAndSuccess(path, &success);
707 }
708 }
709
edisonn@google.com84f548c2012-12-18 22:24:03 +0000710 if (NULL != out) {
711 *out = SkNEW(SkBitmap);
712 setup_bitmap(*out, fPicture->width(), fPicture->height());
713 for (int i = 0; i < fNumThreads; i++) {
714 fCloneData[i]->setBitmap(*out);
715 }
716 } else {
717 for (int i = 0; i < fNumThreads; i++) {
718 fCloneData[i]->setBitmap(NULL);
719 }
720 }
721
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000722 fCountdown.reset(fNumThreads);
723 for (int i = 0; i < fNumThreads; i++) {
724 fThreadPool.add(fCloneData[i]);
725 }
726 fCountdown.wait();
727
728 return success;
729}
730
731void MultiCorePictureRenderer::end() {
732 for (int i = 0; i < fNumThreads - 1; i++) {
733 SkDELETE(fCloneData[i]);
734 fCloneData[i] = NULL;
735 }
736
737 fCanvasPool.unrefAll();
738
739 this->INHERITED::end();
740}
741
742MultiCorePictureRenderer::~MultiCorePictureRenderer() {
743 // Each individual CloneData was deleted in end.
744 SkDELETE_ARRAY(fCloneData);
745 SkDELETE_ARRAY(fPictureClones);
746}
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000747
scroggo@google.com0a049b82012-11-02 22:01:26 +0000748SkString MultiCorePictureRenderer::getConfigNameInternal() {
749 SkString name = this->INHERITED::getConfigNameInternal();
750 name.appendf("_multi_%i_threads", fNumThreads);
751 return name;
752}
753
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000754///////////////////////////////////////////////////////////////////////////////////////////////
scroggo@google.com9a412522012-09-07 15:21:18 +0000755
756void PlaybackCreationRenderer::setup() {
junov@chromium.org9313ca42012-11-02 18:11:49 +0000757 fReplayer.reset(this->createPicture());
scroggo@google.com82ec0b02012-12-17 19:25:54 +0000758 SkCanvas* recorder = fReplayer->beginRecording(this->getViewWidth(), this->getViewHeight(),
junov@chromium.org9313ca42012-11-02 18:11:49 +0000759 this->recordFlags());
scroggo@google.com82ec0b02012-12-17 19:25:54 +0000760 this->scaleToScaleFactor(recorder);
scroggo@google.com9a412522012-09-07 15:21:18 +0000761 fPicture->draw(recorder);
762}
763
edisonn@google.com84f548c2012-12-18 22:24:03 +0000764bool PlaybackCreationRenderer::render(const SkString*, SkBitmap** out) {
junov@chromium.org9313ca42012-11-02 18:11:49 +0000765 fReplayer->endRecording();
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000766 // Since this class does not actually render, return false.
767 return false;
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000768}
769
scroggo@google.com0a049b82012-11-02 22:01:26 +0000770SkString PlaybackCreationRenderer::getConfigNameInternal() {
771 return SkString("playback_creation");
772}
773
junov@chromium.org9313ca42012-11-02 18:11:49 +0000774///////////////////////////////////////////////////////////////////////////////////////////////
775// SkPicture variants for each BBoxHierarchy type
776
777class RTreePicture : public SkPicture {
778public:
779 virtual SkBBoxHierarchy* createBBoxHierarchy() const SK_OVERRIDE{
780 static const int kRTreeMinChildren = 6;
781 static const int kRTreeMaxChildren = 11;
782 SkScalar aspectRatio = SkScalarDiv(SkIntToScalar(fWidth),
783 SkIntToScalar(fHeight));
784 return SkRTree::Create(kRTreeMinChildren, kRTreeMaxChildren,
785 aspectRatio);
786 }
787};
788
789SkPicture* PictureRenderer::createPicture() {
790 switch (fBBoxHierarchyType) {
791 case kNone_BBoxHierarchyType:
792 return SkNEW(SkPicture);
793 case kRTree_BBoxHierarchyType:
794 return SkNEW(RTreePicture);
junov@chromium.org7b537062012-11-06 18:58:43 +0000795 case kTileGrid_BBoxHierarchyType:
junov@chromium.org29b19e52013-02-27 18:35:16 +0000796 return SkNEW_ARGS(SkTileGridPicture, (fPicture->width(),
797 fPicture->height(), fGridInfo));
junov@chromium.org9313ca42012-11-02 18:11:49 +0000798 }
799 SkASSERT(0); // invalid bbhType
800 return NULL;
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000801}
junov@chromium.org9313ca42012-11-02 18:11:49 +0000802
reed@google.comfe7b1ed2012-11-29 21:00:39 +0000803///////////////////////////////////////////////////////////////////////////////
804
805class GatherRenderer : public PictureRenderer {
806public:
edisonn@google.com84f548c2012-12-18 22:24:03 +0000807 virtual bool render(const SkString* path, SkBitmap** out = NULL)
808 SK_OVERRIDE {
reed@google.comfe7b1ed2012-11-29 21:00:39 +0000809 SkRect bounds = SkRect::MakeWH(SkIntToScalar(fPicture->width()),
810 SkIntToScalar(fPicture->height()));
811 SkData* data = SkPictureUtils::GatherPixelRefs(fPicture, bounds);
812 SkSafeUnref(data);
skia.committer@gmail.comc7b4be72012-12-11 02:01:20 +0000813
reed@google.comfe7b1ed2012-11-29 21:00:39 +0000814 return NULL == path; // we don't have anything to write
815 }
skia.committer@gmail.comc7b4be72012-12-11 02:01:20 +0000816
reed@google.comfe7b1ed2012-11-29 21:00:39 +0000817private:
818 virtual SkString getConfigNameInternal() SK_OVERRIDE {
819 return SkString("gather_pixelrefs");
820 }
821};
822
823PictureRenderer* CreateGatherPixelRefsRenderer() {
824 return SkNEW(GatherRenderer);
825}
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +0000826
reed@google.com5a34fd32012-12-10 16:05:09 +0000827///////////////////////////////////////////////////////////////////////////////
828
829class PictureCloneRenderer : public PictureRenderer {
830public:
edisonn@google.com84f548c2012-12-18 22:24:03 +0000831 virtual bool render(const SkString* path, SkBitmap** out = NULL)
832 SK_OVERRIDE {
reed@google.com5a34fd32012-12-10 16:05:09 +0000833 for (int i = 0; i < 100; ++i) {
834 SkPicture* clone = fPicture->clone();
835 SkSafeUnref(clone);
836 }
skia.committer@gmail.comc7b4be72012-12-11 02:01:20 +0000837
reed@google.com5a34fd32012-12-10 16:05:09 +0000838 return NULL == path; // we don't have anything to write
839 }
skia.committer@gmail.comc7b4be72012-12-11 02:01:20 +0000840
reed@google.com5a34fd32012-12-10 16:05:09 +0000841private:
842 virtual SkString getConfigNameInternal() SK_OVERRIDE {
843 return SkString("picture_clone");
844 }
845};
846
847PictureRenderer* CreatePictureCloneRenderer() {
848 return SkNEW(PictureCloneRenderer);
849}
850
junov@chromium.org9313ca42012-11-02 18:11:49 +0000851} // namespace sk_tools