blob: de7e6411aa3e95d5e2786916b3f04f10fbecdd8c [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"
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +000011#include "SkBitmapHasher.h"
keyar@chromium.org451bb9f2012-07-26 17:27:57 +000012#include "SkCanvas.h"
scroggo@google.com1b1bcc32013-05-21 20:31:23 +000013#include "SkData.h"
keyar@chromium.org451bb9f2012-07-26 17:27:57 +000014#include "SkDevice.h"
robertphillips@google.com94d8f1e2013-12-18 17:25:33 +000015#include "SkDiscardableMemoryPool.h"
keyar@chromium.org451bb9f2012-07-26 17:27:57 +000016#include "SkGPipe.h"
scroggo@google.com58b4ead2012-08-31 16:15:22 +000017#if SK_SUPPORT_GPU
robertphillips@google.comfe1b5362013-02-07 19:45:46 +000018#include "gl/GrGLDefines.h"
scroggo@google.com58b4ead2012-08-31 16:15:22 +000019#include "SkGpuDevice.h"
20#endif
21#include "SkGraphics.h"
22#include "SkImageEncoder.h"
caryclark@google.coma3622372012-11-06 21:26:13 +000023#include "SkMaskFilter.h"
keyar@chromium.orgea826952012-08-23 15:24:13 +000024#include "SkMatrix.h"
keyar@chromium.org451bb9f2012-07-26 17:27:57 +000025#include "SkPicture.h"
scroggo@google.com1b1bcc32013-05-21 20:31:23 +000026#include "SkPictureUtils.h"
27#include "SkPixelRef.h"
junov@chromium.org9313ca42012-11-02 18:11:49 +000028#include "SkRTree.h"
keyar@chromium.orgea826952012-08-23 15:24:13 +000029#include "SkScalar.h"
scroggo@google.coma9e3a362012-11-07 17:52:48 +000030#include "SkStream.h"
keyar@chromium.org9299ede2012-08-21 19:05:08 +000031#include "SkString.h"
scroggo@google.com58b4ead2012-08-31 16:15:22 +000032#include "SkTemplates.h"
junov@chromium.org3cb834b2012-12-13 16:39:53 +000033#include "SkTileGridPicture.h"
keyar@chromium.org451bb9f2012-07-26 17:27:57 +000034#include "SkTDArray.h"
scroggo@google.com58b4ead2012-08-31 16:15:22 +000035#include "SkThreadUtils.h"
keyar@chromium.org451bb9f2012-07-26 17:27:57 +000036#include "SkTypes.h"
keyar@chromium.org4ea96c52012-08-20 15:03:29 +000037
reed@google.come15b2f52013-12-18 04:59:26 +000038static inline SkScalar scalar_log2(SkScalar x) {
39 static const SkScalar log2_conversion_factor = SkScalarDiv(1, SkScalarLog(2));
skia.committer@gmail.com3b85deb2013-12-18 07:01:56 +000040
reed@google.come15b2f52013-12-18 04:59:26 +000041 return SkScalarLog(x) * log2_conversion_factor;
42}
43
keyar@chromium.org451bb9f2012-07-26 17:27:57 +000044namespace sk_tools {
45
46enum {
47 kDefaultTileWidth = 256,
48 kDefaultTileHeight = 256
49};
50
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +000051/* TODO(epoger): These constants are already maintained in 2 other places:
52 * gm/gm_json.py and gm/gm_expectations.cpp. We shouldn't add yet a third place.
53 * Figure out a way to share the definitions instead.
54 */
55const static char kJsonKey_ActualResults[] = "actual-results";
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +000056const static char kJsonKey_ActualResults_NoComparison[] = "no-comparison";
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +000057const static char kJsonKey_Hashtype_Bitmap_64bitMD5[] = "bitmap-64bitMD5";
58
59void ImageResultsSummary::add(const char *testName, const SkBitmap& bitmap) {
60 uint64_t hash;
61 SkAssertResult(SkBitmapHasher::ComputeDigest(bitmap, &hash));
62 Json::Value jsonTypeValuePair;
63 jsonTypeValuePair.append(Json::Value(kJsonKey_Hashtype_Bitmap_64bitMD5));
64 jsonTypeValuePair.append(Json::UInt64(hash));
65 fActualResultsNoComparison[testName] = jsonTypeValuePair;
66}
67
68void ImageResultsSummary::writeToFile(const char *filename) {
69 Json::Value actualResults;
70 actualResults[kJsonKey_ActualResults_NoComparison] = fActualResultsNoComparison;
71 Json::Value root;
72 root[kJsonKey_ActualResults] = actualResults;
73 std::string jsonStdString = root.toStyledString();
74 SkFILEWStream stream(filename);
75 stream.write(jsonStdString.c_str(), jsonStdString.length());
76}
77
keyar@chromium.org9d696c02012-08-07 17:11:33 +000078void PictureRenderer::init(SkPicture* pict) {
keyar@chromium.org78a35c52012-08-20 15:03:44 +000079 SkASSERT(NULL == fPicture);
80 SkASSERT(NULL == fCanvas.get());
81 if (fPicture != NULL || NULL != fCanvas.get()) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +000082 return;
83 }
84
85 SkASSERT(pict != NULL);
keyar@chromium.org78a35c52012-08-20 15:03:44 +000086 if (NULL == pict) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +000087 return;
88 }
89
90 fPicture = pict;
junov@chromium.org9313ca42012-11-02 18:11:49 +000091 fPicture->ref();
keyar@chromium.orga474ce32012-08-20 15:03:57 +000092 fCanvas.reset(this->setupCanvas());
93}
94
caryclark@google.coma3622372012-11-06 21:26:13 +000095class FlagsDrawFilter : public SkDrawFilter {
96public:
97 FlagsDrawFilter(PictureRenderer::DrawFilterFlags* flags) :
98 fFlags(flags) {}
99
reed@google.com971aca72012-11-26 20:26:54 +0000100 virtual bool filter(SkPaint* paint, Type t) {
caryclark@google.coma3622372012-11-06 21:26:13 +0000101 paint->setFlags(paint->getFlags() & ~fFlags[t] & SkPaint::kAllFlags);
robertphillips@google.com49149312013-07-03 15:34:35 +0000102 if (PictureRenderer::kMaskFilter_DrawFilterFlag & fFlags[t]) {
caryclark@google.coma3622372012-11-06 21:26:13 +0000103 SkMaskFilter* maskFilter = paint->getMaskFilter();
robertphillips@google.com49149312013-07-03 15:34:35 +0000104 if (NULL != maskFilter) {
reed@google.com457d8a72012-12-18 18:20:44 +0000105 paint->setMaskFilter(NULL);
caryclark@google.coma3622372012-11-06 21:26:13 +0000106 }
107 }
108 if (PictureRenderer::kHinting_DrawFilterFlag & fFlags[t]) {
109 paint->setHinting(SkPaint::kNo_Hinting);
110 } else if (PictureRenderer::kSlightHinting_DrawFilterFlag & fFlags[t]) {
111 paint->setHinting(SkPaint::kSlight_Hinting);
112 }
reed@google.com971aca72012-11-26 20:26:54 +0000113 return true;
caryclark@google.coma3622372012-11-06 21:26:13 +0000114 }
115
116private:
117 PictureRenderer::DrawFilterFlags* fFlags;
118};
119
scroggo@google.com82ec0b02012-12-17 19:25:54 +0000120static void setUpFilter(SkCanvas* canvas, PictureRenderer::DrawFilterFlags* drawFilters) {
caryclark@google.coma3622372012-11-06 21:26:13 +0000121 if (drawFilters && !canvas->getDrawFilter()) {
122 canvas->setDrawFilter(SkNEW_ARGS(FlagsDrawFilter, (drawFilters)))->unref();
caryclark@google.come3e940c2012-11-07 16:42:17 +0000123 if (drawFilters[0] & PictureRenderer::kAAClip_DrawFilterFlag) {
124 canvas->setAllowSoftClip(false);
125 }
caryclark@google.coma3622372012-11-06 21:26:13 +0000126 }
caryclark@google.coma3622372012-11-06 21:26:13 +0000127}
128
keyar@chromium.orga474ce32012-08-20 15:03:57 +0000129SkCanvas* PictureRenderer::setupCanvas() {
scroggo@google.comc0d5e542012-12-13 21:40:48 +0000130 const int width = this->getViewWidth();
131 const int height = this->getViewHeight();
132 return this->setupCanvas(width, height);
keyar@chromium.orga474ce32012-08-20 15:03:57 +0000133}
134
135SkCanvas* PictureRenderer::setupCanvas(int width, int height) {
caryclark@google.coma3622372012-11-06 21:26:13 +0000136 SkCanvas* canvas;
keyar@chromium.org4ea96c52012-08-20 15:03:29 +0000137 switch(fDeviceType) {
138 case kBitmap_DeviceType: {
139 SkBitmap bitmap;
keyar@chromium.orga474ce32012-08-20 15:03:57 +0000140 sk_tools::setup_bitmap(&bitmap, width, height);
caryclark@google.coma3622372012-11-06 21:26:13 +0000141 canvas = SkNEW_ARGS(SkCanvas, (bitmap));
keyar@chromium.org4ea96c52012-08-20 15:03:29 +0000142 }
scroggo@google.com82ec0b02012-12-17 19:25:54 +0000143 break;
keyar@chromium.org4ea96c52012-08-20 15:03:29 +0000144#if SK_SUPPORT_GPU
scroggo@google.com0556ea02013-02-08 19:38:21 +0000145#if SK_ANGLE
146 case kAngle_DeviceType:
147 // fall through
148#endif
keyar@chromium.org4ea96c52012-08-20 15:03:29 +0000149 case kGPU_DeviceType: {
commit-bot@chromium.orgae403b92013-04-10 17:27:30 +0000150 SkAutoTUnref<GrSurface> target;
scroggo@google.com0556ea02013-02-08 19:38:21 +0000151 if (fGrContext) {
152 // create a render target to back the device
153 GrTextureDesc desc;
154 desc.fConfig = kSkia8888_GrPixelConfig;
155 desc.fFlags = kRenderTarget_GrTextureFlagBit;
156 desc.fWidth = width;
157 desc.fHeight = height;
jvanverth@google.comf6a90332013-05-02 12:39:37 +0000158 desc.fSampleCnt = fSampleCount;
commit-bot@chromium.orgae403b92013-04-10 17:27:30 +0000159 target.reset(fGrContext->createUncachedTexture(desc, NULL, 0));
scroggo@google.com0556ea02013-02-08 19:38:21 +0000160 }
commit-bot@chromium.orgae403b92013-04-10 17:27:30 +0000161 if (NULL == target.get()) {
scroggo@google.com0556ea02013-02-08 19:38:21 +0000162 SkASSERT(0);
163 return NULL;
164 }
165
commit-bot@chromium.orgae403b92013-04-10 17:27:30 +0000166 SkAutoTUnref<SkGpuDevice> device(SkGpuDevice::Create(target));
caryclark@google.coma3622372012-11-06 21:26:13 +0000167 canvas = SkNEW_ARGS(SkCanvas, (device.get()));
scroggo@google.com0556ea02013-02-08 19:38:21 +0000168 break;
keyar@chromium.org4ea96c52012-08-20 15:03:29 +0000169 }
170#endif
171 default:
172 SkASSERT(0);
scroggo@google.com82ec0b02012-12-17 19:25:54 +0000173 return NULL;
keyar@chromium.org4ea96c52012-08-20 15:03:29 +0000174 }
scroggo@google.com82ec0b02012-12-17 19:25:54 +0000175 setUpFilter(canvas, fDrawFilters);
176 this->scaleToScaleFactor(canvas);
177 return canvas;
178}
keyar@chromium.orga474ce32012-08-20 15:03:57 +0000179
scroggo@google.com82ec0b02012-12-17 19:25:54 +0000180void PictureRenderer::scaleToScaleFactor(SkCanvas* canvas) {
181 SkASSERT(canvas != NULL);
182 if (fScaleFactor != SK_Scalar1) {
183 canvas->scale(fScaleFactor, fScaleFactor);
184 }
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000185}
186
187void PictureRenderer::end() {
scroggo@google.com08085f82013-01-28 20:40:24 +0000188 this->resetState(true);
junov@chromium.org9313ca42012-11-02 18:11:49 +0000189 SkSafeUnref(fPicture);
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000190 fPicture = NULL;
191 fCanvas.reset(NULL);
192}
193
scroggo@google.comc0d5e542012-12-13 21:40:48 +0000194int PictureRenderer::getViewWidth() {
195 SkASSERT(fPicture != NULL);
robertphillips@google.com8ac811e2013-02-07 00:13:34 +0000196 int width = SkScalarCeilToInt(fPicture->width() * fScaleFactor);
scroggo@google.comc0d5e542012-12-13 21:40:48 +0000197 if (fViewport.width() > 0) {
198 width = SkMin32(width, fViewport.width());
199 }
200 return width;
201}
202
203int PictureRenderer::getViewHeight() {
204 SkASSERT(fPicture != NULL);
robertphillips@google.com8ac811e2013-02-07 00:13:34 +0000205 int height = SkScalarCeilToInt(fPicture->height() * fScaleFactor);
scroggo@google.comc0d5e542012-12-13 21:40:48 +0000206 if (fViewport.height() > 0) {
207 height = SkMin32(height, fViewport.height());
208 }
209 return height;
210}
211
junov@chromium.org9313ca42012-11-02 18:11:49 +0000212/** Converts fPicture to a picture that uses a BBoxHierarchy.
213 * PictureRenderer subclasses that are used to test picture playback
214 * should call this method during init.
215 */
216void PictureRenderer::buildBBoxHierarchy() {
217 SkASSERT(NULL != fPicture);
218 if (kNone_BBoxHierarchyType != fBBoxHierarchyType && NULL != fPicture) {
219 SkPicture* newPicture = this->createPicture();
220 SkCanvas* recorder = newPicture->beginRecording(fPicture->width(), fPicture->height(),
221 this->recordFlags());
222 fPicture->draw(recorder);
223 newPicture->endRecording();
224 fPicture->unref();
225 fPicture = newPicture;
226 }
227}
228
scroggo@google.com08085f82013-01-28 20:40:24 +0000229void PictureRenderer::resetState(bool callFinish) {
keyar@chromium.org28136b32012-08-20 15:04:15 +0000230#if SK_SUPPORT_GPU
robertphillips@google.com6177e692013-02-28 20:16:25 +0000231 SkGLContextHelper* glContext = this->getGLContext();
scroggo@google.com0556ea02013-02-08 19:38:21 +0000232 if (NULL == glContext) {
233 SkASSERT(kBitmap_DeviceType == fDeviceType);
234 return;
235 }
keyar@chromium.org28136b32012-08-20 15:04:15 +0000236
scroggo@google.com0556ea02013-02-08 19:38:21 +0000237 fGrContext->flush();
238 if (callFinish) {
239 SK_GL(*glContext, Finish());
keyar@chromium.org77a55222012-08-20 15:03:47 +0000240 }
keyar@chromium.orga40c20d2012-08-20 15:04:12 +0000241#endif
keyar@chromium.org77a55222012-08-20 15:03:47 +0000242}
243
robertphillips@google.com94d8f1e2013-12-18 17:25:33 +0000244void PictureRenderer::purgeTextures() {
245 SkDiscardableMemoryPool* pool = SkGetGlobalDiscardableMemoryPool();
246
247 pool->dumpPool();
248
249#if SK_SUPPORT_GPU
250 SkGLContextHelper* glContext = this->getGLContext();
251 if (NULL == glContext) {
252 SkASSERT(kBitmap_DeviceType == fDeviceType);
253 return;
254 }
255
256 // resetState should've already done this
257 fGrContext->flush();
258
259 fGrContext->purgeAllUnlockedResources();
260#endif
261}
262
junov@chromium.org9313ca42012-11-02 18:11:49 +0000263uint32_t PictureRenderer::recordFlags() {
junov@chromium.org100b1c52013-01-16 20:12:22 +0000264 return ((kNone_BBoxHierarchyType == fBBoxHierarchyType) ? 0 :
265 SkPicture::kOptimizeForClippedPlayback_RecordingFlag) |
266 SkPicture::kUsePathBoundsForClip_RecordingFlag;
junov@chromium.org9313ca42012-11-02 18:11:49 +0000267}
268
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000269/**
270 * Write the canvas to the specified path.
271 * @param canvas Must be non-null. Canvas to be written to a file.
272 * @param path Path for the file to be written. Should have no extension; write() will append
273 * an appropriate one. Passed in by value so it can be modified.
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +0000274 * @param jsonSummaryPtr If not null, add image results to this summary.
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000275 * @return bool True if the Canvas is written to a file.
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +0000276 *
277 * TODO(epoger): Right now, all canvases must pass through this function in order to be appended
278 * to the ImageResultsSummary. We need some way to add bitmaps to the ImageResultsSummary
279 * even if --writePath has not been specified (and thus this function is not called).
280 *
281 * One fix would be to pass in these path elements separately, and allow this function to be
282 * called even if --writePath was not specified...
283 * const char *outputDir // NULL if we don't want to write image files to disk
284 * const char *filename // name we use within JSON summary, and as the filename within outputDir
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000285 */
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +0000286static bool write(SkCanvas* canvas, const SkString* path, ImageResultsSummary *jsonSummaryPtr) {
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000287 SkASSERT(canvas != NULL);
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000288 if (NULL == canvas) {
keyar@chromium.org9299ede2012-08-21 19:05:08 +0000289 return false;
290 }
291
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +0000292 SkASSERT(path != NULL); // TODO(epoger): we want to remove this constraint, as noted above
293 SkString fullPathname(*path);
294 fullPathname.append(".png");
295
keyar@chromium.org9299ede2012-08-21 19:05:08 +0000296 SkBitmap bitmap;
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000297 SkISize size = canvas->getDeviceSize();
298 sk_tools::setup_bitmap(&bitmap, size.width(), size.height());
keyar@chromium.org9299ede2012-08-21 19:05:08 +0000299
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000300 canvas->readPixels(&bitmap, 0, 0);
keyar@chromium.org9299ede2012-08-21 19:05:08 +0000301 sk_tools::force_all_opaque(bitmap);
302
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +0000303 if (NULL != jsonSummaryPtr) {
304 // EPOGER: This is a hacky way of constructing the filename associated with the
305 // image checksum; we assume that outputDir is not NULL, and we remove outputDir
306 // from fullPathname.
307 //
308 // EPOGER: what about including the config type within hashFilename? That way,
309 // we could combine results of different config types without conflicting filenames.
310 SkString hashFilename;
311 sk_tools::get_basename(&hashFilename, fullPathname);
312 jsonSummaryPtr->add(hashFilename.c_str(), bitmap);
313 }
314
315 return SkImageEncoder::EncodeFile(fullPathname.c_str(), bitmap, SkImageEncoder::kPNG_Type, 100);
keyar@chromium.org9299ede2012-08-21 19:05:08 +0000316}
317
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000318/**
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +0000319 * If path is non NULL, append number to it, and call write() to write the
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000320 * provided canvas to a file. Returns true if path is NULL or if write() succeeds.
321 */
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +0000322static bool writeAppendNumber(SkCanvas* canvas, const SkString* path, int number,
323 ImageResultsSummary *jsonSummaryPtr) {
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000324 if (NULL == path) {
325 return true;
326 }
327 SkString pathWithNumber(*path);
328 pathWithNumber.appendf("%i", number);
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +0000329 return write(canvas, &pathWithNumber, jsonSummaryPtr);
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000330}
331
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000332///////////////////////////////////////////////////////////////////////////////////////////////
333
djsollen@google.comfd9720c2012-11-06 16:54:40 +0000334SkCanvas* RecordPictureRenderer::setupCanvas(int width, int height) {
335 // defer the canvas setup until the render step
336 return NULL;
337}
338
scroggo@google.com1b1bcc32013-05-21 20:31:23 +0000339static SkData* encode_bitmap_to_data(size_t* offset, const SkBitmap& bm) {
340 SkPixelRef* pr = bm.pixelRef();
341 if (pr != NULL) {
342 SkData* data = pr->refEncodedData();
343 if (data != NULL) {
344 *offset = bm.pixelRefOffset();
345 return data;
346 }
347 }
348 *offset = 0;
349 return SkImageEncoder::EncodeData(bm, SkImageEncoder::kPNG_Type, 100);
scroggo@google.coma9e3a362012-11-07 17:52:48 +0000350}
351
edisonn@google.com84f548c2012-12-18 22:24:03 +0000352bool RecordPictureRenderer::render(const SkString* path, SkBitmap** out) {
junov@chromium.org9313ca42012-11-02 18:11:49 +0000353 SkAutoTUnref<SkPicture> replayer(this->createPicture());
scroggo@google.com82ec0b02012-12-17 19:25:54 +0000354 SkCanvas* recorder = replayer->beginRecording(this->getViewWidth(), this->getViewHeight(),
junov@chromium.org9313ca42012-11-02 18:11:49 +0000355 this->recordFlags());
scroggo@google.com82ec0b02012-12-17 19:25:54 +0000356 this->scaleToScaleFactor(recorder);
scroggo@google.com9a412522012-09-07 15:21:18 +0000357 fPicture->draw(recorder);
junov@chromium.org9313ca42012-11-02 18:11:49 +0000358 replayer->endRecording();
scroggo@google.coma9e3a362012-11-07 17:52:48 +0000359 if (path != NULL) {
360 // Record the new picture as a new SKP with PNG encoded bitmaps.
361 SkString skpPath(*path);
362 // ".skp" was removed from 'path' before being passed in here.
363 skpPath.append(".skp");
364 SkFILEWStream stream(skpPath.c_str());
scroggo@google.com1b1bcc32013-05-21 20:31:23 +0000365 replayer->serialize(&stream, &encode_bitmap_to_data);
scroggo@google.coma9e3a362012-11-07 17:52:48 +0000366 return true;
367 }
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000368 return false;
scroggo@google.com9a412522012-09-07 15:21:18 +0000369}
370
scroggo@google.com0a049b82012-11-02 22:01:26 +0000371SkString RecordPictureRenderer::getConfigNameInternal() {
372 return SkString("record");
373}
374
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000375///////////////////////////////////////////////////////////////////////////////////////////////
376
edisonn@google.com84f548c2012-12-18 22:24:03 +0000377bool PipePictureRenderer::render(const SkString* path, SkBitmap** out) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000378 SkASSERT(fCanvas.get() != NULL);
379 SkASSERT(fPicture != NULL);
keyar@chromium.org78a35c52012-08-20 15:03:44 +0000380 if (NULL == fCanvas.get() || NULL == fPicture) {
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000381 return false;
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000382 }
383
384 PipeController pipeController(fCanvas.get());
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000385 SkGPipeWriter writer;
386 SkCanvas* pipeCanvas = writer.startRecording(&pipeController);
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000387 pipeCanvas->drawPicture(*fPicture);
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000388 writer.endRecording();
scroggo@google.com9a412522012-09-07 15:21:18 +0000389 fCanvas->flush();
borenet@google.com070d3542012-10-26 13:26:55 +0000390 if (NULL != path) {
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +0000391 return write(fCanvas, path, fJsonSummaryPtr);
borenet@google.com070d3542012-10-26 13:26:55 +0000392 }
edisonn@google.com84f548c2012-12-18 22:24:03 +0000393 if (NULL != out) {
394 *out = SkNEW(SkBitmap);
395 setup_bitmap(*out, fPicture->width(), fPicture->height());
396 fCanvas->readPixels(*out, 0, 0);
skia.committer@gmail.coma7d8e3e2012-12-19 02:01:38 +0000397 }
borenet@google.com070d3542012-10-26 13:26:55 +0000398 return true;
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000399}
400
scroggo@google.com0a049b82012-11-02 22:01:26 +0000401SkString PipePictureRenderer::getConfigNameInternal() {
402 return SkString("pipe");
403}
404
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000405///////////////////////////////////////////////////////////////////////////////////////////////
406
junov@chromium.org9313ca42012-11-02 18:11:49 +0000407void SimplePictureRenderer::init(SkPicture* picture) {
408 INHERITED::init(picture);
409 this->buildBBoxHierarchy();
410}
411
edisonn@google.com84f548c2012-12-18 22:24:03 +0000412bool SimplePictureRenderer::render(const SkString* path, SkBitmap** out) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000413 SkASSERT(fCanvas.get() != NULL);
414 SkASSERT(fPicture != NULL);
keyar@chromium.org78a35c52012-08-20 15:03:44 +0000415 if (NULL == fCanvas.get() || NULL == fPicture) {
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000416 return false;
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000417 }
418
419 fCanvas->drawPicture(*fPicture);
scroggo@google.com9a412522012-09-07 15:21:18 +0000420 fCanvas->flush();
borenet@google.com070d3542012-10-26 13:26:55 +0000421 if (NULL != path) {
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +0000422 return write(fCanvas, path, fJsonSummaryPtr);
borenet@google.com070d3542012-10-26 13:26:55 +0000423 }
skia.committer@gmail.coma7d8e3e2012-12-19 02:01:38 +0000424
edisonn@google.com84f548c2012-12-18 22:24:03 +0000425 if (NULL != out) {
426 *out = SkNEW(SkBitmap);
427 setup_bitmap(*out, fPicture->width(), fPicture->height());
428 fCanvas->readPixels(*out, 0, 0);
429 }
430
borenet@google.com070d3542012-10-26 13:26:55 +0000431 return true;
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000432}
433
scroggo@google.com0a049b82012-11-02 22:01:26 +0000434SkString SimplePictureRenderer::getConfigNameInternal() {
435 return SkString("simple");
436}
437
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000438///////////////////////////////////////////////////////////////////////////////////////////////
439
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000440TiledPictureRenderer::TiledPictureRenderer()
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000441 : fTileWidth(kDefaultTileWidth)
rileya@google.comb947b912012-08-29 17:35:07 +0000442 , fTileHeight(kDefaultTileHeight)
rileya@google.coma04dc022012-09-10 19:01:38 +0000443 , fTileWidthPercentage(0.0)
rileya@google.comb947b912012-08-29 17:35:07 +0000444 , fTileHeightPercentage(0.0)
scroggo@google.comcbcef702012-12-13 22:09:28 +0000445 , fTileMinPowerOf2Width(0)
446 , fCurrentTileOffset(-1)
447 , fTilesX(0)
448 , fTilesY(0) { }
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000449
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000450void TiledPictureRenderer::init(SkPicture* pict) {
451 SkASSERT(pict != NULL);
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000452 SkASSERT(0 == fTileRects.count());
453 if (NULL == pict || fTileRects.count() != 0) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000454 return;
455 }
456
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000457 // Do not call INHERITED::init(), which would create a (potentially large) canvas which is not
458 // used by bench_pictures.
459 fPicture = pict;
junov@chromium.org9313ca42012-11-02 18:11:49 +0000460 fPicture->ref();
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000461 this->buildBBoxHierarchy();
keyar@chromium.orgcc6e5ef2012-07-27 20:09:26 +0000462
463 if (fTileWidthPercentage > 0) {
robertphillips@google.com5d8d1862012-08-15 14:36:41 +0000464 fTileWidth = sk_float_ceil2int(float(fTileWidthPercentage * fPicture->width() / 100));
keyar@chromium.orgcc6e5ef2012-07-27 20:09:26 +0000465 }
466 if (fTileHeightPercentage > 0) {
robertphillips@google.com5d8d1862012-08-15 14:36:41 +0000467 fTileHeight = sk_float_ceil2int(float(fTileHeightPercentage * fPicture->height() / 100));
keyar@chromium.orgcc6e5ef2012-07-27 20:09:26 +0000468 }
469
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000470 if (fTileMinPowerOf2Width > 0) {
471 this->setupPowerOf2Tiles();
472 } else {
473 this->setupTiles();
474 }
scroggo@google.comcbcef702012-12-13 22:09:28 +0000475 fCanvas.reset(this->setupCanvas(fTileWidth, fTileHeight));
476 // Initialize to -1 so that the first call to nextTile will set this up to draw tile 0 on the
477 // first call to drawCurrentTile.
478 fCurrentTileOffset = -1;
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000479}
480
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000481void TiledPictureRenderer::end() {
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000482 fTileRects.reset();
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000483 this->INHERITED::end();
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000484}
485
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000486void TiledPictureRenderer::setupTiles() {
scroggo@google.comc0d5e542012-12-13 21:40:48 +0000487 // Only use enough tiles to cover the viewport
488 const int width = this->getViewWidth();
489 const int height = this->getViewHeight();
490
scroggo@google.comcbcef702012-12-13 22:09:28 +0000491 fTilesX = fTilesY = 0;
scroggo@google.comc0d5e542012-12-13 21:40:48 +0000492 for (int tile_y_start = 0; tile_y_start < height; tile_y_start += fTileHeight) {
scroggo@google.comcbcef702012-12-13 22:09:28 +0000493 fTilesY++;
scroggo@google.comc0d5e542012-12-13 21:40:48 +0000494 for (int tile_x_start = 0; tile_x_start < width; tile_x_start += fTileWidth) {
scroggo@google.comcbcef702012-12-13 22:09:28 +0000495 if (0 == tile_y_start) {
496 // Only count tiles in the X direction on the first pass.
497 fTilesX++;
498 }
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000499 *fTileRects.append() = SkRect::MakeXYWH(SkIntToScalar(tile_x_start),
500 SkIntToScalar(tile_y_start),
501 SkIntToScalar(fTileWidth),
502 SkIntToScalar(fTileHeight));
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000503 }
504 }
505}
506
scroggo@google.comcbcef702012-12-13 22:09:28 +0000507bool TiledPictureRenderer::tileDimensions(int &x, int &y) {
508 if (fTileRects.count() == 0 || NULL == fPicture) {
509 return false;
510 }
511 x = fTilesX;
512 y = fTilesY;
513 return true;
514}
515
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000516// The goal of the powers of two tiles is to minimize the amount of wasted tile
517// space in the width-wise direction and then minimize the number of tiles. The
518// constraints are that every tile must have a pixel width that is a power of
519// two and also be of some minimal width (that is also a power of two).
520//
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000521// This is solved by first taking our picture size and rounding it up to the
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000522// multiple of the minimal width. The binary representation of this rounded
523// value gives us the tiles we need: a bit of value one means we need a tile of
524// that size.
525void TiledPictureRenderer::setupPowerOf2Tiles() {
scroggo@google.comc0d5e542012-12-13 21:40:48 +0000526 // Only use enough tiles to cover the viewport
527 const int width = this->getViewWidth();
528 const int height = this->getViewHeight();
529
530 int rounded_value = width;
531 if (width % fTileMinPowerOf2Width != 0) {
532 rounded_value = width - (width % fTileMinPowerOf2Width) + fTileMinPowerOf2Width;
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000533 }
534
reed@google.come15b2f52013-12-18 04:59:26 +0000535 int num_bits = SkScalarCeilToInt(scalar_log2(SkIntToScalar(width)));
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000536 int largest_possible_tile_size = 1 << num_bits;
537
scroggo@google.comcbcef702012-12-13 22:09:28 +0000538 fTilesX = fTilesY = 0;
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000539 // The tile height is constant for a particular picture.
scroggo@google.comc0d5e542012-12-13 21:40:48 +0000540 for (int tile_y_start = 0; tile_y_start < height; tile_y_start += fTileHeight) {
scroggo@google.comcbcef702012-12-13 22:09:28 +0000541 fTilesY++;
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000542 int tile_x_start = 0;
543 int current_width = largest_possible_tile_size;
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000544 // Set fTileWidth to be the width of the widest tile, so that each canvas is large enough
545 // to draw each tile.
546 fTileWidth = current_width;
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000547
548 while (current_width >= fTileMinPowerOf2Width) {
549 // It is very important this is a bitwise AND.
550 if (current_width & rounded_value) {
scroggo@google.comcbcef702012-12-13 22:09:28 +0000551 if (0 == tile_y_start) {
552 // Only count tiles in the X direction on the first pass.
553 fTilesX++;
554 }
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000555 *fTileRects.append() = SkRect::MakeXYWH(SkIntToScalar(tile_x_start),
556 SkIntToScalar(tile_y_start),
557 SkIntToScalar(current_width),
558 SkIntToScalar(fTileHeight));
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000559 tile_x_start += current_width;
560 }
561
562 current_width >>= 1;
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000563 }
564 }
565}
566
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000567/**
568 * Draw the specified playback to the canvas translated to rectangle provided, so that this mini
569 * canvas represents the rectangle's portion of the overall picture.
570 * Saves and restores so that the initial clip and matrix return to their state before this function
571 * is called.
572 */
573template<class T>
574static void DrawTileToCanvas(SkCanvas* canvas, const SkRect& tileRect, T* playback) {
575 int saveCount = canvas->save();
scroggo@google.com82ec0b02012-12-17 19:25:54 +0000576 // Translate so that we draw the correct portion of the picture.
577 // Perform a postTranslate so that the scaleFactor does not interfere with the positioning.
578 SkMatrix mat(canvas->getTotalMatrix());
579 mat.postTranslate(-tileRect.fLeft, -tileRect.fTop);
580 canvas->setMatrix(mat);
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000581 playback->draw(canvas);
582 canvas->restoreToCount(saveCount);
583 canvas->flush();
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000584}
585
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000586///////////////////////////////////////////////////////////////////////////////////////////////
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000587
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +0000588/**
589 * Copies the entirety of the src bitmap (typically a tile) into a portion of the dst bitmap.
590 * If the src bitmap is too large to fit within the dst bitmap after the x and y
591 * offsets have been applied, any excess will be ignored (so only the top-left portion of the
592 * src bitmap will be copied).
593 *
594 * @param src source bitmap
595 * @param dst destination bitmap
596 * @param xOffset x-offset within destination bitmap
597 * @param yOffset y-offset within destination bitmap
598 */
599static void bitmapCopyAtOffset(const SkBitmap& src, SkBitmap* dst,
600 int xOffset, int yOffset) {
601 for (int y = 0; y <src.height() && y + yOffset < dst->height() ; y++) {
602 for (int x = 0; x < src.width() && x + xOffset < dst->width() ; x++) {
603 *dst->getAddr32(xOffset + x, yOffset + y) = *src.getAddr32(x, y);
edisonn@google.com84f548c2012-12-18 22:24:03 +0000604 }
605 }
606}
607
scroggo@google.comcbcef702012-12-13 22:09:28 +0000608bool TiledPictureRenderer::nextTile(int &i, int &j) {
609 if (++fCurrentTileOffset < fTileRects.count()) {
610 i = fCurrentTileOffset % fTilesX;
611 j = fCurrentTileOffset / fTilesX;
612 return true;
613 }
614 return false;
615}
616
617void TiledPictureRenderer::drawCurrentTile() {
618 SkASSERT(fCurrentTileOffset >= 0 && fCurrentTileOffset < fTileRects.count());
619 DrawTileToCanvas(fCanvas, fTileRects[fCurrentTileOffset], fPicture);
620}
621
edisonn@google.com84f548c2012-12-18 22:24:03 +0000622bool TiledPictureRenderer::render(const SkString* path, SkBitmap** out) {
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000623 SkASSERT(fPicture != NULL);
624 if (NULL == fPicture) {
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000625 return false;
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000626 }
627
edisonn@google.com84f548c2012-12-18 22:24:03 +0000628 SkBitmap bitmap;
629 if (out){
630 *out = SkNEW(SkBitmap);
631 setup_bitmap(*out, fPicture->width(), fPicture->height());
632 setup_bitmap(&bitmap, fTileWidth, fTileHeight);
633 }
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000634 bool success = true;
635 for (int i = 0; i < fTileRects.count(); ++i) {
scroggo@google.comcbcef702012-12-13 22:09:28 +0000636 DrawTileToCanvas(fCanvas, fTileRects[i], fPicture);
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000637 if (NULL != path) {
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +0000638 success &= writeAppendNumber(fCanvas, path, i, fJsonSummaryPtr);
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000639 }
edisonn@google.com84f548c2012-12-18 22:24:03 +0000640 if (NULL != out) {
641 if (fCanvas->readPixels(&bitmap, 0, 0)) {
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +0000642 // Add this tile to the entire bitmap.
643 bitmapCopyAtOffset(bitmap, *out, SkScalarFloorToInt(fTileRects[i].left()),
644 SkScalarFloorToInt(fTileRects[i].top()));
edisonn@google.com84f548c2012-12-18 22:24:03 +0000645 } else {
646 success = false;
647 }
648 }
keyar@chromium.org163b5672012-08-01 17:53:29 +0000649 }
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000650 return success;
keyar@chromium.org163b5672012-08-01 17:53:29 +0000651}
652
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000653SkCanvas* TiledPictureRenderer::setupCanvas(int width, int height) {
654 SkCanvas* canvas = this->INHERITED::setupCanvas(width, height);
655 SkASSERT(fPicture != NULL);
scroggo@google.com82ec0b02012-12-17 19:25:54 +0000656 // Clip the tile to an area that is completely inside both the SkPicture and the viewport. This
657 // is mostly important for tiles on the right and bottom edges as they may go over this area and
658 // the picture may have some commands that draw outside of this area and so should not actually
659 // be written.
660 // Uses a clipRegion so that it will be unaffected by the scale factor, which may have been set
661 // by INHERITED::setupCanvas.
662 SkRegion clipRegion;
663 clipRegion.setRect(0, 0, this->getViewWidth(), this->getViewHeight());
664 canvas->clipRegion(clipRegion);
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000665 return canvas;
666}
scroggo@google.com0a049b82012-11-02 22:01:26 +0000667
668SkString TiledPictureRenderer::getConfigNameInternal() {
669 SkString name;
670 if (fTileMinPowerOf2Width > 0) {
671 name.append("pow2tile_");
672 name.appendf("%i", fTileMinPowerOf2Width);
673 } else {
674 name.append("tile_");
675 if (fTileWidthPercentage > 0) {
676 name.appendf("%.f%%", fTileWidthPercentage);
677 } else {
678 name.appendf("%i", fTileWidth);
679 }
680 }
681 name.append("x");
682 if (fTileHeightPercentage > 0) {
683 name.appendf("%.f%%", fTileHeightPercentage);
684 } else {
685 name.appendf("%i", fTileHeight);
686 }
687 return name;
688}
689
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000690///////////////////////////////////////////////////////////////////////////////////////////////
691
692// Holds all of the information needed to draw a set of tiles.
693class CloneData : public SkRunnable {
694
695public:
696 CloneData(SkPicture* clone, SkCanvas* canvas, SkTDArray<SkRect>& rects, int start, int end,
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +0000697 SkRunnable* done, ImageResultsSummary* jsonSummaryPtr)
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000698 : fClone(clone)
699 , fCanvas(canvas)
700 , fPath(NULL)
701 , fRects(rects)
702 , fStart(start)
703 , fEnd(end)
704 , fSuccess(NULL)
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +0000705 , fDone(done)
706 , fJsonSummaryPtr(jsonSummaryPtr) {
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000707 SkASSERT(fDone != NULL);
708 }
709
710 virtual void run() SK_OVERRIDE {
711 SkGraphics::SetTLSFontCacheLimit(1024 * 1024);
edisonn@google.com84f548c2012-12-18 22:24:03 +0000712
713 SkBitmap bitmap;
714 if (fBitmap != NULL) {
715 // All tiles are the same size.
jvanverth@google.com9c4e5ac2013-01-07 18:41:28 +0000716 setup_bitmap(&bitmap, SkScalarFloorToInt(fRects[0].width()), SkScalarFloorToInt(fRects[0].height()));
edisonn@google.com84f548c2012-12-18 22:24:03 +0000717 }
skia.committer@gmail.coma7d8e3e2012-12-19 02:01:38 +0000718
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000719 for (int i = fStart; i < fEnd; i++) {
720 DrawTileToCanvas(fCanvas, fRects[i], fClone);
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +0000721 if ((fPath != NULL) && !writeAppendNumber(fCanvas, fPath, i, fJsonSummaryPtr)
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000722 && fSuccess != NULL) {
723 *fSuccess = false;
724 // If one tile fails to write to a file, do not continue drawing the rest.
725 break;
726 }
edisonn@google.com84f548c2012-12-18 22:24:03 +0000727 if (fBitmap != NULL) {
728 if (fCanvas->readPixels(&bitmap, 0, 0)) {
729 SkAutoLockPixels alp(*fBitmap);
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +0000730 bitmapCopyAtOffset(bitmap, fBitmap, SkScalarFloorToInt(fRects[i].left()),
731 SkScalarFloorToInt(fRects[i].top()));
edisonn@google.com84f548c2012-12-18 22:24:03 +0000732 } else {
733 *fSuccess = false;
734 // If one tile fails to read pixels, do not continue drawing the rest.
735 break;
736 }
737 }
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000738 }
739 fDone->run();
740 }
741
742 void setPathAndSuccess(const SkString* path, bool* success) {
743 fPath = path;
744 fSuccess = success;
745 }
746
edisonn@google.com84f548c2012-12-18 22:24:03 +0000747 void setBitmap(SkBitmap* bitmap) {
748 fBitmap = bitmap;
749 }
750
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000751private:
752 // All pointers unowned.
753 SkPicture* fClone; // Picture to draw from. Each CloneData has a unique one which
754 // is threadsafe.
755 SkCanvas* fCanvas; // Canvas to draw to. Reused for each tile.
756 const SkString* fPath; // If non-null, path to write the result to as a PNG.
757 SkTDArray<SkRect>& fRects; // All tiles of the picture.
758 const int fStart; // Range of tiles drawn by this thread.
759 const int fEnd;
760 bool* fSuccess; // Only meaningful if path is non-null. Shared by all threads,
761 // and only set to false upon failure to write to a PNG.
762 SkRunnable* fDone;
edisonn@google.com84f548c2012-12-18 22:24:03 +0000763 SkBitmap* fBitmap;
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +0000764 ImageResultsSummary* fJsonSummaryPtr;
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000765};
766
767MultiCorePictureRenderer::MultiCorePictureRenderer(int threadCount)
768: fNumThreads(threadCount)
769, fThreadPool(threadCount)
770, fCountdown(threadCount) {
771 // Only need to create fNumThreads - 1 clones, since one thread will use the base
772 // picture.
773 fPictureClones = SkNEW_ARRAY(SkPicture, fNumThreads - 1);
774 fCloneData = SkNEW_ARRAY(CloneData*, fNumThreads);
775}
776
777void MultiCorePictureRenderer::init(SkPicture *pict) {
778 // Set fPicture and the tiles.
779 this->INHERITED::init(pict);
780 for (int i = 0; i < fNumThreads; ++i) {
781 *fCanvasPool.append() = this->setupCanvas(this->getTileWidth(), this->getTileHeight());
782 }
783 // Only need to create fNumThreads - 1 clones, since one thread will use the base picture.
784 fPicture->clone(fPictureClones, fNumThreads - 1);
785 // Populate each thread with the appropriate data.
786 // Group the tiles into nearly equal size chunks, rounding up so we're sure to cover them all.
787 const int chunkSize = (fTileRects.count() + fNumThreads - 1) / fNumThreads;
788
789 for (int i = 0; i < fNumThreads; i++) {
790 SkPicture* pic;
791 if (i == fNumThreads-1) {
792 // The last set will use the original SkPicture.
793 pic = fPicture;
794 } else {
795 pic = &fPictureClones[i];
796 }
797 const int start = i * chunkSize;
798 const int end = SkMin32(start + chunkSize, fTileRects.count());
799 fCloneData[i] = SkNEW_ARGS(CloneData,
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +0000800 (pic, fCanvasPool[i], fTileRects, start, end, &fCountdown,
801 fJsonSummaryPtr));
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000802 }
803}
804
edisonn@google.com84f548c2012-12-18 22:24:03 +0000805bool MultiCorePictureRenderer::render(const SkString *path, SkBitmap** out) {
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000806 bool success = true;
807 if (path != NULL) {
808 for (int i = 0; i < fNumThreads-1; i++) {
809 fCloneData[i]->setPathAndSuccess(path, &success);
810 }
811 }
812
edisonn@google.com84f548c2012-12-18 22:24:03 +0000813 if (NULL != out) {
814 *out = SkNEW(SkBitmap);
815 setup_bitmap(*out, fPicture->width(), fPicture->height());
816 for (int i = 0; i < fNumThreads; i++) {
817 fCloneData[i]->setBitmap(*out);
818 }
819 } else {
820 for (int i = 0; i < fNumThreads; i++) {
821 fCloneData[i]->setBitmap(NULL);
822 }
823 }
824
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000825 fCountdown.reset(fNumThreads);
826 for (int i = 0; i < fNumThreads; i++) {
827 fThreadPool.add(fCloneData[i]);
828 }
829 fCountdown.wait();
830
831 return success;
832}
833
834void MultiCorePictureRenderer::end() {
835 for (int i = 0; i < fNumThreads - 1; i++) {
836 SkDELETE(fCloneData[i]);
837 fCloneData[i] = NULL;
838 }
839
840 fCanvasPool.unrefAll();
841
842 this->INHERITED::end();
843}
844
845MultiCorePictureRenderer::~MultiCorePictureRenderer() {
846 // Each individual CloneData was deleted in end.
847 SkDELETE_ARRAY(fCloneData);
848 SkDELETE_ARRAY(fPictureClones);
849}
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000850
scroggo@google.com0a049b82012-11-02 22:01:26 +0000851SkString MultiCorePictureRenderer::getConfigNameInternal() {
852 SkString name = this->INHERITED::getConfigNameInternal();
853 name.appendf("_multi_%i_threads", fNumThreads);
854 return name;
855}
856
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000857///////////////////////////////////////////////////////////////////////////////////////////////
scroggo@google.com9a412522012-09-07 15:21:18 +0000858
859void PlaybackCreationRenderer::setup() {
junov@chromium.org9313ca42012-11-02 18:11:49 +0000860 fReplayer.reset(this->createPicture());
scroggo@google.com82ec0b02012-12-17 19:25:54 +0000861 SkCanvas* recorder = fReplayer->beginRecording(this->getViewWidth(), this->getViewHeight(),
junov@chromium.org9313ca42012-11-02 18:11:49 +0000862 this->recordFlags());
scroggo@google.com82ec0b02012-12-17 19:25:54 +0000863 this->scaleToScaleFactor(recorder);
scroggo@google.com9a412522012-09-07 15:21:18 +0000864 fPicture->draw(recorder);
865}
866
edisonn@google.com84f548c2012-12-18 22:24:03 +0000867bool PlaybackCreationRenderer::render(const SkString*, SkBitmap** out) {
junov@chromium.org9313ca42012-11-02 18:11:49 +0000868 fReplayer->endRecording();
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000869 // Since this class does not actually render, return false.
870 return false;
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000871}
872
scroggo@google.com0a049b82012-11-02 22:01:26 +0000873SkString PlaybackCreationRenderer::getConfigNameInternal() {
874 return SkString("playback_creation");
875}
876
junov@chromium.org9313ca42012-11-02 18:11:49 +0000877///////////////////////////////////////////////////////////////////////////////////////////////
878// SkPicture variants for each BBoxHierarchy type
879
880class RTreePicture : public SkPicture {
881public:
882 virtual SkBBoxHierarchy* createBBoxHierarchy() const SK_OVERRIDE{
883 static const int kRTreeMinChildren = 6;
884 static const int kRTreeMaxChildren = 11;
885 SkScalar aspectRatio = SkScalarDiv(SkIntToScalar(fWidth),
886 SkIntToScalar(fHeight));
sglez@google.com8c902122013-08-30 17:27:47 +0000887 bool sortDraws = false;
junov@chromium.org9313ca42012-11-02 18:11:49 +0000888 return SkRTree::Create(kRTreeMinChildren, kRTreeMaxChildren,
sglez@google.com8c902122013-08-30 17:27:47 +0000889 aspectRatio, sortDraws);
junov@chromium.org9313ca42012-11-02 18:11:49 +0000890 }
891};
892
893SkPicture* PictureRenderer::createPicture() {
894 switch (fBBoxHierarchyType) {
895 case kNone_BBoxHierarchyType:
896 return SkNEW(SkPicture);
897 case kRTree_BBoxHierarchyType:
898 return SkNEW(RTreePicture);
junov@chromium.org7b537062012-11-06 18:58:43 +0000899 case kTileGrid_BBoxHierarchyType:
junov@chromium.org29b19e52013-02-27 18:35:16 +0000900 return SkNEW_ARGS(SkTileGridPicture, (fPicture->width(),
901 fPicture->height(), fGridInfo));
junov@chromium.org9313ca42012-11-02 18:11:49 +0000902 }
903 SkASSERT(0); // invalid bbhType
904 return NULL;
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000905}
junov@chromium.org9313ca42012-11-02 18:11:49 +0000906
reed@google.comfe7b1ed2012-11-29 21:00:39 +0000907///////////////////////////////////////////////////////////////////////////////
908
909class GatherRenderer : public PictureRenderer {
910public:
edisonn@google.com84f548c2012-12-18 22:24:03 +0000911 virtual bool render(const SkString* path, SkBitmap** out = NULL)
912 SK_OVERRIDE {
reed@google.comfe7b1ed2012-11-29 21:00:39 +0000913 SkRect bounds = SkRect::MakeWH(SkIntToScalar(fPicture->width()),
914 SkIntToScalar(fPicture->height()));
915 SkData* data = SkPictureUtils::GatherPixelRefs(fPicture, bounds);
916 SkSafeUnref(data);
skia.committer@gmail.comc7b4be72012-12-11 02:01:20 +0000917
reed@google.comfe7b1ed2012-11-29 21:00:39 +0000918 return NULL == path; // we don't have anything to write
919 }
skia.committer@gmail.comc7b4be72012-12-11 02:01:20 +0000920
reed@google.comfe7b1ed2012-11-29 21:00:39 +0000921private:
922 virtual SkString getConfigNameInternal() SK_OVERRIDE {
923 return SkString("gather_pixelrefs");
924 }
925};
926
927PictureRenderer* CreateGatherPixelRefsRenderer() {
928 return SkNEW(GatherRenderer);
929}
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +0000930
reed@google.com5a34fd32012-12-10 16:05:09 +0000931///////////////////////////////////////////////////////////////////////////////
932
933class PictureCloneRenderer : public PictureRenderer {
934public:
edisonn@google.com84f548c2012-12-18 22:24:03 +0000935 virtual bool render(const SkString* path, SkBitmap** out = NULL)
936 SK_OVERRIDE {
reed@google.com5a34fd32012-12-10 16:05:09 +0000937 for (int i = 0; i < 100; ++i) {
938 SkPicture* clone = fPicture->clone();
939 SkSafeUnref(clone);
940 }
skia.committer@gmail.comc7b4be72012-12-11 02:01:20 +0000941
reed@google.com5a34fd32012-12-10 16:05:09 +0000942 return NULL == path; // we don't have anything to write
943 }
skia.committer@gmail.comc7b4be72012-12-11 02:01:20 +0000944
reed@google.com5a34fd32012-12-10 16:05:09 +0000945private:
946 virtual SkString getConfigNameInternal() SK_OVERRIDE {
947 return SkString("picture_clone");
948 }
949};
950
951PictureRenderer* CreatePictureCloneRenderer() {
952 return SkNEW(PictureCloneRenderer);
953}
954
junov@chromium.org9313ca42012-11-02 18:11:49 +0000955} // namespace sk_tools