blob: 16768c79e25e0c4756220e13033e168f5dce194a [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: {
scroggo@google.com0556ea02013-02-08 19:38:21 +0000115 SkAutoTUnref<GrRenderTarget> rt;
116 bool grSuccess = false;
117 if (fGrContext) {
118 // create a render target to back the device
119 GrTextureDesc desc;
120 desc.fConfig = kSkia8888_GrPixelConfig;
121 desc.fFlags = kRenderTarget_GrTextureFlagBit;
122 desc.fWidth = width;
123 desc.fHeight = height;
124 desc.fSampleCnt = 0;
125 GrTexture* tex = fGrContext->createUncachedTexture(desc, NULL, 0);
126 if (tex) {
127 rt.reset(tex->asRenderTarget());
128 rt.get()->ref();
129 tex->unref();
130 grSuccess = NULL != rt.get();
131 }
132 }
133 if (!grSuccess) {
134 SkASSERT(0);
135 return NULL;
136 }
137
138 SkAutoTUnref<SkGpuDevice> device(SkNEW_ARGS(SkGpuDevice, (fGrContext, rt)));
caryclark@google.coma3622372012-11-06 21:26:13 +0000139 canvas = SkNEW_ARGS(SkCanvas, (device.get()));
scroggo@google.com0556ea02013-02-08 19:38:21 +0000140 break;
keyar@chromium.org4ea96c52012-08-20 15:03:29 +0000141 }
142#endif
143 default:
144 SkASSERT(0);
scroggo@google.com82ec0b02012-12-17 19:25:54 +0000145 return NULL;
keyar@chromium.org4ea96c52012-08-20 15:03:29 +0000146 }
scroggo@google.com82ec0b02012-12-17 19:25:54 +0000147 setUpFilter(canvas, fDrawFilters);
148 this->scaleToScaleFactor(canvas);
149 return canvas;
150}
keyar@chromium.orga474ce32012-08-20 15:03:57 +0000151
scroggo@google.com82ec0b02012-12-17 19:25:54 +0000152void PictureRenderer::scaleToScaleFactor(SkCanvas* canvas) {
153 SkASSERT(canvas != NULL);
154 if (fScaleFactor != SK_Scalar1) {
155 canvas->scale(fScaleFactor, fScaleFactor);
156 }
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000157}
158
159void PictureRenderer::end() {
scroggo@google.com08085f82013-01-28 20:40:24 +0000160 this->resetState(true);
junov@chromium.org9313ca42012-11-02 18:11:49 +0000161 SkSafeUnref(fPicture);
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000162 fPicture = NULL;
163 fCanvas.reset(NULL);
164}
165
scroggo@google.comc0d5e542012-12-13 21:40:48 +0000166int PictureRenderer::getViewWidth() {
167 SkASSERT(fPicture != NULL);
robertphillips@google.com8ac811e2013-02-07 00:13:34 +0000168 int width = SkScalarCeilToInt(fPicture->width() * fScaleFactor);
scroggo@google.comc0d5e542012-12-13 21:40:48 +0000169 if (fViewport.width() > 0) {
170 width = SkMin32(width, fViewport.width());
171 }
172 return width;
173}
174
175int PictureRenderer::getViewHeight() {
176 SkASSERT(fPicture != NULL);
robertphillips@google.com8ac811e2013-02-07 00:13:34 +0000177 int height = SkScalarCeilToInt(fPicture->height() * fScaleFactor);
scroggo@google.comc0d5e542012-12-13 21:40:48 +0000178 if (fViewport.height() > 0) {
179 height = SkMin32(height, fViewport.height());
180 }
181 return height;
182}
183
junov@chromium.org9313ca42012-11-02 18:11:49 +0000184/** Converts fPicture to a picture that uses a BBoxHierarchy.
185 * PictureRenderer subclasses that are used to test picture playback
186 * should call this method during init.
187 */
188void PictureRenderer::buildBBoxHierarchy() {
189 SkASSERT(NULL != fPicture);
190 if (kNone_BBoxHierarchyType != fBBoxHierarchyType && NULL != fPicture) {
191 SkPicture* newPicture = this->createPicture();
192 SkCanvas* recorder = newPicture->beginRecording(fPicture->width(), fPicture->height(),
193 this->recordFlags());
194 fPicture->draw(recorder);
195 newPicture->endRecording();
196 fPicture->unref();
197 fPicture = newPicture;
198 }
199}
200
scroggo@google.com08085f82013-01-28 20:40:24 +0000201void PictureRenderer::resetState(bool callFinish) {
keyar@chromium.org28136b32012-08-20 15:04:15 +0000202#if SK_SUPPORT_GPU
scroggo@google.com0556ea02013-02-08 19:38:21 +0000203 SkGLContext* glContext = this->getGLContext();
204 if (NULL == glContext) {
205 SkASSERT(kBitmap_DeviceType == fDeviceType);
206 return;
207 }
keyar@chromium.org28136b32012-08-20 15:04:15 +0000208
scroggo@google.com0556ea02013-02-08 19:38:21 +0000209 fGrContext->flush();
210 if (callFinish) {
211 SK_GL(*glContext, Finish());
keyar@chromium.org77a55222012-08-20 15:03:47 +0000212 }
keyar@chromium.orga40c20d2012-08-20 15:04:12 +0000213#endif
keyar@chromium.org77a55222012-08-20 15:03:47 +0000214}
215
junov@chromium.org9313ca42012-11-02 18:11:49 +0000216uint32_t PictureRenderer::recordFlags() {
junov@chromium.org100b1c52013-01-16 20:12:22 +0000217 return ((kNone_BBoxHierarchyType == fBBoxHierarchyType) ? 0 :
218 SkPicture::kOptimizeForClippedPlayback_RecordingFlag) |
219 SkPicture::kUsePathBoundsForClip_RecordingFlag;
junov@chromium.org9313ca42012-11-02 18:11:49 +0000220}
221
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000222/**
223 * Write the canvas to the specified path.
224 * @param canvas Must be non-null. Canvas to be written to a file.
225 * @param path Path for the file to be written. Should have no extension; write() will append
226 * an appropriate one. Passed in by value so it can be modified.
227 * @return bool True if the Canvas is written to a file.
228 */
229static bool write(SkCanvas* canvas, SkString path) {
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000230 SkASSERT(canvas != NULL);
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000231 if (NULL == canvas) {
keyar@chromium.org9299ede2012-08-21 19:05:08 +0000232 return false;
233 }
234
235 SkBitmap bitmap;
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000236 SkISize size = canvas->getDeviceSize();
237 sk_tools::setup_bitmap(&bitmap, size.width(), size.height());
keyar@chromium.org9299ede2012-08-21 19:05:08 +0000238
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000239 canvas->readPixels(&bitmap, 0, 0);
keyar@chromium.org9299ede2012-08-21 19:05:08 +0000240 sk_tools::force_all_opaque(bitmap);
241
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000242 // Since path is passed in by value, it is okay to modify it.
243 path.append(".png");
keyar@chromium.org9299ede2012-08-21 19:05:08 +0000244 return SkImageEncoder::EncodeFile(path.c_str(), bitmap, SkImageEncoder::kPNG_Type, 100);
245}
246
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000247/**
248 * If path is non NULL, append number to it, and call write(SkCanvas*, SkString) to write the
249 * provided canvas to a file. Returns true if path is NULL or if write() succeeds.
250 */
251static bool writeAppendNumber(SkCanvas* canvas, const SkString* path, int number) {
252 if (NULL == path) {
253 return true;
254 }
255 SkString pathWithNumber(*path);
256 pathWithNumber.appendf("%i", number);
257 return write(canvas, pathWithNumber);
258}
259
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000260///////////////////////////////////////////////////////////////////////////////////////////////
261
djsollen@google.comfd9720c2012-11-06 16:54:40 +0000262SkCanvas* RecordPictureRenderer::setupCanvas(int width, int height) {
263 // defer the canvas setup until the render step
264 return NULL;
265}
266
scroggo@google.coma9e3a362012-11-07 17:52:48 +0000267static bool PNGEncodeBitmapToStream(SkWStream* wStream, const SkBitmap& bm) {
268 return SkImageEncoder::EncodeStream(wStream, bm, SkImageEncoder::kPNG_Type, 100);
269}
270
edisonn@google.com84f548c2012-12-18 22:24:03 +0000271bool RecordPictureRenderer::render(const SkString* path, SkBitmap** out) {
junov@chromium.org9313ca42012-11-02 18:11:49 +0000272 SkAutoTUnref<SkPicture> replayer(this->createPicture());
scroggo@google.com82ec0b02012-12-17 19:25:54 +0000273 SkCanvas* recorder = replayer->beginRecording(this->getViewWidth(), this->getViewHeight(),
junov@chromium.org9313ca42012-11-02 18:11:49 +0000274 this->recordFlags());
scroggo@google.com82ec0b02012-12-17 19:25:54 +0000275 this->scaleToScaleFactor(recorder);
scroggo@google.com9a412522012-09-07 15:21:18 +0000276 fPicture->draw(recorder);
junov@chromium.org9313ca42012-11-02 18:11:49 +0000277 replayer->endRecording();
scroggo@google.coma9e3a362012-11-07 17:52:48 +0000278 if (path != NULL) {
279 // Record the new picture as a new SKP with PNG encoded bitmaps.
280 SkString skpPath(*path);
281 // ".skp" was removed from 'path' before being passed in here.
282 skpPath.append(".skp");
283 SkFILEWStream stream(skpPath.c_str());
284 replayer->serialize(&stream, &PNGEncodeBitmapToStream);
285 return true;
286 }
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000287 return false;
scroggo@google.com9a412522012-09-07 15:21:18 +0000288}
289
scroggo@google.com0a049b82012-11-02 22:01:26 +0000290SkString RecordPictureRenderer::getConfigNameInternal() {
291 return SkString("record");
292}
293
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000294///////////////////////////////////////////////////////////////////////////////////////////////
295
edisonn@google.com84f548c2012-12-18 22:24:03 +0000296bool PipePictureRenderer::render(const SkString* path, SkBitmap** out) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000297 SkASSERT(fCanvas.get() != NULL);
298 SkASSERT(fPicture != NULL);
keyar@chromium.org78a35c52012-08-20 15:03:44 +0000299 if (NULL == fCanvas.get() || NULL == fPicture) {
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000300 return false;
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000301 }
302
303 PipeController pipeController(fCanvas.get());
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000304 SkGPipeWriter writer;
305 SkCanvas* pipeCanvas = writer.startRecording(&pipeController);
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000306 pipeCanvas->drawPicture(*fPicture);
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000307 writer.endRecording();
scroggo@google.com9a412522012-09-07 15:21:18 +0000308 fCanvas->flush();
borenet@google.com070d3542012-10-26 13:26:55 +0000309 if (NULL != path) {
310 return write(fCanvas, *path);
311 }
edisonn@google.com84f548c2012-12-18 22:24:03 +0000312 if (NULL != out) {
313 *out = SkNEW(SkBitmap);
314 setup_bitmap(*out, fPicture->width(), fPicture->height());
315 fCanvas->readPixels(*out, 0, 0);
skia.committer@gmail.coma7d8e3e2012-12-19 02:01:38 +0000316 }
borenet@google.com070d3542012-10-26 13:26:55 +0000317 return true;
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000318}
319
scroggo@google.com0a049b82012-11-02 22:01:26 +0000320SkString PipePictureRenderer::getConfigNameInternal() {
321 return SkString("pipe");
322}
323
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000324///////////////////////////////////////////////////////////////////////////////////////////////
325
junov@chromium.org9313ca42012-11-02 18:11:49 +0000326void SimplePictureRenderer::init(SkPicture* picture) {
327 INHERITED::init(picture);
328 this->buildBBoxHierarchy();
329}
330
edisonn@google.com84f548c2012-12-18 22:24:03 +0000331bool SimplePictureRenderer::render(const SkString* path, SkBitmap** out) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000332 SkASSERT(fCanvas.get() != NULL);
333 SkASSERT(fPicture != NULL);
keyar@chromium.org78a35c52012-08-20 15:03:44 +0000334 if (NULL == fCanvas.get() || NULL == fPicture) {
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000335 return false;
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000336 }
337
338 fCanvas->drawPicture(*fPicture);
scroggo@google.com9a412522012-09-07 15:21:18 +0000339 fCanvas->flush();
borenet@google.com070d3542012-10-26 13:26:55 +0000340 if (NULL != path) {
341 return write(fCanvas, *path);
342 }
skia.committer@gmail.coma7d8e3e2012-12-19 02:01:38 +0000343
edisonn@google.com84f548c2012-12-18 22:24:03 +0000344 if (NULL != out) {
345 *out = SkNEW(SkBitmap);
346 setup_bitmap(*out, fPicture->width(), fPicture->height());
347 fCanvas->readPixels(*out, 0, 0);
348 }
349
borenet@google.com070d3542012-10-26 13:26:55 +0000350 return true;
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000351}
352
scroggo@google.com0a049b82012-11-02 22:01:26 +0000353SkString SimplePictureRenderer::getConfigNameInternal() {
354 return SkString("simple");
355}
356
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000357///////////////////////////////////////////////////////////////////////////////////////////////
358
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000359TiledPictureRenderer::TiledPictureRenderer()
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000360 : fTileWidth(kDefaultTileWidth)
rileya@google.comb947b912012-08-29 17:35:07 +0000361 , fTileHeight(kDefaultTileHeight)
rileya@google.coma04dc022012-09-10 19:01:38 +0000362 , fTileWidthPercentage(0.0)
rileya@google.comb947b912012-08-29 17:35:07 +0000363 , fTileHeightPercentage(0.0)
scroggo@google.comcbcef702012-12-13 22:09:28 +0000364 , fTileMinPowerOf2Width(0)
365 , fCurrentTileOffset(-1)
366 , fTilesX(0)
367 , fTilesY(0) { }
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000368
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000369void TiledPictureRenderer::init(SkPicture* pict) {
370 SkASSERT(pict != NULL);
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000371 SkASSERT(0 == fTileRects.count());
372 if (NULL == pict || fTileRects.count() != 0) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000373 return;
374 }
375
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000376 // Do not call INHERITED::init(), which would create a (potentially large) canvas which is not
377 // used by bench_pictures.
378 fPicture = pict;
junov@chromium.org9313ca42012-11-02 18:11:49 +0000379 fPicture->ref();
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000380 this->buildBBoxHierarchy();
keyar@chromium.orgcc6e5ef2012-07-27 20:09:26 +0000381
382 if (fTileWidthPercentage > 0) {
robertphillips@google.com5d8d1862012-08-15 14:36:41 +0000383 fTileWidth = sk_float_ceil2int(float(fTileWidthPercentage * fPicture->width() / 100));
keyar@chromium.orgcc6e5ef2012-07-27 20:09:26 +0000384 }
385 if (fTileHeightPercentage > 0) {
robertphillips@google.com5d8d1862012-08-15 14:36:41 +0000386 fTileHeight = sk_float_ceil2int(float(fTileHeightPercentage * fPicture->height() / 100));
keyar@chromium.orgcc6e5ef2012-07-27 20:09:26 +0000387 }
388
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000389 if (fTileMinPowerOf2Width > 0) {
390 this->setupPowerOf2Tiles();
391 } else {
392 this->setupTiles();
393 }
scroggo@google.comcbcef702012-12-13 22:09:28 +0000394 fCanvas.reset(this->setupCanvas(fTileWidth, fTileHeight));
395 // Initialize to -1 so that the first call to nextTile will set this up to draw tile 0 on the
396 // first call to drawCurrentTile.
397 fCurrentTileOffset = -1;
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000398}
399
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000400void TiledPictureRenderer::end() {
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000401 fTileRects.reset();
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000402 this->INHERITED::end();
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000403}
404
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000405void TiledPictureRenderer::setupTiles() {
scroggo@google.comc0d5e542012-12-13 21:40:48 +0000406 // Only use enough tiles to cover the viewport
407 const int width = this->getViewWidth();
408 const int height = this->getViewHeight();
409
scroggo@google.comcbcef702012-12-13 22:09:28 +0000410 fTilesX = fTilesY = 0;
scroggo@google.comc0d5e542012-12-13 21:40:48 +0000411 for (int tile_y_start = 0; tile_y_start < height; tile_y_start += fTileHeight) {
scroggo@google.comcbcef702012-12-13 22:09:28 +0000412 fTilesY++;
scroggo@google.comc0d5e542012-12-13 21:40:48 +0000413 for (int tile_x_start = 0; tile_x_start < width; tile_x_start += fTileWidth) {
scroggo@google.comcbcef702012-12-13 22:09:28 +0000414 if (0 == tile_y_start) {
415 // Only count tiles in the X direction on the first pass.
416 fTilesX++;
417 }
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000418 *fTileRects.append() = SkRect::MakeXYWH(SkIntToScalar(tile_x_start),
419 SkIntToScalar(tile_y_start),
420 SkIntToScalar(fTileWidth),
421 SkIntToScalar(fTileHeight));
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000422 }
423 }
424}
425
scroggo@google.comcbcef702012-12-13 22:09:28 +0000426bool TiledPictureRenderer::tileDimensions(int &x, int &y) {
427 if (fTileRects.count() == 0 || NULL == fPicture) {
428 return false;
429 }
430 x = fTilesX;
431 y = fTilesY;
432 return true;
433}
434
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000435// The goal of the powers of two tiles is to minimize the amount of wasted tile
436// space in the width-wise direction and then minimize the number of tiles. The
437// constraints are that every tile must have a pixel width that is a power of
438// two and also be of some minimal width (that is also a power of two).
439//
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000440// This is solved by first taking our picture size and rounding it up to the
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000441// multiple of the minimal width. The binary representation of this rounded
442// value gives us the tiles we need: a bit of value one means we need a tile of
443// that size.
444void TiledPictureRenderer::setupPowerOf2Tiles() {
scroggo@google.comc0d5e542012-12-13 21:40:48 +0000445 // Only use enough tiles to cover the viewport
446 const int width = this->getViewWidth();
447 const int height = this->getViewHeight();
448
449 int rounded_value = width;
450 if (width % fTileMinPowerOf2Width != 0) {
451 rounded_value = width - (width % fTileMinPowerOf2Width) + fTileMinPowerOf2Width;
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000452 }
453
scroggo@google.comc0d5e542012-12-13 21:40:48 +0000454 int num_bits = SkScalarCeilToInt(SkScalarLog2(SkIntToScalar(width)));
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000455 int largest_possible_tile_size = 1 << num_bits;
456
scroggo@google.comcbcef702012-12-13 22:09:28 +0000457 fTilesX = fTilesY = 0;
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000458 // The tile height is constant for a particular picture.
scroggo@google.comc0d5e542012-12-13 21:40:48 +0000459 for (int tile_y_start = 0; tile_y_start < height; tile_y_start += fTileHeight) {
scroggo@google.comcbcef702012-12-13 22:09:28 +0000460 fTilesY++;
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000461 int tile_x_start = 0;
462 int current_width = largest_possible_tile_size;
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000463 // Set fTileWidth to be the width of the widest tile, so that each canvas is large enough
464 // to draw each tile.
465 fTileWidth = current_width;
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000466
467 while (current_width >= fTileMinPowerOf2Width) {
468 // It is very important this is a bitwise AND.
469 if (current_width & rounded_value) {
scroggo@google.comcbcef702012-12-13 22:09:28 +0000470 if (0 == tile_y_start) {
471 // Only count tiles in the X direction on the first pass.
472 fTilesX++;
473 }
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000474 *fTileRects.append() = SkRect::MakeXYWH(SkIntToScalar(tile_x_start),
475 SkIntToScalar(tile_y_start),
476 SkIntToScalar(current_width),
477 SkIntToScalar(fTileHeight));
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000478 tile_x_start += current_width;
479 }
480
481 current_width >>= 1;
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000482 }
483 }
484}
485
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000486/**
487 * Draw the specified playback to the canvas translated to rectangle provided, so that this mini
488 * canvas represents the rectangle's portion of the overall picture.
489 * Saves and restores so that the initial clip and matrix return to their state before this function
490 * is called.
491 */
492template<class T>
493static void DrawTileToCanvas(SkCanvas* canvas, const SkRect& tileRect, T* playback) {
494 int saveCount = canvas->save();
scroggo@google.com82ec0b02012-12-17 19:25:54 +0000495 // Translate so that we draw the correct portion of the picture.
496 // Perform a postTranslate so that the scaleFactor does not interfere with the positioning.
497 SkMatrix mat(canvas->getTotalMatrix());
498 mat.postTranslate(-tileRect.fLeft, -tileRect.fTop);
499 canvas->setMatrix(mat);
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000500 playback->draw(canvas);
501 canvas->restoreToCount(saveCount);
502 canvas->flush();
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000503}
504
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000505///////////////////////////////////////////////////////////////////////////////////////////////
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000506
edisonn@google.com84f548c2012-12-18 22:24:03 +0000507static void bitmapCopySubset(const SkBitmap& src, SkBitmap* dst, int xDst,
508 int yDst) {
509 for (int y = 0; y <src.height() && y + yDst < dst->height() ; y++) {
510 for (int x = 0; x < src.width() && x + xDst < dst->width() ; x++) {
511 *dst->getAddr32(xDst + x, yDst + y) = *src.getAddr32(x, y);
512 }
513 }
514}
515
scroggo@google.comcbcef702012-12-13 22:09:28 +0000516bool TiledPictureRenderer::nextTile(int &i, int &j) {
517 if (++fCurrentTileOffset < fTileRects.count()) {
518 i = fCurrentTileOffset % fTilesX;
519 j = fCurrentTileOffset / fTilesX;
520 return true;
521 }
522 return false;
523}
524
525void TiledPictureRenderer::drawCurrentTile() {
526 SkASSERT(fCurrentTileOffset >= 0 && fCurrentTileOffset < fTileRects.count());
527 DrawTileToCanvas(fCanvas, fTileRects[fCurrentTileOffset], fPicture);
528}
529
edisonn@google.com84f548c2012-12-18 22:24:03 +0000530bool TiledPictureRenderer::render(const SkString* path, SkBitmap** out) {
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000531 SkASSERT(fPicture != NULL);
532 if (NULL == fPicture) {
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000533 return false;
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000534 }
535
edisonn@google.com84f548c2012-12-18 22:24:03 +0000536 SkBitmap bitmap;
537 if (out){
538 *out = SkNEW(SkBitmap);
539 setup_bitmap(*out, fPicture->width(), fPicture->height());
540 setup_bitmap(&bitmap, fTileWidth, fTileHeight);
541 }
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000542 bool success = true;
543 for (int i = 0; i < fTileRects.count(); ++i) {
scroggo@google.comcbcef702012-12-13 22:09:28 +0000544 DrawTileToCanvas(fCanvas, fTileRects[i], fPicture);
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000545 if (NULL != path) {
scroggo@google.comcbcef702012-12-13 22:09:28 +0000546 success &= writeAppendNumber(fCanvas, path, i);
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000547 }
edisonn@google.com84f548c2012-12-18 22:24:03 +0000548 if (NULL != out) {
549 if (fCanvas->readPixels(&bitmap, 0, 0)) {
jvanverth@google.com9c4e5ac2013-01-07 18:41:28 +0000550 bitmapCopySubset(bitmap, *out, SkScalarFloorToInt(fTileRects[i].left()),
551 SkScalarFloorToInt(fTileRects[i].top()));
edisonn@google.com84f548c2012-12-18 22:24:03 +0000552 } else {
553 success = false;
554 }
555 }
keyar@chromium.org163b5672012-08-01 17:53:29 +0000556 }
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000557 return success;
keyar@chromium.org163b5672012-08-01 17:53:29 +0000558}
559
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000560SkCanvas* TiledPictureRenderer::setupCanvas(int width, int height) {
561 SkCanvas* canvas = this->INHERITED::setupCanvas(width, height);
562 SkASSERT(fPicture != NULL);
scroggo@google.com82ec0b02012-12-17 19:25:54 +0000563 // Clip the tile to an area that is completely inside both the SkPicture and the viewport. This
564 // is mostly important for tiles on the right and bottom edges as they may go over this area and
565 // the picture may have some commands that draw outside of this area and so should not actually
566 // be written.
567 // Uses a clipRegion so that it will be unaffected by the scale factor, which may have been set
568 // by INHERITED::setupCanvas.
569 SkRegion clipRegion;
570 clipRegion.setRect(0, 0, this->getViewWidth(), this->getViewHeight());
571 canvas->clipRegion(clipRegion);
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000572 return canvas;
573}
scroggo@google.com0a049b82012-11-02 22:01:26 +0000574
575SkString TiledPictureRenderer::getConfigNameInternal() {
576 SkString name;
577 if (fTileMinPowerOf2Width > 0) {
578 name.append("pow2tile_");
579 name.appendf("%i", fTileMinPowerOf2Width);
580 } else {
581 name.append("tile_");
582 if (fTileWidthPercentage > 0) {
583 name.appendf("%.f%%", fTileWidthPercentage);
584 } else {
585 name.appendf("%i", fTileWidth);
586 }
587 }
588 name.append("x");
589 if (fTileHeightPercentage > 0) {
590 name.appendf("%.f%%", fTileHeightPercentage);
591 } else {
592 name.appendf("%i", fTileHeight);
593 }
594 return name;
595}
596
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000597///////////////////////////////////////////////////////////////////////////////////////////////
598
599// Holds all of the information needed to draw a set of tiles.
600class CloneData : public SkRunnable {
601
602public:
603 CloneData(SkPicture* clone, SkCanvas* canvas, SkTDArray<SkRect>& rects, int start, int end,
604 SkRunnable* done)
605 : fClone(clone)
606 , fCanvas(canvas)
607 , fPath(NULL)
608 , fRects(rects)
609 , fStart(start)
610 , fEnd(end)
611 , fSuccess(NULL)
612 , fDone(done) {
613 SkASSERT(fDone != NULL);
614 }
615
616 virtual void run() SK_OVERRIDE {
617 SkGraphics::SetTLSFontCacheLimit(1024 * 1024);
edisonn@google.com84f548c2012-12-18 22:24:03 +0000618
619 SkBitmap bitmap;
620 if (fBitmap != NULL) {
621 // All tiles are the same size.
jvanverth@google.com9c4e5ac2013-01-07 18:41:28 +0000622 setup_bitmap(&bitmap, SkScalarFloorToInt(fRects[0].width()), SkScalarFloorToInt(fRects[0].height()));
edisonn@google.com84f548c2012-12-18 22:24:03 +0000623 }
skia.committer@gmail.coma7d8e3e2012-12-19 02:01:38 +0000624
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000625 for (int i = fStart; i < fEnd; i++) {
626 DrawTileToCanvas(fCanvas, fRects[i], fClone);
627 if (fPath != NULL && !writeAppendNumber(fCanvas, fPath, i)
628 && fSuccess != NULL) {
629 *fSuccess = false;
630 // If one tile fails to write to a file, do not continue drawing the rest.
631 break;
632 }
edisonn@google.com84f548c2012-12-18 22:24:03 +0000633 if (fBitmap != NULL) {
634 if (fCanvas->readPixels(&bitmap, 0, 0)) {
635 SkAutoLockPixels alp(*fBitmap);
jvanverth@google.com9c4e5ac2013-01-07 18:41:28 +0000636 bitmapCopySubset(bitmap, fBitmap, SkScalarFloorToInt(fRects[i].left()),
637 SkScalarFloorToInt(fRects[i].top()));
edisonn@google.com84f548c2012-12-18 22:24:03 +0000638 } else {
639 *fSuccess = false;
640 // If one tile fails to read pixels, do not continue drawing the rest.
641 break;
642 }
643 }
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000644 }
645 fDone->run();
646 }
647
648 void setPathAndSuccess(const SkString* path, bool* success) {
649 fPath = path;
650 fSuccess = success;
651 }
652
edisonn@google.com84f548c2012-12-18 22:24:03 +0000653 void setBitmap(SkBitmap* bitmap) {
654 fBitmap = bitmap;
655 }
656
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000657private:
658 // All pointers unowned.
659 SkPicture* fClone; // Picture to draw from. Each CloneData has a unique one which
660 // is threadsafe.
661 SkCanvas* fCanvas; // Canvas to draw to. Reused for each tile.
662 const SkString* fPath; // If non-null, path to write the result to as a PNG.
663 SkTDArray<SkRect>& fRects; // All tiles of the picture.
664 const int fStart; // Range of tiles drawn by this thread.
665 const int fEnd;
666 bool* fSuccess; // Only meaningful if path is non-null. Shared by all threads,
667 // and only set to false upon failure to write to a PNG.
668 SkRunnable* fDone;
edisonn@google.com84f548c2012-12-18 22:24:03 +0000669 SkBitmap* fBitmap;
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000670};
671
672MultiCorePictureRenderer::MultiCorePictureRenderer(int threadCount)
673: fNumThreads(threadCount)
674, fThreadPool(threadCount)
675, fCountdown(threadCount) {
676 // Only need to create fNumThreads - 1 clones, since one thread will use the base
677 // picture.
678 fPictureClones = SkNEW_ARRAY(SkPicture, fNumThreads - 1);
679 fCloneData = SkNEW_ARRAY(CloneData*, fNumThreads);
680}
681
682void MultiCorePictureRenderer::init(SkPicture *pict) {
683 // Set fPicture and the tiles.
684 this->INHERITED::init(pict);
685 for (int i = 0; i < fNumThreads; ++i) {
686 *fCanvasPool.append() = this->setupCanvas(this->getTileWidth(), this->getTileHeight());
687 }
688 // Only need to create fNumThreads - 1 clones, since one thread will use the base picture.
689 fPicture->clone(fPictureClones, fNumThreads - 1);
690 // Populate each thread with the appropriate data.
691 // Group the tiles into nearly equal size chunks, rounding up so we're sure to cover them all.
692 const int chunkSize = (fTileRects.count() + fNumThreads - 1) / fNumThreads;
693
694 for (int i = 0; i < fNumThreads; i++) {
695 SkPicture* pic;
696 if (i == fNumThreads-1) {
697 // The last set will use the original SkPicture.
698 pic = fPicture;
699 } else {
700 pic = &fPictureClones[i];
701 }
702 const int start = i * chunkSize;
703 const int end = SkMin32(start + chunkSize, fTileRects.count());
704 fCloneData[i] = SkNEW_ARGS(CloneData,
705 (pic, fCanvasPool[i], fTileRects, start, end, &fCountdown));
706 }
707}
708
edisonn@google.com84f548c2012-12-18 22:24:03 +0000709bool MultiCorePictureRenderer::render(const SkString *path, SkBitmap** out) {
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000710 bool success = true;
711 if (path != NULL) {
712 for (int i = 0; i < fNumThreads-1; i++) {
713 fCloneData[i]->setPathAndSuccess(path, &success);
714 }
715 }
716
edisonn@google.com84f548c2012-12-18 22:24:03 +0000717 if (NULL != out) {
718 *out = SkNEW(SkBitmap);
719 setup_bitmap(*out, fPicture->width(), fPicture->height());
720 for (int i = 0; i < fNumThreads; i++) {
721 fCloneData[i]->setBitmap(*out);
722 }
723 } else {
724 for (int i = 0; i < fNumThreads; i++) {
725 fCloneData[i]->setBitmap(NULL);
726 }
727 }
728
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000729 fCountdown.reset(fNumThreads);
730 for (int i = 0; i < fNumThreads; i++) {
731 fThreadPool.add(fCloneData[i]);
732 }
733 fCountdown.wait();
734
735 return success;
736}
737
738void MultiCorePictureRenderer::end() {
739 for (int i = 0; i < fNumThreads - 1; i++) {
740 SkDELETE(fCloneData[i]);
741 fCloneData[i] = NULL;
742 }
743
744 fCanvasPool.unrefAll();
745
746 this->INHERITED::end();
747}
748
749MultiCorePictureRenderer::~MultiCorePictureRenderer() {
750 // Each individual CloneData was deleted in end.
751 SkDELETE_ARRAY(fCloneData);
752 SkDELETE_ARRAY(fPictureClones);
753}
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000754
scroggo@google.com0a049b82012-11-02 22:01:26 +0000755SkString MultiCorePictureRenderer::getConfigNameInternal() {
756 SkString name = this->INHERITED::getConfigNameInternal();
757 name.appendf("_multi_%i_threads", fNumThreads);
758 return name;
759}
760
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000761///////////////////////////////////////////////////////////////////////////////////////////////
scroggo@google.com9a412522012-09-07 15:21:18 +0000762
763void PlaybackCreationRenderer::setup() {
junov@chromium.org9313ca42012-11-02 18:11:49 +0000764 fReplayer.reset(this->createPicture());
scroggo@google.com82ec0b02012-12-17 19:25:54 +0000765 SkCanvas* recorder = fReplayer->beginRecording(this->getViewWidth(), this->getViewHeight(),
junov@chromium.org9313ca42012-11-02 18:11:49 +0000766 this->recordFlags());
scroggo@google.com82ec0b02012-12-17 19:25:54 +0000767 this->scaleToScaleFactor(recorder);
scroggo@google.com9a412522012-09-07 15:21:18 +0000768 fPicture->draw(recorder);
769}
770
edisonn@google.com84f548c2012-12-18 22:24:03 +0000771bool PlaybackCreationRenderer::render(const SkString*, SkBitmap** out) {
junov@chromium.org9313ca42012-11-02 18:11:49 +0000772 fReplayer->endRecording();
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000773 // Since this class does not actually render, return false.
774 return false;
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000775}
776
scroggo@google.com0a049b82012-11-02 22:01:26 +0000777SkString PlaybackCreationRenderer::getConfigNameInternal() {
778 return SkString("playback_creation");
779}
780
junov@chromium.org9313ca42012-11-02 18:11:49 +0000781///////////////////////////////////////////////////////////////////////////////////////////////
782// SkPicture variants for each BBoxHierarchy type
783
784class RTreePicture : public SkPicture {
785public:
786 virtual SkBBoxHierarchy* createBBoxHierarchy() const SK_OVERRIDE{
787 static const int kRTreeMinChildren = 6;
788 static const int kRTreeMaxChildren = 11;
789 SkScalar aspectRatio = SkScalarDiv(SkIntToScalar(fWidth),
790 SkIntToScalar(fHeight));
791 return SkRTree::Create(kRTreeMinChildren, kRTreeMaxChildren,
792 aspectRatio);
793 }
794};
795
796SkPicture* PictureRenderer::createPicture() {
797 switch (fBBoxHierarchyType) {
798 case kNone_BBoxHierarchyType:
799 return SkNEW(SkPicture);
800 case kRTree_BBoxHierarchyType:
801 return SkNEW(RTreePicture);
junov@chromium.org7b537062012-11-06 18:58:43 +0000802 case kTileGrid_BBoxHierarchyType:
junov@chromium.org3cb834b2012-12-13 16:39:53 +0000803 return SkNEW_ARGS(SkTileGridPicture, (fGridWidth, fGridHeight, fPicture->width(),
804 fPicture->height()));
junov@chromium.org9313ca42012-11-02 18:11:49 +0000805 }
806 SkASSERT(0); // invalid bbhType
807 return NULL;
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000808}
junov@chromium.org9313ca42012-11-02 18:11:49 +0000809
reed@google.comfe7b1ed2012-11-29 21:00:39 +0000810///////////////////////////////////////////////////////////////////////////////
811
812class GatherRenderer : public PictureRenderer {
813public:
edisonn@google.com84f548c2012-12-18 22:24:03 +0000814 virtual bool render(const SkString* path, SkBitmap** out = NULL)
815 SK_OVERRIDE {
reed@google.comfe7b1ed2012-11-29 21:00:39 +0000816 SkRect bounds = SkRect::MakeWH(SkIntToScalar(fPicture->width()),
817 SkIntToScalar(fPicture->height()));
818 SkData* data = SkPictureUtils::GatherPixelRefs(fPicture, bounds);
819 SkSafeUnref(data);
skia.committer@gmail.comc7b4be72012-12-11 02:01:20 +0000820
reed@google.comfe7b1ed2012-11-29 21:00:39 +0000821 return NULL == path; // we don't have anything to write
822 }
skia.committer@gmail.comc7b4be72012-12-11 02:01:20 +0000823
reed@google.comfe7b1ed2012-11-29 21:00:39 +0000824private:
825 virtual SkString getConfigNameInternal() SK_OVERRIDE {
826 return SkString("gather_pixelrefs");
827 }
828};
829
830PictureRenderer* CreateGatherPixelRefsRenderer() {
831 return SkNEW(GatherRenderer);
832}
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +0000833
reed@google.com5a34fd32012-12-10 16:05:09 +0000834///////////////////////////////////////////////////////////////////////////////
835
836class PictureCloneRenderer : public PictureRenderer {
837public:
edisonn@google.com84f548c2012-12-18 22:24:03 +0000838 virtual bool render(const SkString* path, SkBitmap** out = NULL)
839 SK_OVERRIDE {
reed@google.com5a34fd32012-12-10 16:05:09 +0000840 for (int i = 0; i < 100; ++i) {
841 SkPicture* clone = fPicture->clone();
842 SkSafeUnref(clone);
843 }
skia.committer@gmail.comc7b4be72012-12-11 02:01:20 +0000844
reed@google.com5a34fd32012-12-10 16:05:09 +0000845 return NULL == path; // we don't have anything to write
846 }
skia.committer@gmail.comc7b4be72012-12-11 02:01:20 +0000847
reed@google.com5a34fd32012-12-10 16:05:09 +0000848private:
849 virtual SkString getConfigNameInternal() SK_OVERRIDE {
850 return SkString("picture_clone");
851 }
852};
853
854PictureRenderer* CreatePictureCloneRenderer() {
855 return SkNEW(PictureCloneRenderer);
856}
857
junov@chromium.org9313ca42012-11-02 18:11:49 +0000858} // namespace sk_tools