blob: ab5df5414ee9fe909e22fd632fc915068e6cc96a [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());
junov@chromium.org29b19e52013-02-27 18:35:16 +000045 fGridInfo.fMargin.setEmpty();
46 fGridInfo.fOffset.setZero();
47 fGridInfo.fTileInterval.set(1, 1);
keyar@chromium.org78a35c52012-08-20 15:03:44 +000048 if (fPicture != NULL || NULL != fCanvas.get()) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +000049 return;
50 }
51
52 SkASSERT(pict != NULL);
keyar@chromium.org78a35c52012-08-20 15:03:44 +000053 if (NULL == pict) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +000054 return;
55 }
56
57 fPicture = pict;
junov@chromium.org9313ca42012-11-02 18:11:49 +000058 fPicture->ref();
keyar@chromium.orga474ce32012-08-20 15:03:57 +000059 fCanvas.reset(this->setupCanvas());
60}
61
caryclark@google.coma3622372012-11-06 21:26:13 +000062class FlagsDrawFilter : public SkDrawFilter {
63public:
64 FlagsDrawFilter(PictureRenderer::DrawFilterFlags* flags) :
65 fFlags(flags) {}
66
reed@google.com971aca72012-11-26 20:26:54 +000067 virtual bool filter(SkPaint* paint, Type t) {
caryclark@google.coma3622372012-11-06 21:26:13 +000068 paint->setFlags(paint->getFlags() & ~fFlags[t] & SkPaint::kAllFlags);
reed@google.com457d8a72012-12-18 18:20:44 +000069 if (PictureRenderer::kBlur_DrawFilterFlag & fFlags[t]) {
caryclark@google.coma3622372012-11-06 21:26:13 +000070 SkMaskFilter* maskFilter = paint->getMaskFilter();
71 SkMaskFilter::BlurInfo blurInfo;
72 if (maskFilter && maskFilter->asABlur(&blurInfo)) {
reed@google.com457d8a72012-12-18 18:20:44 +000073 paint->setMaskFilter(NULL);
caryclark@google.coma3622372012-11-06 21:26:13 +000074 }
75 }
76 if (PictureRenderer::kHinting_DrawFilterFlag & fFlags[t]) {
77 paint->setHinting(SkPaint::kNo_Hinting);
78 } else if (PictureRenderer::kSlightHinting_DrawFilterFlag & fFlags[t]) {
79 paint->setHinting(SkPaint::kSlight_Hinting);
80 }
reed@google.com971aca72012-11-26 20:26:54 +000081 return true;
caryclark@google.coma3622372012-11-06 21:26:13 +000082 }
83
84private:
85 PictureRenderer::DrawFilterFlags* fFlags;
86};
87
scroggo@google.com82ec0b02012-12-17 19:25:54 +000088static void setUpFilter(SkCanvas* canvas, PictureRenderer::DrawFilterFlags* drawFilters) {
caryclark@google.coma3622372012-11-06 21:26:13 +000089 if (drawFilters && !canvas->getDrawFilter()) {
90 canvas->setDrawFilter(SkNEW_ARGS(FlagsDrawFilter, (drawFilters)))->unref();
caryclark@google.come3e940c2012-11-07 16:42:17 +000091 if (drawFilters[0] & PictureRenderer::kAAClip_DrawFilterFlag) {
92 canvas->setAllowSoftClip(false);
93 }
caryclark@google.coma3622372012-11-06 21:26:13 +000094 }
caryclark@google.coma3622372012-11-06 21:26:13 +000095}
96
keyar@chromium.orga474ce32012-08-20 15:03:57 +000097SkCanvas* PictureRenderer::setupCanvas() {
scroggo@google.comc0d5e542012-12-13 21:40:48 +000098 const int width = this->getViewWidth();
99 const int height = this->getViewHeight();
100 return this->setupCanvas(width, height);
keyar@chromium.orga474ce32012-08-20 15:03:57 +0000101}
102
103SkCanvas* PictureRenderer::setupCanvas(int width, int height) {
caryclark@google.coma3622372012-11-06 21:26:13 +0000104 SkCanvas* canvas;
keyar@chromium.org4ea96c52012-08-20 15:03:29 +0000105 switch(fDeviceType) {
106 case kBitmap_DeviceType: {
107 SkBitmap bitmap;
keyar@chromium.orga474ce32012-08-20 15:03:57 +0000108 sk_tools::setup_bitmap(&bitmap, width, height);
caryclark@google.coma3622372012-11-06 21:26:13 +0000109 canvas = SkNEW_ARGS(SkCanvas, (bitmap));
keyar@chromium.org4ea96c52012-08-20 15:03:29 +0000110 }
scroggo@google.com82ec0b02012-12-17 19:25:54 +0000111 break;
keyar@chromium.org4ea96c52012-08-20 15:03:29 +0000112#if SK_SUPPORT_GPU
scroggo@google.com0556ea02013-02-08 19:38:21 +0000113#if SK_ANGLE
114 case kAngle_DeviceType:
115 // fall through
116#endif
keyar@chromium.org4ea96c52012-08-20 15:03:29 +0000117 case kGPU_DeviceType: {
scroggo@google.com0556ea02013-02-08 19:38:21 +0000118 SkAutoTUnref<GrRenderTarget> rt;
119 bool grSuccess = false;
120 if (fGrContext) {
121 // create a render target to back the device
122 GrTextureDesc desc;
123 desc.fConfig = kSkia8888_GrPixelConfig;
124 desc.fFlags = kRenderTarget_GrTextureFlagBit;
125 desc.fWidth = width;
126 desc.fHeight = height;
127 desc.fSampleCnt = 0;
128 GrTexture* tex = fGrContext->createUncachedTexture(desc, NULL, 0);
129 if (tex) {
130 rt.reset(tex->asRenderTarget());
131 rt.get()->ref();
132 tex->unref();
133 grSuccess = NULL != rt.get();
134 }
135 }
136 if (!grSuccess) {
137 SkASSERT(0);
138 return NULL;
139 }
140
141 SkAutoTUnref<SkGpuDevice> device(SkNEW_ARGS(SkGpuDevice, (fGrContext, rt)));
caryclark@google.coma3622372012-11-06 21:26:13 +0000142 canvas = SkNEW_ARGS(SkCanvas, (device.get()));
scroggo@google.com0556ea02013-02-08 19:38:21 +0000143 break;
keyar@chromium.org4ea96c52012-08-20 15:03:29 +0000144 }
145#endif
146 default:
147 SkASSERT(0);
scroggo@google.com82ec0b02012-12-17 19:25:54 +0000148 return NULL;
keyar@chromium.org4ea96c52012-08-20 15:03:29 +0000149 }
scroggo@google.com82ec0b02012-12-17 19:25:54 +0000150 setUpFilter(canvas, fDrawFilters);
151 this->scaleToScaleFactor(canvas);
152 return canvas;
153}
keyar@chromium.orga474ce32012-08-20 15:03:57 +0000154
scroggo@google.com82ec0b02012-12-17 19:25:54 +0000155void PictureRenderer::scaleToScaleFactor(SkCanvas* canvas) {
156 SkASSERT(canvas != NULL);
157 if (fScaleFactor != SK_Scalar1) {
158 canvas->scale(fScaleFactor, fScaleFactor);
159 }
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000160}
161
162void PictureRenderer::end() {
scroggo@google.com08085f82013-01-28 20:40:24 +0000163 this->resetState(true);
junov@chromium.org9313ca42012-11-02 18:11:49 +0000164 SkSafeUnref(fPicture);
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000165 fPicture = NULL;
166 fCanvas.reset(NULL);
167}
168
scroggo@google.comc0d5e542012-12-13 21:40:48 +0000169int PictureRenderer::getViewWidth() {
170 SkASSERT(fPicture != NULL);
robertphillips@google.com8ac811e2013-02-07 00:13:34 +0000171 int width = SkScalarCeilToInt(fPicture->width() * fScaleFactor);
scroggo@google.comc0d5e542012-12-13 21:40:48 +0000172 if (fViewport.width() > 0) {
173 width = SkMin32(width, fViewport.width());
174 }
175 return width;
176}
177
178int PictureRenderer::getViewHeight() {
179 SkASSERT(fPicture != NULL);
robertphillips@google.com8ac811e2013-02-07 00:13:34 +0000180 int height = SkScalarCeilToInt(fPicture->height() * fScaleFactor);
scroggo@google.comc0d5e542012-12-13 21:40:48 +0000181 if (fViewport.height() > 0) {
182 height = SkMin32(height, fViewport.height());
183 }
184 return height;
185}
186
junov@chromium.org9313ca42012-11-02 18:11:49 +0000187/** Converts fPicture to a picture that uses a BBoxHierarchy.
188 * PictureRenderer subclasses that are used to test picture playback
189 * should call this method during init.
190 */
191void PictureRenderer::buildBBoxHierarchy() {
192 SkASSERT(NULL != fPicture);
193 if (kNone_BBoxHierarchyType != fBBoxHierarchyType && NULL != fPicture) {
194 SkPicture* newPicture = this->createPicture();
195 SkCanvas* recorder = newPicture->beginRecording(fPicture->width(), fPicture->height(),
196 this->recordFlags());
197 fPicture->draw(recorder);
198 newPicture->endRecording();
199 fPicture->unref();
200 fPicture = newPicture;
201 }
202}
203
scroggo@google.com08085f82013-01-28 20:40:24 +0000204void PictureRenderer::resetState(bool callFinish) {
keyar@chromium.org28136b32012-08-20 15:04:15 +0000205#if SK_SUPPORT_GPU
robertphillips@google.com6177e692013-02-28 20:16:25 +0000206 SkGLContextHelper* glContext = this->getGLContext();
scroggo@google.com0556ea02013-02-08 19:38:21 +0000207 if (NULL == glContext) {
208 SkASSERT(kBitmap_DeviceType == fDeviceType);
209 return;
210 }
keyar@chromium.org28136b32012-08-20 15:04:15 +0000211
scroggo@google.com0556ea02013-02-08 19:38:21 +0000212 fGrContext->flush();
213 if (callFinish) {
214 SK_GL(*glContext, Finish());
keyar@chromium.org77a55222012-08-20 15:03:47 +0000215 }
keyar@chromium.orga40c20d2012-08-20 15:04:12 +0000216#endif
keyar@chromium.org77a55222012-08-20 15:03:47 +0000217}
218
junov@chromium.org9313ca42012-11-02 18:11:49 +0000219uint32_t PictureRenderer::recordFlags() {
junov@chromium.org100b1c52013-01-16 20:12:22 +0000220 return ((kNone_BBoxHierarchyType == fBBoxHierarchyType) ? 0 :
221 SkPicture::kOptimizeForClippedPlayback_RecordingFlag) |
222 SkPicture::kUsePathBoundsForClip_RecordingFlag;
junov@chromium.org9313ca42012-11-02 18:11:49 +0000223}
224
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000225/**
226 * Write the canvas to the specified path.
227 * @param canvas Must be non-null. Canvas to be written to a file.
228 * @param path Path for the file to be written. Should have no extension; write() will append
229 * an appropriate one. Passed in by value so it can be modified.
230 * @return bool True if the Canvas is written to a file.
231 */
232static bool write(SkCanvas* canvas, SkString path) {
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000233 SkASSERT(canvas != NULL);
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000234 if (NULL == canvas) {
keyar@chromium.org9299ede2012-08-21 19:05:08 +0000235 return false;
236 }
237
238 SkBitmap bitmap;
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000239 SkISize size = canvas->getDeviceSize();
240 sk_tools::setup_bitmap(&bitmap, size.width(), size.height());
keyar@chromium.org9299ede2012-08-21 19:05:08 +0000241
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000242 canvas->readPixels(&bitmap, 0, 0);
keyar@chromium.org9299ede2012-08-21 19:05:08 +0000243 sk_tools::force_all_opaque(bitmap);
244
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000245 // Since path is passed in by value, it is okay to modify it.
246 path.append(".png");
keyar@chromium.org9299ede2012-08-21 19:05:08 +0000247 return SkImageEncoder::EncodeFile(path.c_str(), bitmap, SkImageEncoder::kPNG_Type, 100);
248}
249
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000250/**
251 * If path is non NULL, append number to it, and call write(SkCanvas*, SkString) to write the
252 * provided canvas to a file. Returns true if path is NULL or if write() succeeds.
253 */
254static bool writeAppendNumber(SkCanvas* canvas, const SkString* path, int number) {
255 if (NULL == path) {
256 return true;
257 }
258 SkString pathWithNumber(*path);
259 pathWithNumber.appendf("%i", number);
260 return write(canvas, pathWithNumber);
261}
262
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000263///////////////////////////////////////////////////////////////////////////////////////////////
264
djsollen@google.comfd9720c2012-11-06 16:54:40 +0000265SkCanvas* RecordPictureRenderer::setupCanvas(int width, int height) {
266 // defer the canvas setup until the render step
267 return NULL;
268}
269
scroggo@google.coma9e3a362012-11-07 17:52:48 +0000270static bool PNGEncodeBitmapToStream(SkWStream* wStream, const SkBitmap& bm) {
271 return SkImageEncoder::EncodeStream(wStream, bm, SkImageEncoder::kPNG_Type, 100);
272}
273
edisonn@google.com84f548c2012-12-18 22:24:03 +0000274bool RecordPictureRenderer::render(const SkString* path, SkBitmap** out) {
junov@chromium.org9313ca42012-11-02 18:11:49 +0000275 SkAutoTUnref<SkPicture> replayer(this->createPicture());
scroggo@google.com82ec0b02012-12-17 19:25:54 +0000276 SkCanvas* recorder = replayer->beginRecording(this->getViewWidth(), this->getViewHeight(),
junov@chromium.org9313ca42012-11-02 18:11:49 +0000277 this->recordFlags());
scroggo@google.com82ec0b02012-12-17 19:25:54 +0000278 this->scaleToScaleFactor(recorder);
scroggo@google.com9a412522012-09-07 15:21:18 +0000279 fPicture->draw(recorder);
junov@chromium.org9313ca42012-11-02 18:11:49 +0000280 replayer->endRecording();
scroggo@google.coma9e3a362012-11-07 17:52:48 +0000281 if (path != NULL) {
282 // Record the new picture as a new SKP with PNG encoded bitmaps.
283 SkString skpPath(*path);
284 // ".skp" was removed from 'path' before being passed in here.
285 skpPath.append(".skp");
286 SkFILEWStream stream(skpPath.c_str());
287 replayer->serialize(&stream, &PNGEncodeBitmapToStream);
288 return true;
289 }
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000290 return false;
scroggo@google.com9a412522012-09-07 15:21:18 +0000291}
292
scroggo@google.com0a049b82012-11-02 22:01:26 +0000293SkString RecordPictureRenderer::getConfigNameInternal() {
294 return SkString("record");
295}
296
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000297///////////////////////////////////////////////////////////////////////////////////////////////
298
edisonn@google.com84f548c2012-12-18 22:24:03 +0000299bool PipePictureRenderer::render(const SkString* path, SkBitmap** out) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000300 SkASSERT(fCanvas.get() != NULL);
301 SkASSERT(fPicture != NULL);
keyar@chromium.org78a35c52012-08-20 15:03:44 +0000302 if (NULL == fCanvas.get() || NULL == fPicture) {
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000303 return false;
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000304 }
305
306 PipeController pipeController(fCanvas.get());
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000307 SkGPipeWriter writer;
308 SkCanvas* pipeCanvas = writer.startRecording(&pipeController);
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000309 pipeCanvas->drawPicture(*fPicture);
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000310 writer.endRecording();
scroggo@google.com9a412522012-09-07 15:21:18 +0000311 fCanvas->flush();
borenet@google.com070d3542012-10-26 13:26:55 +0000312 if (NULL != path) {
313 return write(fCanvas, *path);
314 }
edisonn@google.com84f548c2012-12-18 22:24:03 +0000315 if (NULL != out) {
316 *out = SkNEW(SkBitmap);
317 setup_bitmap(*out, fPicture->width(), fPicture->height());
318 fCanvas->readPixels(*out, 0, 0);
skia.committer@gmail.coma7d8e3e2012-12-19 02:01:38 +0000319 }
borenet@google.com070d3542012-10-26 13:26:55 +0000320 return true;
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000321}
322
scroggo@google.com0a049b82012-11-02 22:01:26 +0000323SkString PipePictureRenderer::getConfigNameInternal() {
324 return SkString("pipe");
325}
326
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000327///////////////////////////////////////////////////////////////////////////////////////////////
328
junov@chromium.org9313ca42012-11-02 18:11:49 +0000329void SimplePictureRenderer::init(SkPicture* picture) {
330 INHERITED::init(picture);
331 this->buildBBoxHierarchy();
332}
333
edisonn@google.com84f548c2012-12-18 22:24:03 +0000334bool SimplePictureRenderer::render(const SkString* path, SkBitmap** out) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000335 SkASSERT(fCanvas.get() != NULL);
336 SkASSERT(fPicture != NULL);
keyar@chromium.org78a35c52012-08-20 15:03:44 +0000337 if (NULL == fCanvas.get() || NULL == fPicture) {
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000338 return false;
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000339 }
340
341 fCanvas->drawPicture(*fPicture);
scroggo@google.com9a412522012-09-07 15:21:18 +0000342 fCanvas->flush();
borenet@google.com070d3542012-10-26 13:26:55 +0000343 if (NULL != path) {
344 return write(fCanvas, *path);
345 }
skia.committer@gmail.coma7d8e3e2012-12-19 02:01:38 +0000346
edisonn@google.com84f548c2012-12-18 22:24:03 +0000347 if (NULL != out) {
348 *out = SkNEW(SkBitmap);
349 setup_bitmap(*out, fPicture->width(), fPicture->height());
350 fCanvas->readPixels(*out, 0, 0);
351 }
352
borenet@google.com070d3542012-10-26 13:26:55 +0000353 return true;
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000354}
355
scroggo@google.com0a049b82012-11-02 22:01:26 +0000356SkString SimplePictureRenderer::getConfigNameInternal() {
357 return SkString("simple");
358}
359
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000360///////////////////////////////////////////////////////////////////////////////////////////////
361
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000362TiledPictureRenderer::TiledPictureRenderer()
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000363 : fTileWidth(kDefaultTileWidth)
rileya@google.comb947b912012-08-29 17:35:07 +0000364 , fTileHeight(kDefaultTileHeight)
rileya@google.coma04dc022012-09-10 19:01:38 +0000365 , fTileWidthPercentage(0.0)
rileya@google.comb947b912012-08-29 17:35:07 +0000366 , fTileHeightPercentage(0.0)
scroggo@google.comcbcef702012-12-13 22:09:28 +0000367 , fTileMinPowerOf2Width(0)
368 , fCurrentTileOffset(-1)
369 , fTilesX(0)
370 , fTilesY(0) { }
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000371
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000372void TiledPictureRenderer::init(SkPicture* pict) {
373 SkASSERT(pict != NULL);
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000374 SkASSERT(0 == fTileRects.count());
375 if (NULL == pict || fTileRects.count() != 0) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000376 return;
377 }
378
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000379 // Do not call INHERITED::init(), which would create a (potentially large) canvas which is not
380 // used by bench_pictures.
381 fPicture = pict;
junov@chromium.org9313ca42012-11-02 18:11:49 +0000382 fPicture->ref();
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000383 this->buildBBoxHierarchy();
keyar@chromium.orgcc6e5ef2012-07-27 20:09:26 +0000384
385 if (fTileWidthPercentage > 0) {
robertphillips@google.com5d8d1862012-08-15 14:36:41 +0000386 fTileWidth = sk_float_ceil2int(float(fTileWidthPercentage * fPicture->width() / 100));
keyar@chromium.orgcc6e5ef2012-07-27 20:09:26 +0000387 }
388 if (fTileHeightPercentage > 0) {
robertphillips@google.com5d8d1862012-08-15 14:36:41 +0000389 fTileHeight = sk_float_ceil2int(float(fTileHeightPercentage * fPicture->height() / 100));
keyar@chromium.orgcc6e5ef2012-07-27 20:09:26 +0000390 }
391
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000392 if (fTileMinPowerOf2Width > 0) {
393 this->setupPowerOf2Tiles();
394 } else {
395 this->setupTiles();
396 }
scroggo@google.comcbcef702012-12-13 22:09:28 +0000397 fCanvas.reset(this->setupCanvas(fTileWidth, fTileHeight));
398 // Initialize to -1 so that the first call to nextTile will set this up to draw tile 0 on the
399 // first call to drawCurrentTile.
400 fCurrentTileOffset = -1;
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000401}
402
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000403void TiledPictureRenderer::end() {
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000404 fTileRects.reset();
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000405 this->INHERITED::end();
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000406}
407
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000408void TiledPictureRenderer::setupTiles() {
scroggo@google.comc0d5e542012-12-13 21:40:48 +0000409 // Only use enough tiles to cover the viewport
410 const int width = this->getViewWidth();
411 const int height = this->getViewHeight();
412
scroggo@google.comcbcef702012-12-13 22:09:28 +0000413 fTilesX = fTilesY = 0;
scroggo@google.comc0d5e542012-12-13 21:40:48 +0000414 for (int tile_y_start = 0; tile_y_start < height; tile_y_start += fTileHeight) {
scroggo@google.comcbcef702012-12-13 22:09:28 +0000415 fTilesY++;
scroggo@google.comc0d5e542012-12-13 21:40:48 +0000416 for (int tile_x_start = 0; tile_x_start < width; tile_x_start += fTileWidth) {
scroggo@google.comcbcef702012-12-13 22:09:28 +0000417 if (0 == tile_y_start) {
418 // Only count tiles in the X direction on the first pass.
419 fTilesX++;
420 }
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000421 *fTileRects.append() = SkRect::MakeXYWH(SkIntToScalar(tile_x_start),
422 SkIntToScalar(tile_y_start),
423 SkIntToScalar(fTileWidth),
424 SkIntToScalar(fTileHeight));
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000425 }
426 }
427}
428
scroggo@google.comcbcef702012-12-13 22:09:28 +0000429bool TiledPictureRenderer::tileDimensions(int &x, int &y) {
430 if (fTileRects.count() == 0 || NULL == fPicture) {
431 return false;
432 }
433 x = fTilesX;
434 y = fTilesY;
435 return true;
436}
437
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000438// The goal of the powers of two tiles is to minimize the amount of wasted tile
439// space in the width-wise direction and then minimize the number of tiles. The
440// constraints are that every tile must have a pixel width that is a power of
441// two and also be of some minimal width (that is also a power of two).
442//
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000443// This is solved by first taking our picture size and rounding it up to the
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000444// multiple of the minimal width. The binary representation of this rounded
445// value gives us the tiles we need: a bit of value one means we need a tile of
446// that size.
447void TiledPictureRenderer::setupPowerOf2Tiles() {
scroggo@google.comc0d5e542012-12-13 21:40:48 +0000448 // Only use enough tiles to cover the viewport
449 const int width = this->getViewWidth();
450 const int height = this->getViewHeight();
451
452 int rounded_value = width;
453 if (width % fTileMinPowerOf2Width != 0) {
454 rounded_value = width - (width % fTileMinPowerOf2Width) + fTileMinPowerOf2Width;
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000455 }
456
scroggo@google.comc0d5e542012-12-13 21:40:48 +0000457 int num_bits = SkScalarCeilToInt(SkScalarLog2(SkIntToScalar(width)));
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000458 int largest_possible_tile_size = 1 << num_bits;
459
scroggo@google.comcbcef702012-12-13 22:09:28 +0000460 fTilesX = fTilesY = 0;
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000461 // The tile height is constant for a particular picture.
scroggo@google.comc0d5e542012-12-13 21:40:48 +0000462 for (int tile_y_start = 0; tile_y_start < height; tile_y_start += fTileHeight) {
scroggo@google.comcbcef702012-12-13 22:09:28 +0000463 fTilesY++;
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000464 int tile_x_start = 0;
465 int current_width = largest_possible_tile_size;
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000466 // Set fTileWidth to be the width of the widest tile, so that each canvas is large enough
467 // to draw each tile.
468 fTileWidth = current_width;
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000469
470 while (current_width >= fTileMinPowerOf2Width) {
471 // It is very important this is a bitwise AND.
472 if (current_width & rounded_value) {
scroggo@google.comcbcef702012-12-13 22:09:28 +0000473 if (0 == tile_y_start) {
474 // Only count tiles in the X direction on the first pass.
475 fTilesX++;
476 }
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000477 *fTileRects.append() = SkRect::MakeXYWH(SkIntToScalar(tile_x_start),
478 SkIntToScalar(tile_y_start),
479 SkIntToScalar(current_width),
480 SkIntToScalar(fTileHeight));
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000481 tile_x_start += current_width;
482 }
483
484 current_width >>= 1;
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000485 }
486 }
487}
488
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000489/**
490 * Draw the specified playback to the canvas translated to rectangle provided, so that this mini
491 * canvas represents the rectangle's portion of the overall picture.
492 * Saves and restores so that the initial clip and matrix return to their state before this function
493 * is called.
494 */
495template<class T>
496static void DrawTileToCanvas(SkCanvas* canvas, const SkRect& tileRect, T* playback) {
497 int saveCount = canvas->save();
scroggo@google.com82ec0b02012-12-17 19:25:54 +0000498 // Translate so that we draw the correct portion of the picture.
499 // Perform a postTranslate so that the scaleFactor does not interfere with the positioning.
500 SkMatrix mat(canvas->getTotalMatrix());
501 mat.postTranslate(-tileRect.fLeft, -tileRect.fTop);
502 canvas->setMatrix(mat);
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000503 playback->draw(canvas);
504 canvas->restoreToCount(saveCount);
505 canvas->flush();
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000506}
507
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000508///////////////////////////////////////////////////////////////////////////////////////////////
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000509
edisonn@google.com84f548c2012-12-18 22:24:03 +0000510static void bitmapCopySubset(const SkBitmap& src, SkBitmap* dst, int xDst,
511 int yDst) {
512 for (int y = 0; y <src.height() && y + yDst < dst->height() ; y++) {
513 for (int x = 0; x < src.width() && x + xDst < dst->width() ; x++) {
514 *dst->getAddr32(xDst + x, yDst + y) = *src.getAddr32(x, y);
515 }
516 }
517}
518
scroggo@google.comcbcef702012-12-13 22:09:28 +0000519bool TiledPictureRenderer::nextTile(int &i, int &j) {
520 if (++fCurrentTileOffset < fTileRects.count()) {
521 i = fCurrentTileOffset % fTilesX;
522 j = fCurrentTileOffset / fTilesX;
523 return true;
524 }
525 return false;
526}
527
528void TiledPictureRenderer::drawCurrentTile() {
529 SkASSERT(fCurrentTileOffset >= 0 && fCurrentTileOffset < fTileRects.count());
530 DrawTileToCanvas(fCanvas, fTileRects[fCurrentTileOffset], fPicture);
531}
532
edisonn@google.com84f548c2012-12-18 22:24:03 +0000533bool TiledPictureRenderer::render(const SkString* path, SkBitmap** out) {
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000534 SkASSERT(fPicture != NULL);
535 if (NULL == fPicture) {
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000536 return false;
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000537 }
538
edisonn@google.com84f548c2012-12-18 22:24:03 +0000539 SkBitmap bitmap;
540 if (out){
541 *out = SkNEW(SkBitmap);
542 setup_bitmap(*out, fPicture->width(), fPicture->height());
543 setup_bitmap(&bitmap, fTileWidth, fTileHeight);
544 }
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000545 bool success = true;
546 for (int i = 0; i < fTileRects.count(); ++i) {
scroggo@google.comcbcef702012-12-13 22:09:28 +0000547 DrawTileToCanvas(fCanvas, fTileRects[i], fPicture);
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000548 if (NULL != path) {
scroggo@google.comcbcef702012-12-13 22:09:28 +0000549 success &= writeAppendNumber(fCanvas, path, i);
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000550 }
edisonn@google.com84f548c2012-12-18 22:24:03 +0000551 if (NULL != out) {
552 if (fCanvas->readPixels(&bitmap, 0, 0)) {
jvanverth@google.com9c4e5ac2013-01-07 18:41:28 +0000553 bitmapCopySubset(bitmap, *out, SkScalarFloorToInt(fTileRects[i].left()),
554 SkScalarFloorToInt(fTileRects[i].top()));
edisonn@google.com84f548c2012-12-18 22:24:03 +0000555 } else {
556 success = false;
557 }
558 }
keyar@chromium.org163b5672012-08-01 17:53:29 +0000559 }
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000560 return success;
keyar@chromium.org163b5672012-08-01 17:53:29 +0000561}
562
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000563SkCanvas* TiledPictureRenderer::setupCanvas(int width, int height) {
564 SkCanvas* canvas = this->INHERITED::setupCanvas(width, height);
565 SkASSERT(fPicture != NULL);
scroggo@google.com82ec0b02012-12-17 19:25:54 +0000566 // Clip the tile to an area that is completely inside both the SkPicture and the viewport. This
567 // is mostly important for tiles on the right and bottom edges as they may go over this area and
568 // the picture may have some commands that draw outside of this area and so should not actually
569 // be written.
570 // Uses a clipRegion so that it will be unaffected by the scale factor, which may have been set
571 // by INHERITED::setupCanvas.
572 SkRegion clipRegion;
573 clipRegion.setRect(0, 0, this->getViewWidth(), this->getViewHeight());
574 canvas->clipRegion(clipRegion);
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000575 return canvas;
576}
scroggo@google.com0a049b82012-11-02 22:01:26 +0000577
578SkString TiledPictureRenderer::getConfigNameInternal() {
579 SkString name;
580 if (fTileMinPowerOf2Width > 0) {
581 name.append("pow2tile_");
582 name.appendf("%i", fTileMinPowerOf2Width);
583 } else {
584 name.append("tile_");
585 if (fTileWidthPercentage > 0) {
586 name.appendf("%.f%%", fTileWidthPercentage);
587 } else {
588 name.appendf("%i", fTileWidth);
589 }
590 }
591 name.append("x");
592 if (fTileHeightPercentage > 0) {
593 name.appendf("%.f%%", fTileHeightPercentage);
594 } else {
595 name.appendf("%i", fTileHeight);
596 }
597 return name;
598}
599
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000600///////////////////////////////////////////////////////////////////////////////////////////////
601
602// Holds all of the information needed to draw a set of tiles.
603class CloneData : public SkRunnable {
604
605public:
606 CloneData(SkPicture* clone, SkCanvas* canvas, SkTDArray<SkRect>& rects, int start, int end,
607 SkRunnable* done)
608 : fClone(clone)
609 , fCanvas(canvas)
610 , fPath(NULL)
611 , fRects(rects)
612 , fStart(start)
613 , fEnd(end)
614 , fSuccess(NULL)
615 , fDone(done) {
616 SkASSERT(fDone != NULL);
617 }
618
619 virtual void run() SK_OVERRIDE {
620 SkGraphics::SetTLSFontCacheLimit(1024 * 1024);
edisonn@google.com84f548c2012-12-18 22:24:03 +0000621
622 SkBitmap bitmap;
623 if (fBitmap != NULL) {
624 // All tiles are the same size.
jvanverth@google.com9c4e5ac2013-01-07 18:41:28 +0000625 setup_bitmap(&bitmap, SkScalarFloorToInt(fRects[0].width()), SkScalarFloorToInt(fRects[0].height()));
edisonn@google.com84f548c2012-12-18 22:24:03 +0000626 }
skia.committer@gmail.coma7d8e3e2012-12-19 02:01:38 +0000627
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000628 for (int i = fStart; i < fEnd; i++) {
629 DrawTileToCanvas(fCanvas, fRects[i], fClone);
630 if (fPath != NULL && !writeAppendNumber(fCanvas, fPath, i)
631 && fSuccess != NULL) {
632 *fSuccess = false;
633 // If one tile fails to write to a file, do not continue drawing the rest.
634 break;
635 }
edisonn@google.com84f548c2012-12-18 22:24:03 +0000636 if (fBitmap != NULL) {
637 if (fCanvas->readPixels(&bitmap, 0, 0)) {
638 SkAutoLockPixels alp(*fBitmap);
jvanverth@google.com9c4e5ac2013-01-07 18:41:28 +0000639 bitmapCopySubset(bitmap, fBitmap, SkScalarFloorToInt(fRects[i].left()),
640 SkScalarFloorToInt(fRects[i].top()));
edisonn@google.com84f548c2012-12-18 22:24:03 +0000641 } else {
642 *fSuccess = false;
643 // If one tile fails to read pixels, do not continue drawing the rest.
644 break;
645 }
646 }
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000647 }
648 fDone->run();
649 }
650
651 void setPathAndSuccess(const SkString* path, bool* success) {
652 fPath = path;
653 fSuccess = success;
654 }
655
edisonn@google.com84f548c2012-12-18 22:24:03 +0000656 void setBitmap(SkBitmap* bitmap) {
657 fBitmap = bitmap;
658 }
659
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000660private:
661 // All pointers unowned.
662 SkPicture* fClone; // Picture to draw from. Each CloneData has a unique one which
663 // is threadsafe.
664 SkCanvas* fCanvas; // Canvas to draw to. Reused for each tile.
665 const SkString* fPath; // If non-null, path to write the result to as a PNG.
666 SkTDArray<SkRect>& fRects; // All tiles of the picture.
667 const int fStart; // Range of tiles drawn by this thread.
668 const int fEnd;
669 bool* fSuccess; // Only meaningful if path is non-null. Shared by all threads,
670 // and only set to false upon failure to write to a PNG.
671 SkRunnable* fDone;
edisonn@google.com84f548c2012-12-18 22:24:03 +0000672 SkBitmap* fBitmap;
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000673};
674
675MultiCorePictureRenderer::MultiCorePictureRenderer(int threadCount)
676: fNumThreads(threadCount)
677, fThreadPool(threadCount)
678, fCountdown(threadCount) {
679 // Only need to create fNumThreads - 1 clones, since one thread will use the base
680 // picture.
681 fPictureClones = SkNEW_ARRAY(SkPicture, fNumThreads - 1);
682 fCloneData = SkNEW_ARRAY(CloneData*, fNumThreads);
683}
684
685void MultiCorePictureRenderer::init(SkPicture *pict) {
686 // Set fPicture and the tiles.
687 this->INHERITED::init(pict);
688 for (int i = 0; i < fNumThreads; ++i) {
689 *fCanvasPool.append() = this->setupCanvas(this->getTileWidth(), this->getTileHeight());
690 }
691 // Only need to create fNumThreads - 1 clones, since one thread will use the base picture.
692 fPicture->clone(fPictureClones, fNumThreads - 1);
693 // Populate each thread with the appropriate data.
694 // Group the tiles into nearly equal size chunks, rounding up so we're sure to cover them all.
695 const int chunkSize = (fTileRects.count() + fNumThreads - 1) / fNumThreads;
696
697 for (int i = 0; i < fNumThreads; i++) {
698 SkPicture* pic;
699 if (i == fNumThreads-1) {
700 // The last set will use the original SkPicture.
701 pic = fPicture;
702 } else {
703 pic = &fPictureClones[i];
704 }
705 const int start = i * chunkSize;
706 const int end = SkMin32(start + chunkSize, fTileRects.count());
707 fCloneData[i] = SkNEW_ARGS(CloneData,
708 (pic, fCanvasPool[i], fTileRects, start, end, &fCountdown));
709 }
710}
711
edisonn@google.com84f548c2012-12-18 22:24:03 +0000712bool MultiCorePictureRenderer::render(const SkString *path, SkBitmap** out) {
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000713 bool success = true;
714 if (path != NULL) {
715 for (int i = 0; i < fNumThreads-1; i++) {
716 fCloneData[i]->setPathAndSuccess(path, &success);
717 }
718 }
719
edisonn@google.com84f548c2012-12-18 22:24:03 +0000720 if (NULL != out) {
721 *out = SkNEW(SkBitmap);
722 setup_bitmap(*out, fPicture->width(), fPicture->height());
723 for (int i = 0; i < fNumThreads; i++) {
724 fCloneData[i]->setBitmap(*out);
725 }
726 } else {
727 for (int i = 0; i < fNumThreads; i++) {
728 fCloneData[i]->setBitmap(NULL);
729 }
730 }
731
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000732 fCountdown.reset(fNumThreads);
733 for (int i = 0; i < fNumThreads; i++) {
734 fThreadPool.add(fCloneData[i]);
735 }
736 fCountdown.wait();
737
738 return success;
739}
740
741void MultiCorePictureRenderer::end() {
742 for (int i = 0; i < fNumThreads - 1; i++) {
743 SkDELETE(fCloneData[i]);
744 fCloneData[i] = NULL;
745 }
746
747 fCanvasPool.unrefAll();
748
749 this->INHERITED::end();
750}
751
752MultiCorePictureRenderer::~MultiCorePictureRenderer() {
753 // Each individual CloneData was deleted in end.
754 SkDELETE_ARRAY(fCloneData);
755 SkDELETE_ARRAY(fPictureClones);
756}
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000757
scroggo@google.com0a049b82012-11-02 22:01:26 +0000758SkString MultiCorePictureRenderer::getConfigNameInternal() {
759 SkString name = this->INHERITED::getConfigNameInternal();
760 name.appendf("_multi_%i_threads", fNumThreads);
761 return name;
762}
763
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000764///////////////////////////////////////////////////////////////////////////////////////////////
scroggo@google.com9a412522012-09-07 15:21:18 +0000765
766void PlaybackCreationRenderer::setup() {
junov@chromium.org9313ca42012-11-02 18:11:49 +0000767 fReplayer.reset(this->createPicture());
scroggo@google.com82ec0b02012-12-17 19:25:54 +0000768 SkCanvas* recorder = fReplayer->beginRecording(this->getViewWidth(), this->getViewHeight(),
junov@chromium.org9313ca42012-11-02 18:11:49 +0000769 this->recordFlags());
scroggo@google.com82ec0b02012-12-17 19:25:54 +0000770 this->scaleToScaleFactor(recorder);
scroggo@google.com9a412522012-09-07 15:21:18 +0000771 fPicture->draw(recorder);
772}
773
edisonn@google.com84f548c2012-12-18 22:24:03 +0000774bool PlaybackCreationRenderer::render(const SkString*, SkBitmap** out) {
junov@chromium.org9313ca42012-11-02 18:11:49 +0000775 fReplayer->endRecording();
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000776 // Since this class does not actually render, return false.
777 return false;
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000778}
779
scroggo@google.com0a049b82012-11-02 22:01:26 +0000780SkString PlaybackCreationRenderer::getConfigNameInternal() {
781 return SkString("playback_creation");
782}
783
junov@chromium.org9313ca42012-11-02 18:11:49 +0000784///////////////////////////////////////////////////////////////////////////////////////////////
785// SkPicture variants for each BBoxHierarchy type
786
787class RTreePicture : public SkPicture {
788public:
789 virtual SkBBoxHierarchy* createBBoxHierarchy() const SK_OVERRIDE{
790 static const int kRTreeMinChildren = 6;
791 static const int kRTreeMaxChildren = 11;
792 SkScalar aspectRatio = SkScalarDiv(SkIntToScalar(fWidth),
793 SkIntToScalar(fHeight));
794 return SkRTree::Create(kRTreeMinChildren, kRTreeMaxChildren,
795 aspectRatio);
796 }
797};
798
799SkPicture* PictureRenderer::createPicture() {
800 switch (fBBoxHierarchyType) {
801 case kNone_BBoxHierarchyType:
802 return SkNEW(SkPicture);
803 case kRTree_BBoxHierarchyType:
804 return SkNEW(RTreePicture);
junov@chromium.org7b537062012-11-06 18:58:43 +0000805 case kTileGrid_BBoxHierarchyType:
junov@chromium.org29b19e52013-02-27 18:35:16 +0000806 return SkNEW_ARGS(SkTileGridPicture, (fPicture->width(),
807 fPicture->height(), fGridInfo));
junov@chromium.org9313ca42012-11-02 18:11:49 +0000808 }
809 SkASSERT(0); // invalid bbhType
810 return NULL;
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000811}
junov@chromium.org9313ca42012-11-02 18:11:49 +0000812
reed@google.comfe7b1ed2012-11-29 21:00:39 +0000813///////////////////////////////////////////////////////////////////////////////
814
815class GatherRenderer : public PictureRenderer {
816public:
edisonn@google.com84f548c2012-12-18 22:24:03 +0000817 virtual bool render(const SkString* path, SkBitmap** out = NULL)
818 SK_OVERRIDE {
reed@google.comfe7b1ed2012-11-29 21:00:39 +0000819 SkRect bounds = SkRect::MakeWH(SkIntToScalar(fPicture->width()),
820 SkIntToScalar(fPicture->height()));
821 SkData* data = SkPictureUtils::GatherPixelRefs(fPicture, bounds);
822 SkSafeUnref(data);
skia.committer@gmail.comc7b4be72012-12-11 02:01:20 +0000823
reed@google.comfe7b1ed2012-11-29 21:00:39 +0000824 return NULL == path; // we don't have anything to write
825 }
skia.committer@gmail.comc7b4be72012-12-11 02:01:20 +0000826
reed@google.comfe7b1ed2012-11-29 21:00:39 +0000827private:
828 virtual SkString getConfigNameInternal() SK_OVERRIDE {
829 return SkString("gather_pixelrefs");
830 }
831};
832
833PictureRenderer* CreateGatherPixelRefsRenderer() {
834 return SkNEW(GatherRenderer);
835}
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +0000836
reed@google.com5a34fd32012-12-10 16:05:09 +0000837///////////////////////////////////////////////////////////////////////////////
838
839class PictureCloneRenderer : public PictureRenderer {
840public:
edisonn@google.com84f548c2012-12-18 22:24:03 +0000841 virtual bool render(const SkString* path, SkBitmap** out = NULL)
842 SK_OVERRIDE {
reed@google.com5a34fd32012-12-10 16:05:09 +0000843 for (int i = 0; i < 100; ++i) {
844 SkPicture* clone = fPicture->clone();
845 SkSafeUnref(clone);
846 }
skia.committer@gmail.comc7b4be72012-12-11 02:01:20 +0000847
reed@google.com5a34fd32012-12-10 16:05:09 +0000848 return NULL == path; // we don't have anything to write
849 }
skia.committer@gmail.comc7b4be72012-12-11 02:01:20 +0000850
reed@google.com5a34fd32012-12-10 16:05:09 +0000851private:
852 virtual SkString getConfigNameInternal() SK_OVERRIDE {
853 return SkString("picture_clone");
854 }
855};
856
857PictureRenderer* CreatePictureCloneRenderer() {
858 return SkNEW(PictureCloneRenderer);
859}
860
junov@chromium.org9313ca42012-11-02 18:11:49 +0000861} // namespace sk_tools