blob: 88ddc7513748161bbeed1b804b88b06c663f3ca3 [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"
15#include "SkGPipe.h"
scroggo@google.com58b4ead2012-08-31 16:15:22 +000016#if SK_SUPPORT_GPU
robertphillips@google.comfe1b5362013-02-07 19:45:46 +000017#include "gl/GrGLDefines.h"
scroggo@google.com58b4ead2012-08-31 16:15:22 +000018#include "SkGpuDevice.h"
19#endif
20#include "SkGraphics.h"
21#include "SkImageEncoder.h"
caryclark@google.coma3622372012-11-06 21:26:13 +000022#include "SkMaskFilter.h"
keyar@chromium.orgea826952012-08-23 15:24:13 +000023#include "SkMatrix.h"
keyar@chromium.org451bb9f2012-07-26 17:27:57 +000024#include "SkPicture.h"
scroggo@google.com1b1bcc32013-05-21 20:31:23 +000025#include "SkPictureUtils.h"
26#include "SkPixelRef.h"
junov@chromium.org9313ca42012-11-02 18:11:49 +000027#include "SkRTree.h"
keyar@chromium.orgea826952012-08-23 15:24:13 +000028#include "SkScalar.h"
scroggo@google.coma9e3a362012-11-07 17:52:48 +000029#include "SkStream.h"
keyar@chromium.org9299ede2012-08-21 19:05:08 +000030#include "SkString.h"
scroggo@google.com58b4ead2012-08-31 16:15:22 +000031#include "SkTemplates.h"
junov@chromium.org3cb834b2012-12-13 16:39:53 +000032#include "SkTileGridPicture.h"
keyar@chromium.org451bb9f2012-07-26 17:27:57 +000033#include "SkTDArray.h"
scroggo@google.com58b4ead2012-08-31 16:15:22 +000034#include "SkThreadUtils.h"
keyar@chromium.org451bb9f2012-07-26 17:27:57 +000035#include "SkTypes.h"
keyar@chromium.org4ea96c52012-08-20 15:03:29 +000036
keyar@chromium.org451bb9f2012-07-26 17:27:57 +000037namespace sk_tools {
38
39enum {
40 kDefaultTileWidth = 256,
41 kDefaultTileHeight = 256
42};
43
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +000044/* TODO(epoger): These constants are already maintained in 2 other places:
45 * gm/gm_json.py and gm/gm_expectations.cpp. We shouldn't add yet a third place.
46 * Figure out a way to share the definitions instead.
47 */
48const static char kJsonKey_ActualResults[] = "actual-results";
49const static char kJsonKey_ActualResults_Failed[] = "failed";
50const static char kJsonKey_ActualResults_FailureIgnored[]= "failure-ignored";
51const static char kJsonKey_ActualResults_NoComparison[] = "no-comparison";
52const static char kJsonKey_ActualResults_Succeeded[] = "succeeded";
53const static char kJsonKey_ExpectedResults[] = "expected-results";
54const static char kJsonKey_ExpectedResults_AllowedDigests[] = "allowed-digests";
55const static char kJsonKey_ExpectedResults_IgnoreFailure[] = "ignore-failure";
56const static char kJsonKey_Hashtype_Bitmap_64bitMD5[] = "bitmap-64bitMD5";
57
58void ImageResultsSummary::add(const char *testName, const SkBitmap& bitmap) {
59 uint64_t hash;
60 SkAssertResult(SkBitmapHasher::ComputeDigest(bitmap, &hash));
61 Json::Value jsonTypeValuePair;
62 jsonTypeValuePair.append(Json::Value(kJsonKey_Hashtype_Bitmap_64bitMD5));
63 jsonTypeValuePair.append(Json::UInt64(hash));
64 fActualResultsNoComparison[testName] = jsonTypeValuePair;
65}
66
67void ImageResultsSummary::writeToFile(const char *filename) {
68 Json::Value actualResults;
69 actualResults[kJsonKey_ActualResults_NoComparison] = fActualResultsNoComparison;
70 Json::Value root;
71 root[kJsonKey_ActualResults] = actualResults;
72 std::string jsonStdString = root.toStyledString();
73 SkFILEWStream stream(filename);
74 stream.write(jsonStdString.c_str(), jsonStdString.length());
75}
76
keyar@chromium.org9d696c02012-08-07 17:11:33 +000077void PictureRenderer::init(SkPicture* pict) {
keyar@chromium.org78a35c52012-08-20 15:03:44 +000078 SkASSERT(NULL == fPicture);
79 SkASSERT(NULL == fCanvas.get());
80 if (fPicture != NULL || NULL != fCanvas.get()) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +000081 return;
82 }
83
84 SkASSERT(pict != NULL);
keyar@chromium.org78a35c52012-08-20 15:03:44 +000085 if (NULL == pict) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +000086 return;
87 }
88
89 fPicture = pict;
junov@chromium.org9313ca42012-11-02 18:11:49 +000090 fPicture->ref();
keyar@chromium.orga474ce32012-08-20 15:03:57 +000091 fCanvas.reset(this->setupCanvas());
92}
93
caryclark@google.coma3622372012-11-06 21:26:13 +000094class FlagsDrawFilter : public SkDrawFilter {
95public:
96 FlagsDrawFilter(PictureRenderer::DrawFilterFlags* flags) :
97 fFlags(flags) {}
98
reed@google.com971aca72012-11-26 20:26:54 +000099 virtual bool filter(SkPaint* paint, Type t) {
caryclark@google.coma3622372012-11-06 21:26:13 +0000100 paint->setFlags(paint->getFlags() & ~fFlags[t] & SkPaint::kAllFlags);
robertphillips@google.com49149312013-07-03 15:34:35 +0000101 if (PictureRenderer::kMaskFilter_DrawFilterFlag & fFlags[t]) {
caryclark@google.coma3622372012-11-06 21:26:13 +0000102 SkMaskFilter* maskFilter = paint->getMaskFilter();
robertphillips@google.com49149312013-07-03 15:34:35 +0000103 if (NULL != maskFilter) {
reed@google.com457d8a72012-12-18 18:20:44 +0000104 paint->setMaskFilter(NULL);
caryclark@google.coma3622372012-11-06 21:26:13 +0000105 }
106 }
107 if (PictureRenderer::kHinting_DrawFilterFlag & fFlags[t]) {
108 paint->setHinting(SkPaint::kNo_Hinting);
109 } else if (PictureRenderer::kSlightHinting_DrawFilterFlag & fFlags[t]) {
110 paint->setHinting(SkPaint::kSlight_Hinting);
111 }
reed@google.com971aca72012-11-26 20:26:54 +0000112 return true;
caryclark@google.coma3622372012-11-06 21:26:13 +0000113 }
114
115private:
116 PictureRenderer::DrawFilterFlags* fFlags;
117};
118
scroggo@google.com82ec0b02012-12-17 19:25:54 +0000119static void setUpFilter(SkCanvas* canvas, PictureRenderer::DrawFilterFlags* drawFilters) {
caryclark@google.coma3622372012-11-06 21:26:13 +0000120 if (drawFilters && !canvas->getDrawFilter()) {
121 canvas->setDrawFilter(SkNEW_ARGS(FlagsDrawFilter, (drawFilters)))->unref();
caryclark@google.come3e940c2012-11-07 16:42:17 +0000122 if (drawFilters[0] & PictureRenderer::kAAClip_DrawFilterFlag) {
123 canvas->setAllowSoftClip(false);
124 }
caryclark@google.coma3622372012-11-06 21:26:13 +0000125 }
caryclark@google.coma3622372012-11-06 21:26:13 +0000126}
127
keyar@chromium.orga474ce32012-08-20 15:03:57 +0000128SkCanvas* PictureRenderer::setupCanvas() {
scroggo@google.comc0d5e542012-12-13 21:40:48 +0000129 const int width = this->getViewWidth();
130 const int height = this->getViewHeight();
131 return this->setupCanvas(width, height);
keyar@chromium.orga474ce32012-08-20 15:03:57 +0000132}
133
134SkCanvas* PictureRenderer::setupCanvas(int width, int height) {
caryclark@google.coma3622372012-11-06 21:26:13 +0000135 SkCanvas* canvas;
keyar@chromium.org4ea96c52012-08-20 15:03:29 +0000136 switch(fDeviceType) {
137 case kBitmap_DeviceType: {
138 SkBitmap bitmap;
keyar@chromium.orga474ce32012-08-20 15:03:57 +0000139 sk_tools::setup_bitmap(&bitmap, width, height);
caryclark@google.coma3622372012-11-06 21:26:13 +0000140 canvas = SkNEW_ARGS(SkCanvas, (bitmap));
keyar@chromium.org4ea96c52012-08-20 15:03:29 +0000141 }
scroggo@google.com82ec0b02012-12-17 19:25:54 +0000142 break;
keyar@chromium.org4ea96c52012-08-20 15:03:29 +0000143#if SK_SUPPORT_GPU
scroggo@google.com0556ea02013-02-08 19:38:21 +0000144#if SK_ANGLE
145 case kAngle_DeviceType:
146 // fall through
147#endif
keyar@chromium.org4ea96c52012-08-20 15:03:29 +0000148 case kGPU_DeviceType: {
commit-bot@chromium.orgae403b92013-04-10 17:27:30 +0000149 SkAutoTUnref<GrSurface> target;
scroggo@google.com0556ea02013-02-08 19:38:21 +0000150 if (fGrContext) {
151 // create a render target to back the device
152 GrTextureDesc desc;
153 desc.fConfig = kSkia8888_GrPixelConfig;
154 desc.fFlags = kRenderTarget_GrTextureFlagBit;
155 desc.fWidth = width;
156 desc.fHeight = height;
jvanverth@google.comf6a90332013-05-02 12:39:37 +0000157 desc.fSampleCnt = fSampleCount;
commit-bot@chromium.orgae403b92013-04-10 17:27:30 +0000158 target.reset(fGrContext->createUncachedTexture(desc, NULL, 0));
scroggo@google.com0556ea02013-02-08 19:38:21 +0000159 }
commit-bot@chromium.orgae403b92013-04-10 17:27:30 +0000160 if (NULL == target.get()) {
scroggo@google.com0556ea02013-02-08 19:38:21 +0000161 SkASSERT(0);
162 return NULL;
163 }
164
commit-bot@chromium.orgae403b92013-04-10 17:27:30 +0000165 SkAutoTUnref<SkGpuDevice> device(SkGpuDevice::Create(target));
caryclark@google.coma3622372012-11-06 21:26:13 +0000166 canvas = SkNEW_ARGS(SkCanvas, (device.get()));
scroggo@google.com0556ea02013-02-08 19:38:21 +0000167 break;
keyar@chromium.org4ea96c52012-08-20 15:03:29 +0000168 }
169#endif
170 default:
171 SkASSERT(0);
scroggo@google.com82ec0b02012-12-17 19:25:54 +0000172 return NULL;
keyar@chromium.org4ea96c52012-08-20 15:03:29 +0000173 }
scroggo@google.com82ec0b02012-12-17 19:25:54 +0000174 setUpFilter(canvas, fDrawFilters);
175 this->scaleToScaleFactor(canvas);
176 return canvas;
177}
keyar@chromium.orga474ce32012-08-20 15:03:57 +0000178
scroggo@google.com82ec0b02012-12-17 19:25:54 +0000179void PictureRenderer::scaleToScaleFactor(SkCanvas* canvas) {
180 SkASSERT(canvas != NULL);
181 if (fScaleFactor != SK_Scalar1) {
182 canvas->scale(fScaleFactor, fScaleFactor);
183 }
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000184}
185
186void PictureRenderer::end() {
scroggo@google.com08085f82013-01-28 20:40:24 +0000187 this->resetState(true);
junov@chromium.org9313ca42012-11-02 18:11:49 +0000188 SkSafeUnref(fPicture);
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000189 fPicture = NULL;
190 fCanvas.reset(NULL);
191}
192
scroggo@google.comc0d5e542012-12-13 21:40:48 +0000193int PictureRenderer::getViewWidth() {
194 SkASSERT(fPicture != NULL);
robertphillips@google.com8ac811e2013-02-07 00:13:34 +0000195 int width = SkScalarCeilToInt(fPicture->width() * fScaleFactor);
scroggo@google.comc0d5e542012-12-13 21:40:48 +0000196 if (fViewport.width() > 0) {
197 width = SkMin32(width, fViewport.width());
198 }
199 return width;
200}
201
202int PictureRenderer::getViewHeight() {
203 SkASSERT(fPicture != NULL);
robertphillips@google.com8ac811e2013-02-07 00:13:34 +0000204 int height = SkScalarCeilToInt(fPicture->height() * fScaleFactor);
scroggo@google.comc0d5e542012-12-13 21:40:48 +0000205 if (fViewport.height() > 0) {
206 height = SkMin32(height, fViewport.height());
207 }
208 return height;
209}
210
junov@chromium.org9313ca42012-11-02 18:11:49 +0000211/** Converts fPicture to a picture that uses a BBoxHierarchy.
212 * PictureRenderer subclasses that are used to test picture playback
213 * should call this method during init.
214 */
215void PictureRenderer::buildBBoxHierarchy() {
216 SkASSERT(NULL != fPicture);
217 if (kNone_BBoxHierarchyType != fBBoxHierarchyType && NULL != fPicture) {
218 SkPicture* newPicture = this->createPicture();
219 SkCanvas* recorder = newPicture->beginRecording(fPicture->width(), fPicture->height(),
220 this->recordFlags());
221 fPicture->draw(recorder);
222 newPicture->endRecording();
223 fPicture->unref();
224 fPicture = newPicture;
225 }
226}
227
scroggo@google.com08085f82013-01-28 20:40:24 +0000228void PictureRenderer::resetState(bool callFinish) {
keyar@chromium.org28136b32012-08-20 15:04:15 +0000229#if SK_SUPPORT_GPU
robertphillips@google.com6177e692013-02-28 20:16:25 +0000230 SkGLContextHelper* glContext = this->getGLContext();
scroggo@google.com0556ea02013-02-08 19:38:21 +0000231 if (NULL == glContext) {
232 SkASSERT(kBitmap_DeviceType == fDeviceType);
233 return;
234 }
keyar@chromium.org28136b32012-08-20 15:04:15 +0000235
scroggo@google.com0556ea02013-02-08 19:38:21 +0000236 fGrContext->flush();
237 if (callFinish) {
238 SK_GL(*glContext, Finish());
keyar@chromium.org77a55222012-08-20 15:03:47 +0000239 }
keyar@chromium.orga40c20d2012-08-20 15:04:12 +0000240#endif
keyar@chromium.org77a55222012-08-20 15:03:47 +0000241}
242
junov@chromium.org9313ca42012-11-02 18:11:49 +0000243uint32_t PictureRenderer::recordFlags() {
junov@chromium.org100b1c52013-01-16 20:12:22 +0000244 return ((kNone_BBoxHierarchyType == fBBoxHierarchyType) ? 0 :
245 SkPicture::kOptimizeForClippedPlayback_RecordingFlag) |
246 SkPicture::kUsePathBoundsForClip_RecordingFlag;
junov@chromium.org9313ca42012-11-02 18:11:49 +0000247}
248
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000249/**
250 * Write the canvas to the specified path.
251 * @param canvas Must be non-null. Canvas to be written to a file.
252 * @param path Path for the file to be written. Should have no extension; write() will append
253 * an appropriate one. Passed in by value so it can be modified.
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +0000254 * @param jsonSummaryPtr If not null, add image results to this summary.
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000255 * @return bool True if the Canvas is written to a file.
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +0000256 *
257 * TODO(epoger): Right now, all canvases must pass through this function in order to be appended
258 * to the ImageResultsSummary. We need some way to add bitmaps to the ImageResultsSummary
259 * even if --writePath has not been specified (and thus this function is not called).
260 *
261 * One fix would be to pass in these path elements separately, and allow this function to be
262 * called even if --writePath was not specified...
263 * const char *outputDir // NULL if we don't want to write image files to disk
264 * const char *filename // name we use within JSON summary, and as the filename within outputDir
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000265 */
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +0000266static bool write(SkCanvas* canvas, const SkString* path, ImageResultsSummary *jsonSummaryPtr) {
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000267 SkASSERT(canvas != NULL);
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000268 if (NULL == canvas) {
keyar@chromium.org9299ede2012-08-21 19:05:08 +0000269 return false;
270 }
271
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +0000272 SkASSERT(path != NULL); // TODO(epoger): we want to remove this constraint, as noted above
273 SkString fullPathname(*path);
274 fullPathname.append(".png");
275
keyar@chromium.org9299ede2012-08-21 19:05:08 +0000276 SkBitmap bitmap;
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000277 SkISize size = canvas->getDeviceSize();
278 sk_tools::setup_bitmap(&bitmap, size.width(), size.height());
keyar@chromium.org9299ede2012-08-21 19:05:08 +0000279
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000280 canvas->readPixels(&bitmap, 0, 0);
keyar@chromium.org9299ede2012-08-21 19:05:08 +0000281 sk_tools::force_all_opaque(bitmap);
282
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +0000283 if (NULL != jsonSummaryPtr) {
284 // EPOGER: This is a hacky way of constructing the filename associated with the
285 // image checksum; we assume that outputDir is not NULL, and we remove outputDir
286 // from fullPathname.
287 //
288 // EPOGER: what about including the config type within hashFilename? That way,
289 // we could combine results of different config types without conflicting filenames.
290 SkString hashFilename;
291 sk_tools::get_basename(&hashFilename, fullPathname);
292 jsonSummaryPtr->add(hashFilename.c_str(), bitmap);
293 }
294
295 return SkImageEncoder::EncodeFile(fullPathname.c_str(), bitmap, SkImageEncoder::kPNG_Type, 100);
keyar@chromium.org9299ede2012-08-21 19:05:08 +0000296}
297
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000298/**
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +0000299 * If path is non NULL, append number to it, and call write() to write the
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000300 * provided canvas to a file. Returns true if path is NULL or if write() succeeds.
301 */
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +0000302static bool writeAppendNumber(SkCanvas* canvas, const SkString* path, int number,
303 ImageResultsSummary *jsonSummaryPtr) {
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000304 if (NULL == path) {
305 return true;
306 }
307 SkString pathWithNumber(*path);
308 pathWithNumber.appendf("%i", number);
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +0000309 return write(canvas, &pathWithNumber, jsonSummaryPtr);
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000310}
311
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000312///////////////////////////////////////////////////////////////////////////////////////////////
313
djsollen@google.comfd9720c2012-11-06 16:54:40 +0000314SkCanvas* RecordPictureRenderer::setupCanvas(int width, int height) {
315 // defer the canvas setup until the render step
316 return NULL;
317}
318
scroggo@google.com1b1bcc32013-05-21 20:31:23 +0000319static SkData* encode_bitmap_to_data(size_t* offset, const SkBitmap& bm) {
320 SkPixelRef* pr = bm.pixelRef();
321 if (pr != NULL) {
322 SkData* data = pr->refEncodedData();
323 if (data != NULL) {
324 *offset = bm.pixelRefOffset();
325 return data;
326 }
327 }
328 *offset = 0;
329 return SkImageEncoder::EncodeData(bm, SkImageEncoder::kPNG_Type, 100);
scroggo@google.coma9e3a362012-11-07 17:52:48 +0000330}
331
edisonn@google.com84f548c2012-12-18 22:24:03 +0000332bool RecordPictureRenderer::render(const SkString* path, SkBitmap** out) {
junov@chromium.org9313ca42012-11-02 18:11:49 +0000333 SkAutoTUnref<SkPicture> replayer(this->createPicture());
scroggo@google.com82ec0b02012-12-17 19:25:54 +0000334 SkCanvas* recorder = replayer->beginRecording(this->getViewWidth(), this->getViewHeight(),
junov@chromium.org9313ca42012-11-02 18:11:49 +0000335 this->recordFlags());
scroggo@google.com82ec0b02012-12-17 19:25:54 +0000336 this->scaleToScaleFactor(recorder);
scroggo@google.com9a412522012-09-07 15:21:18 +0000337 fPicture->draw(recorder);
junov@chromium.org9313ca42012-11-02 18:11:49 +0000338 replayer->endRecording();
scroggo@google.coma9e3a362012-11-07 17:52:48 +0000339 if (path != NULL) {
340 // Record the new picture as a new SKP with PNG encoded bitmaps.
341 SkString skpPath(*path);
342 // ".skp" was removed from 'path' before being passed in here.
343 skpPath.append(".skp");
344 SkFILEWStream stream(skpPath.c_str());
scroggo@google.com1b1bcc32013-05-21 20:31:23 +0000345 replayer->serialize(&stream, &encode_bitmap_to_data);
scroggo@google.coma9e3a362012-11-07 17:52:48 +0000346 return true;
347 }
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000348 return false;
scroggo@google.com9a412522012-09-07 15:21:18 +0000349}
350
scroggo@google.com0a049b82012-11-02 22:01:26 +0000351SkString RecordPictureRenderer::getConfigNameInternal() {
352 return SkString("record");
353}
354
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000355///////////////////////////////////////////////////////////////////////////////////////////////
356
edisonn@google.com84f548c2012-12-18 22:24:03 +0000357bool PipePictureRenderer::render(const SkString* path, SkBitmap** out) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000358 SkASSERT(fCanvas.get() != NULL);
359 SkASSERT(fPicture != NULL);
keyar@chromium.org78a35c52012-08-20 15:03:44 +0000360 if (NULL == fCanvas.get() || NULL == fPicture) {
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000361 return false;
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000362 }
363
364 PipeController pipeController(fCanvas.get());
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000365 SkGPipeWriter writer;
366 SkCanvas* pipeCanvas = writer.startRecording(&pipeController);
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000367 pipeCanvas->drawPicture(*fPicture);
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000368 writer.endRecording();
scroggo@google.com9a412522012-09-07 15:21:18 +0000369 fCanvas->flush();
borenet@google.com070d3542012-10-26 13:26:55 +0000370 if (NULL != path) {
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +0000371 return write(fCanvas, path, fJsonSummaryPtr);
borenet@google.com070d3542012-10-26 13:26:55 +0000372 }
edisonn@google.com84f548c2012-12-18 22:24:03 +0000373 if (NULL != out) {
374 *out = SkNEW(SkBitmap);
375 setup_bitmap(*out, fPicture->width(), fPicture->height());
376 fCanvas->readPixels(*out, 0, 0);
skia.committer@gmail.coma7d8e3e2012-12-19 02:01:38 +0000377 }
borenet@google.com070d3542012-10-26 13:26:55 +0000378 return true;
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000379}
380
scroggo@google.com0a049b82012-11-02 22:01:26 +0000381SkString PipePictureRenderer::getConfigNameInternal() {
382 return SkString("pipe");
383}
384
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000385///////////////////////////////////////////////////////////////////////////////////////////////
386
junov@chromium.org9313ca42012-11-02 18:11:49 +0000387void SimplePictureRenderer::init(SkPicture* picture) {
388 INHERITED::init(picture);
389 this->buildBBoxHierarchy();
390}
391
edisonn@google.com84f548c2012-12-18 22:24:03 +0000392bool SimplePictureRenderer::render(const SkString* path, SkBitmap** out) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000393 SkASSERT(fCanvas.get() != NULL);
394 SkASSERT(fPicture != NULL);
keyar@chromium.org78a35c52012-08-20 15:03:44 +0000395 if (NULL == fCanvas.get() || NULL == fPicture) {
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000396 return false;
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000397 }
398
399 fCanvas->drawPicture(*fPicture);
scroggo@google.com9a412522012-09-07 15:21:18 +0000400 fCanvas->flush();
borenet@google.com070d3542012-10-26 13:26:55 +0000401 if (NULL != path) {
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +0000402 return write(fCanvas, path, fJsonSummaryPtr);
borenet@google.com070d3542012-10-26 13:26:55 +0000403 }
skia.committer@gmail.coma7d8e3e2012-12-19 02:01:38 +0000404
edisonn@google.com84f548c2012-12-18 22:24:03 +0000405 if (NULL != out) {
406 *out = SkNEW(SkBitmap);
407 setup_bitmap(*out, fPicture->width(), fPicture->height());
408 fCanvas->readPixels(*out, 0, 0);
409 }
410
borenet@google.com070d3542012-10-26 13:26:55 +0000411 return true;
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000412}
413
scroggo@google.com0a049b82012-11-02 22:01:26 +0000414SkString SimplePictureRenderer::getConfigNameInternal() {
415 return SkString("simple");
416}
417
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000418///////////////////////////////////////////////////////////////////////////////////////////////
419
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000420TiledPictureRenderer::TiledPictureRenderer()
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000421 : fTileWidth(kDefaultTileWidth)
rileya@google.comb947b912012-08-29 17:35:07 +0000422 , fTileHeight(kDefaultTileHeight)
rileya@google.coma04dc022012-09-10 19:01:38 +0000423 , fTileWidthPercentage(0.0)
rileya@google.comb947b912012-08-29 17:35:07 +0000424 , fTileHeightPercentage(0.0)
scroggo@google.comcbcef702012-12-13 22:09:28 +0000425 , fTileMinPowerOf2Width(0)
426 , fCurrentTileOffset(-1)
427 , fTilesX(0)
428 , fTilesY(0) { }
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000429
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000430void TiledPictureRenderer::init(SkPicture* pict) {
431 SkASSERT(pict != NULL);
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000432 SkASSERT(0 == fTileRects.count());
433 if (NULL == pict || fTileRects.count() != 0) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000434 return;
435 }
436
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000437 // Do not call INHERITED::init(), which would create a (potentially large) canvas which is not
438 // used by bench_pictures.
439 fPicture = pict;
junov@chromium.org9313ca42012-11-02 18:11:49 +0000440 fPicture->ref();
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000441 this->buildBBoxHierarchy();
keyar@chromium.orgcc6e5ef2012-07-27 20:09:26 +0000442
443 if (fTileWidthPercentage > 0) {
robertphillips@google.com5d8d1862012-08-15 14:36:41 +0000444 fTileWidth = sk_float_ceil2int(float(fTileWidthPercentage * fPicture->width() / 100));
keyar@chromium.orgcc6e5ef2012-07-27 20:09:26 +0000445 }
446 if (fTileHeightPercentage > 0) {
robertphillips@google.com5d8d1862012-08-15 14:36:41 +0000447 fTileHeight = sk_float_ceil2int(float(fTileHeightPercentage * fPicture->height() / 100));
keyar@chromium.orgcc6e5ef2012-07-27 20:09:26 +0000448 }
449
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000450 if (fTileMinPowerOf2Width > 0) {
451 this->setupPowerOf2Tiles();
452 } else {
453 this->setupTiles();
454 }
scroggo@google.comcbcef702012-12-13 22:09:28 +0000455 fCanvas.reset(this->setupCanvas(fTileWidth, fTileHeight));
456 // Initialize to -1 so that the first call to nextTile will set this up to draw tile 0 on the
457 // first call to drawCurrentTile.
458 fCurrentTileOffset = -1;
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000459}
460
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000461void TiledPictureRenderer::end() {
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000462 fTileRects.reset();
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000463 this->INHERITED::end();
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000464}
465
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000466void TiledPictureRenderer::setupTiles() {
scroggo@google.comc0d5e542012-12-13 21:40:48 +0000467 // Only use enough tiles to cover the viewport
468 const int width = this->getViewWidth();
469 const int height = this->getViewHeight();
470
scroggo@google.comcbcef702012-12-13 22:09:28 +0000471 fTilesX = fTilesY = 0;
scroggo@google.comc0d5e542012-12-13 21:40:48 +0000472 for (int tile_y_start = 0; tile_y_start < height; tile_y_start += fTileHeight) {
scroggo@google.comcbcef702012-12-13 22:09:28 +0000473 fTilesY++;
scroggo@google.comc0d5e542012-12-13 21:40:48 +0000474 for (int tile_x_start = 0; tile_x_start < width; tile_x_start += fTileWidth) {
scroggo@google.comcbcef702012-12-13 22:09:28 +0000475 if (0 == tile_y_start) {
476 // Only count tiles in the X direction on the first pass.
477 fTilesX++;
478 }
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000479 *fTileRects.append() = SkRect::MakeXYWH(SkIntToScalar(tile_x_start),
480 SkIntToScalar(tile_y_start),
481 SkIntToScalar(fTileWidth),
482 SkIntToScalar(fTileHeight));
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000483 }
484 }
485}
486
scroggo@google.comcbcef702012-12-13 22:09:28 +0000487bool TiledPictureRenderer::tileDimensions(int &x, int &y) {
488 if (fTileRects.count() == 0 || NULL == fPicture) {
489 return false;
490 }
491 x = fTilesX;
492 y = fTilesY;
493 return true;
494}
495
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000496// The goal of the powers of two tiles is to minimize the amount of wasted tile
497// space in the width-wise direction and then minimize the number of tiles. The
498// constraints are that every tile must have a pixel width that is a power of
499// two and also be of some minimal width (that is also a power of two).
500//
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000501// This is solved by first taking our picture size and rounding it up to the
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000502// multiple of the minimal width. The binary representation of this rounded
503// value gives us the tiles we need: a bit of value one means we need a tile of
504// that size.
505void TiledPictureRenderer::setupPowerOf2Tiles() {
scroggo@google.comc0d5e542012-12-13 21:40:48 +0000506 // Only use enough tiles to cover the viewport
507 const int width = this->getViewWidth();
508 const int height = this->getViewHeight();
509
510 int rounded_value = width;
511 if (width % fTileMinPowerOf2Width != 0) {
512 rounded_value = width - (width % fTileMinPowerOf2Width) + fTileMinPowerOf2Width;
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000513 }
514
scroggo@google.comc0d5e542012-12-13 21:40:48 +0000515 int num_bits = SkScalarCeilToInt(SkScalarLog2(SkIntToScalar(width)));
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000516 int largest_possible_tile_size = 1 << num_bits;
517
scroggo@google.comcbcef702012-12-13 22:09:28 +0000518 fTilesX = fTilesY = 0;
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000519 // The tile height is constant for a particular picture.
scroggo@google.comc0d5e542012-12-13 21:40:48 +0000520 for (int tile_y_start = 0; tile_y_start < height; tile_y_start += fTileHeight) {
scroggo@google.comcbcef702012-12-13 22:09:28 +0000521 fTilesY++;
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000522 int tile_x_start = 0;
523 int current_width = largest_possible_tile_size;
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000524 // Set fTileWidth to be the width of the widest tile, so that each canvas is large enough
525 // to draw each tile.
526 fTileWidth = current_width;
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000527
528 while (current_width >= fTileMinPowerOf2Width) {
529 // It is very important this is a bitwise AND.
530 if (current_width & rounded_value) {
scroggo@google.comcbcef702012-12-13 22:09:28 +0000531 if (0 == tile_y_start) {
532 // Only count tiles in the X direction on the first pass.
533 fTilesX++;
534 }
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000535 *fTileRects.append() = SkRect::MakeXYWH(SkIntToScalar(tile_x_start),
536 SkIntToScalar(tile_y_start),
537 SkIntToScalar(current_width),
538 SkIntToScalar(fTileHeight));
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000539 tile_x_start += current_width;
540 }
541
542 current_width >>= 1;
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000543 }
544 }
545}
546
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000547/**
548 * Draw the specified playback to the canvas translated to rectangle provided, so that this mini
549 * canvas represents the rectangle's portion of the overall picture.
550 * Saves and restores so that the initial clip and matrix return to their state before this function
551 * is called.
552 */
553template<class T>
554static void DrawTileToCanvas(SkCanvas* canvas, const SkRect& tileRect, T* playback) {
555 int saveCount = canvas->save();
scroggo@google.com82ec0b02012-12-17 19:25:54 +0000556 // Translate so that we draw the correct portion of the picture.
557 // Perform a postTranslate so that the scaleFactor does not interfere with the positioning.
558 SkMatrix mat(canvas->getTotalMatrix());
559 mat.postTranslate(-tileRect.fLeft, -tileRect.fTop);
560 canvas->setMatrix(mat);
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000561 playback->draw(canvas);
562 canvas->restoreToCount(saveCount);
563 canvas->flush();
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000564}
565
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000566///////////////////////////////////////////////////////////////////////////////////////////////
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000567
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +0000568/**
569 * Copies the entirety of the src bitmap (typically a tile) into a portion of the dst bitmap.
570 * If the src bitmap is too large to fit within the dst bitmap after the x and y
571 * offsets have been applied, any excess will be ignored (so only the top-left portion of the
572 * src bitmap will be copied).
573 *
574 * @param src source bitmap
575 * @param dst destination bitmap
576 * @param xOffset x-offset within destination bitmap
577 * @param yOffset y-offset within destination bitmap
578 */
579static void bitmapCopyAtOffset(const SkBitmap& src, SkBitmap* dst,
580 int xOffset, int yOffset) {
581 for (int y = 0; y <src.height() && y + yOffset < dst->height() ; y++) {
582 for (int x = 0; x < src.width() && x + xOffset < dst->width() ; x++) {
583 *dst->getAddr32(xOffset + x, yOffset + y) = *src.getAddr32(x, y);
edisonn@google.com84f548c2012-12-18 22:24:03 +0000584 }
585 }
586}
587
scroggo@google.comcbcef702012-12-13 22:09:28 +0000588bool TiledPictureRenderer::nextTile(int &i, int &j) {
589 if (++fCurrentTileOffset < fTileRects.count()) {
590 i = fCurrentTileOffset % fTilesX;
591 j = fCurrentTileOffset / fTilesX;
592 return true;
593 }
594 return false;
595}
596
597void TiledPictureRenderer::drawCurrentTile() {
598 SkASSERT(fCurrentTileOffset >= 0 && fCurrentTileOffset < fTileRects.count());
599 DrawTileToCanvas(fCanvas, fTileRects[fCurrentTileOffset], fPicture);
600}
601
edisonn@google.com84f548c2012-12-18 22:24:03 +0000602bool TiledPictureRenderer::render(const SkString* path, SkBitmap** out) {
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000603 SkASSERT(fPicture != NULL);
604 if (NULL == fPicture) {
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000605 return false;
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000606 }
607
edisonn@google.com84f548c2012-12-18 22:24:03 +0000608 SkBitmap bitmap;
609 if (out){
610 *out = SkNEW(SkBitmap);
611 setup_bitmap(*out, fPicture->width(), fPicture->height());
612 setup_bitmap(&bitmap, fTileWidth, fTileHeight);
613 }
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000614 bool success = true;
615 for (int i = 0; i < fTileRects.count(); ++i) {
scroggo@google.comcbcef702012-12-13 22:09:28 +0000616 DrawTileToCanvas(fCanvas, fTileRects[i], fPicture);
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000617 if (NULL != path) {
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +0000618 success &= writeAppendNumber(fCanvas, path, i, fJsonSummaryPtr);
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000619 }
edisonn@google.com84f548c2012-12-18 22:24:03 +0000620 if (NULL != out) {
621 if (fCanvas->readPixels(&bitmap, 0, 0)) {
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +0000622 // Add this tile to the entire bitmap.
623 bitmapCopyAtOffset(bitmap, *out, SkScalarFloorToInt(fTileRects[i].left()),
624 SkScalarFloorToInt(fTileRects[i].top()));
edisonn@google.com84f548c2012-12-18 22:24:03 +0000625 } else {
626 success = false;
627 }
628 }
keyar@chromium.org163b5672012-08-01 17:53:29 +0000629 }
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000630 return success;
keyar@chromium.org163b5672012-08-01 17:53:29 +0000631}
632
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000633SkCanvas* TiledPictureRenderer::setupCanvas(int width, int height) {
634 SkCanvas* canvas = this->INHERITED::setupCanvas(width, height);
635 SkASSERT(fPicture != NULL);
scroggo@google.com82ec0b02012-12-17 19:25:54 +0000636 // Clip the tile to an area that is completely inside both the SkPicture and the viewport. This
637 // is mostly important for tiles on the right and bottom edges as they may go over this area and
638 // the picture may have some commands that draw outside of this area and so should not actually
639 // be written.
640 // Uses a clipRegion so that it will be unaffected by the scale factor, which may have been set
641 // by INHERITED::setupCanvas.
642 SkRegion clipRegion;
643 clipRegion.setRect(0, 0, this->getViewWidth(), this->getViewHeight());
644 canvas->clipRegion(clipRegion);
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000645 return canvas;
646}
scroggo@google.com0a049b82012-11-02 22:01:26 +0000647
648SkString TiledPictureRenderer::getConfigNameInternal() {
649 SkString name;
650 if (fTileMinPowerOf2Width > 0) {
651 name.append("pow2tile_");
652 name.appendf("%i", fTileMinPowerOf2Width);
653 } else {
654 name.append("tile_");
655 if (fTileWidthPercentage > 0) {
656 name.appendf("%.f%%", fTileWidthPercentage);
657 } else {
658 name.appendf("%i", fTileWidth);
659 }
660 }
661 name.append("x");
662 if (fTileHeightPercentage > 0) {
663 name.appendf("%.f%%", fTileHeightPercentage);
664 } else {
665 name.appendf("%i", fTileHeight);
666 }
667 return name;
668}
669
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000670///////////////////////////////////////////////////////////////////////////////////////////////
671
672// Holds all of the information needed to draw a set of tiles.
673class CloneData : public SkRunnable {
674
675public:
676 CloneData(SkPicture* clone, SkCanvas* canvas, SkTDArray<SkRect>& rects, int start, int end,
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +0000677 SkRunnable* done, ImageResultsSummary* jsonSummaryPtr)
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000678 : fClone(clone)
679 , fCanvas(canvas)
680 , fPath(NULL)
681 , fRects(rects)
682 , fStart(start)
683 , fEnd(end)
684 , fSuccess(NULL)
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +0000685 , fDone(done)
686 , fJsonSummaryPtr(jsonSummaryPtr) {
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000687 SkASSERT(fDone != NULL);
688 }
689
690 virtual void run() SK_OVERRIDE {
691 SkGraphics::SetTLSFontCacheLimit(1024 * 1024);
edisonn@google.com84f548c2012-12-18 22:24:03 +0000692
693 SkBitmap bitmap;
694 if (fBitmap != NULL) {
695 // All tiles are the same size.
jvanverth@google.com9c4e5ac2013-01-07 18:41:28 +0000696 setup_bitmap(&bitmap, SkScalarFloorToInt(fRects[0].width()), SkScalarFloorToInt(fRects[0].height()));
edisonn@google.com84f548c2012-12-18 22:24:03 +0000697 }
skia.committer@gmail.coma7d8e3e2012-12-19 02:01:38 +0000698
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000699 for (int i = fStart; i < fEnd; i++) {
700 DrawTileToCanvas(fCanvas, fRects[i], fClone);
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +0000701 if ((fPath != NULL) && !writeAppendNumber(fCanvas, fPath, i, fJsonSummaryPtr)
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000702 && fSuccess != NULL) {
703 *fSuccess = false;
704 // If one tile fails to write to a file, do not continue drawing the rest.
705 break;
706 }
edisonn@google.com84f548c2012-12-18 22:24:03 +0000707 if (fBitmap != NULL) {
708 if (fCanvas->readPixels(&bitmap, 0, 0)) {
709 SkAutoLockPixels alp(*fBitmap);
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +0000710 bitmapCopyAtOffset(bitmap, fBitmap, SkScalarFloorToInt(fRects[i].left()),
711 SkScalarFloorToInt(fRects[i].top()));
edisonn@google.com84f548c2012-12-18 22:24:03 +0000712 } else {
713 *fSuccess = false;
714 // If one tile fails to read pixels, do not continue drawing the rest.
715 break;
716 }
717 }
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000718 }
719 fDone->run();
720 }
721
722 void setPathAndSuccess(const SkString* path, bool* success) {
723 fPath = path;
724 fSuccess = success;
725 }
726
edisonn@google.com84f548c2012-12-18 22:24:03 +0000727 void setBitmap(SkBitmap* bitmap) {
728 fBitmap = bitmap;
729 }
730
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000731private:
732 // All pointers unowned.
733 SkPicture* fClone; // Picture to draw from. Each CloneData has a unique one which
734 // is threadsafe.
735 SkCanvas* fCanvas; // Canvas to draw to. Reused for each tile.
736 const SkString* fPath; // If non-null, path to write the result to as a PNG.
737 SkTDArray<SkRect>& fRects; // All tiles of the picture.
738 const int fStart; // Range of tiles drawn by this thread.
739 const int fEnd;
740 bool* fSuccess; // Only meaningful if path is non-null. Shared by all threads,
741 // and only set to false upon failure to write to a PNG.
742 SkRunnable* fDone;
edisonn@google.com84f548c2012-12-18 22:24:03 +0000743 SkBitmap* fBitmap;
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +0000744 ImageResultsSummary* fJsonSummaryPtr;
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000745};
746
747MultiCorePictureRenderer::MultiCorePictureRenderer(int threadCount)
748: fNumThreads(threadCount)
749, fThreadPool(threadCount)
750, fCountdown(threadCount) {
751 // Only need to create fNumThreads - 1 clones, since one thread will use the base
752 // picture.
753 fPictureClones = SkNEW_ARRAY(SkPicture, fNumThreads - 1);
754 fCloneData = SkNEW_ARRAY(CloneData*, fNumThreads);
755}
756
757void MultiCorePictureRenderer::init(SkPicture *pict) {
758 // Set fPicture and the tiles.
759 this->INHERITED::init(pict);
760 for (int i = 0; i < fNumThreads; ++i) {
761 *fCanvasPool.append() = this->setupCanvas(this->getTileWidth(), this->getTileHeight());
762 }
763 // Only need to create fNumThreads - 1 clones, since one thread will use the base picture.
764 fPicture->clone(fPictureClones, fNumThreads - 1);
765 // Populate each thread with the appropriate data.
766 // Group the tiles into nearly equal size chunks, rounding up so we're sure to cover them all.
767 const int chunkSize = (fTileRects.count() + fNumThreads - 1) / fNumThreads;
768
769 for (int i = 0; i < fNumThreads; i++) {
770 SkPicture* pic;
771 if (i == fNumThreads-1) {
772 // The last set will use the original SkPicture.
773 pic = fPicture;
774 } else {
775 pic = &fPictureClones[i];
776 }
777 const int start = i * chunkSize;
778 const int end = SkMin32(start + chunkSize, fTileRects.count());
779 fCloneData[i] = SkNEW_ARGS(CloneData,
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +0000780 (pic, fCanvasPool[i], fTileRects, start, end, &fCountdown,
781 fJsonSummaryPtr));
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000782 }
783}
784
edisonn@google.com84f548c2012-12-18 22:24:03 +0000785bool MultiCorePictureRenderer::render(const SkString *path, SkBitmap** out) {
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000786 bool success = true;
787 if (path != NULL) {
788 for (int i = 0; i < fNumThreads-1; i++) {
789 fCloneData[i]->setPathAndSuccess(path, &success);
790 }
791 }
792
edisonn@google.com84f548c2012-12-18 22:24:03 +0000793 if (NULL != out) {
794 *out = SkNEW(SkBitmap);
795 setup_bitmap(*out, fPicture->width(), fPicture->height());
796 for (int i = 0; i < fNumThreads; i++) {
797 fCloneData[i]->setBitmap(*out);
798 }
799 } else {
800 for (int i = 0; i < fNumThreads; i++) {
801 fCloneData[i]->setBitmap(NULL);
802 }
803 }
804
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000805 fCountdown.reset(fNumThreads);
806 for (int i = 0; i < fNumThreads; i++) {
807 fThreadPool.add(fCloneData[i]);
808 }
809 fCountdown.wait();
810
811 return success;
812}
813
814void MultiCorePictureRenderer::end() {
815 for (int i = 0; i < fNumThreads - 1; i++) {
816 SkDELETE(fCloneData[i]);
817 fCloneData[i] = NULL;
818 }
819
820 fCanvasPool.unrefAll();
821
822 this->INHERITED::end();
823}
824
825MultiCorePictureRenderer::~MultiCorePictureRenderer() {
826 // Each individual CloneData was deleted in end.
827 SkDELETE_ARRAY(fCloneData);
828 SkDELETE_ARRAY(fPictureClones);
829}
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000830
scroggo@google.com0a049b82012-11-02 22:01:26 +0000831SkString MultiCorePictureRenderer::getConfigNameInternal() {
832 SkString name = this->INHERITED::getConfigNameInternal();
833 name.appendf("_multi_%i_threads", fNumThreads);
834 return name;
835}
836
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000837///////////////////////////////////////////////////////////////////////////////////////////////
scroggo@google.com9a412522012-09-07 15:21:18 +0000838
839void PlaybackCreationRenderer::setup() {
junov@chromium.org9313ca42012-11-02 18:11:49 +0000840 fReplayer.reset(this->createPicture());
scroggo@google.com82ec0b02012-12-17 19:25:54 +0000841 SkCanvas* recorder = fReplayer->beginRecording(this->getViewWidth(), this->getViewHeight(),
junov@chromium.org9313ca42012-11-02 18:11:49 +0000842 this->recordFlags());
scroggo@google.com82ec0b02012-12-17 19:25:54 +0000843 this->scaleToScaleFactor(recorder);
scroggo@google.com9a412522012-09-07 15:21:18 +0000844 fPicture->draw(recorder);
845}
846
edisonn@google.com84f548c2012-12-18 22:24:03 +0000847bool PlaybackCreationRenderer::render(const SkString*, SkBitmap** out) {
junov@chromium.org9313ca42012-11-02 18:11:49 +0000848 fReplayer->endRecording();
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000849 // Since this class does not actually render, return false.
850 return false;
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000851}
852
scroggo@google.com0a049b82012-11-02 22:01:26 +0000853SkString PlaybackCreationRenderer::getConfigNameInternal() {
854 return SkString("playback_creation");
855}
856
junov@chromium.org9313ca42012-11-02 18:11:49 +0000857///////////////////////////////////////////////////////////////////////////////////////////////
858// SkPicture variants for each BBoxHierarchy type
859
860class RTreePicture : public SkPicture {
861public:
862 virtual SkBBoxHierarchy* createBBoxHierarchy() const SK_OVERRIDE{
863 static const int kRTreeMinChildren = 6;
864 static const int kRTreeMaxChildren = 11;
865 SkScalar aspectRatio = SkScalarDiv(SkIntToScalar(fWidth),
866 SkIntToScalar(fHeight));
sglez@google.com8c902122013-08-30 17:27:47 +0000867 bool sortDraws = false;
junov@chromium.org9313ca42012-11-02 18:11:49 +0000868 return SkRTree::Create(kRTreeMinChildren, kRTreeMaxChildren,
sglez@google.com8c902122013-08-30 17:27:47 +0000869 aspectRatio, sortDraws);
junov@chromium.org9313ca42012-11-02 18:11:49 +0000870 }
871};
872
873SkPicture* PictureRenderer::createPicture() {
874 switch (fBBoxHierarchyType) {
875 case kNone_BBoxHierarchyType:
876 return SkNEW(SkPicture);
877 case kRTree_BBoxHierarchyType:
878 return SkNEW(RTreePicture);
junov@chromium.org7b537062012-11-06 18:58:43 +0000879 case kTileGrid_BBoxHierarchyType:
junov@chromium.org29b19e52013-02-27 18:35:16 +0000880 return SkNEW_ARGS(SkTileGridPicture, (fPicture->width(),
881 fPicture->height(), fGridInfo));
junov@chromium.org9313ca42012-11-02 18:11:49 +0000882 }
883 SkASSERT(0); // invalid bbhType
884 return NULL;
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000885}
junov@chromium.org9313ca42012-11-02 18:11:49 +0000886
reed@google.comfe7b1ed2012-11-29 21:00:39 +0000887///////////////////////////////////////////////////////////////////////////////
888
889class GatherRenderer : public PictureRenderer {
890public:
edisonn@google.com84f548c2012-12-18 22:24:03 +0000891 virtual bool render(const SkString* path, SkBitmap** out = NULL)
892 SK_OVERRIDE {
reed@google.comfe7b1ed2012-11-29 21:00:39 +0000893 SkRect bounds = SkRect::MakeWH(SkIntToScalar(fPicture->width()),
894 SkIntToScalar(fPicture->height()));
895 SkData* data = SkPictureUtils::GatherPixelRefs(fPicture, bounds);
896 SkSafeUnref(data);
skia.committer@gmail.comc7b4be72012-12-11 02:01:20 +0000897
reed@google.comfe7b1ed2012-11-29 21:00:39 +0000898 return NULL == path; // we don't have anything to write
899 }
skia.committer@gmail.comc7b4be72012-12-11 02:01:20 +0000900
reed@google.comfe7b1ed2012-11-29 21:00:39 +0000901private:
902 virtual SkString getConfigNameInternal() SK_OVERRIDE {
903 return SkString("gather_pixelrefs");
904 }
905};
906
907PictureRenderer* CreateGatherPixelRefsRenderer() {
908 return SkNEW(GatherRenderer);
909}
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +0000910
reed@google.com5a34fd32012-12-10 16:05:09 +0000911///////////////////////////////////////////////////////////////////////////////
912
913class PictureCloneRenderer : public PictureRenderer {
914public:
edisonn@google.com84f548c2012-12-18 22:24:03 +0000915 virtual bool render(const SkString* path, SkBitmap** out = NULL)
916 SK_OVERRIDE {
reed@google.com5a34fd32012-12-10 16:05:09 +0000917 for (int i = 0; i < 100; ++i) {
918 SkPicture* clone = fPicture->clone();
919 SkSafeUnref(clone);
920 }
skia.committer@gmail.comc7b4be72012-12-11 02:01:20 +0000921
reed@google.com5a34fd32012-12-10 16:05:09 +0000922 return NULL == path; // we don't have anything to write
923 }
skia.committer@gmail.comc7b4be72012-12-11 02:01:20 +0000924
reed@google.com5a34fd32012-12-10 16:05:09 +0000925private:
926 virtual SkString getConfigNameInternal() SK_OVERRIDE {
927 return SkString("picture_clone");
928 }
929};
930
931PictureRenderer* CreatePictureCloneRenderer() {
932 return SkNEW(PictureCloneRenderer);
933}
934
junov@chromium.org9313ca42012-11-02 18:11:49 +0000935} // namespace sk_tools