blob: aae10067c4411157fbaaf344cfdb2ac0e146b52d [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
reed@google.come15b2f52013-12-18 04:59:26 +000037static inline SkScalar scalar_log2(SkScalar x) {
38 static const SkScalar log2_conversion_factor = SkScalarDiv(1, SkScalarLog(2));
39
40 return SkScalarLog(x) * log2_conversion_factor;
41}
42
keyar@chromium.org451bb9f2012-07-26 17:27:57 +000043namespace sk_tools {
44
45enum {
46 kDefaultTileWidth = 256,
47 kDefaultTileHeight = 256
48};
49
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +000050/* TODO(epoger): These constants are already maintained in 2 other places:
51 * gm/gm_json.py and gm/gm_expectations.cpp. We shouldn't add yet a third place.
52 * Figure out a way to share the definitions instead.
53 */
54const static char kJsonKey_ActualResults[] = "actual-results";
55const static char kJsonKey_ActualResults_Failed[] = "failed";
56const static char kJsonKey_ActualResults_FailureIgnored[]= "failure-ignored";
57const static char kJsonKey_ActualResults_NoComparison[] = "no-comparison";
58const static char kJsonKey_ActualResults_Succeeded[] = "succeeded";
59const static char kJsonKey_ExpectedResults[] = "expected-results";
60const static char kJsonKey_ExpectedResults_AllowedDigests[] = "allowed-digests";
61const static char kJsonKey_ExpectedResults_IgnoreFailure[] = "ignore-failure";
62const static char kJsonKey_Hashtype_Bitmap_64bitMD5[] = "bitmap-64bitMD5";
63
64void ImageResultsSummary::add(const char *testName, const SkBitmap& bitmap) {
65 uint64_t hash;
66 SkAssertResult(SkBitmapHasher::ComputeDigest(bitmap, &hash));
67 Json::Value jsonTypeValuePair;
68 jsonTypeValuePair.append(Json::Value(kJsonKey_Hashtype_Bitmap_64bitMD5));
69 jsonTypeValuePair.append(Json::UInt64(hash));
70 fActualResultsNoComparison[testName] = jsonTypeValuePair;
71}
72
73void ImageResultsSummary::writeToFile(const char *filename) {
74 Json::Value actualResults;
75 actualResults[kJsonKey_ActualResults_NoComparison] = fActualResultsNoComparison;
76 Json::Value root;
77 root[kJsonKey_ActualResults] = actualResults;
78 std::string jsonStdString = root.toStyledString();
79 SkFILEWStream stream(filename);
80 stream.write(jsonStdString.c_str(), jsonStdString.length());
81}
82
keyar@chromium.org9d696c02012-08-07 17:11:33 +000083void PictureRenderer::init(SkPicture* pict) {
keyar@chromium.org78a35c52012-08-20 15:03:44 +000084 SkASSERT(NULL == fPicture);
85 SkASSERT(NULL == fCanvas.get());
86 if (fPicture != NULL || NULL != fCanvas.get()) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +000087 return;
88 }
89
90 SkASSERT(pict != NULL);
keyar@chromium.org78a35c52012-08-20 15:03:44 +000091 if (NULL == pict) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +000092 return;
93 }
94
95 fPicture = pict;
junov@chromium.org9313ca42012-11-02 18:11:49 +000096 fPicture->ref();
keyar@chromium.orga474ce32012-08-20 15:03:57 +000097 fCanvas.reset(this->setupCanvas());
98}
99
caryclark@google.coma3622372012-11-06 21:26:13 +0000100class FlagsDrawFilter : public SkDrawFilter {
101public:
102 FlagsDrawFilter(PictureRenderer::DrawFilterFlags* flags) :
103 fFlags(flags) {}
104
reed@google.com971aca72012-11-26 20:26:54 +0000105 virtual bool filter(SkPaint* paint, Type t) {
caryclark@google.coma3622372012-11-06 21:26:13 +0000106 paint->setFlags(paint->getFlags() & ~fFlags[t] & SkPaint::kAllFlags);
robertphillips@google.com49149312013-07-03 15:34:35 +0000107 if (PictureRenderer::kMaskFilter_DrawFilterFlag & fFlags[t]) {
caryclark@google.coma3622372012-11-06 21:26:13 +0000108 SkMaskFilter* maskFilter = paint->getMaskFilter();
robertphillips@google.com49149312013-07-03 15:34:35 +0000109 if (NULL != maskFilter) {
reed@google.com457d8a72012-12-18 18:20:44 +0000110 paint->setMaskFilter(NULL);
caryclark@google.coma3622372012-11-06 21:26:13 +0000111 }
112 }
113 if (PictureRenderer::kHinting_DrawFilterFlag & fFlags[t]) {
114 paint->setHinting(SkPaint::kNo_Hinting);
115 } else if (PictureRenderer::kSlightHinting_DrawFilterFlag & fFlags[t]) {
116 paint->setHinting(SkPaint::kSlight_Hinting);
117 }
reed@google.com971aca72012-11-26 20:26:54 +0000118 return true;
caryclark@google.coma3622372012-11-06 21:26:13 +0000119 }
120
121private:
122 PictureRenderer::DrawFilterFlags* fFlags;
123};
124
scroggo@google.com82ec0b02012-12-17 19:25:54 +0000125static void setUpFilter(SkCanvas* canvas, PictureRenderer::DrawFilterFlags* drawFilters) {
caryclark@google.coma3622372012-11-06 21:26:13 +0000126 if (drawFilters && !canvas->getDrawFilter()) {
127 canvas->setDrawFilter(SkNEW_ARGS(FlagsDrawFilter, (drawFilters)))->unref();
caryclark@google.come3e940c2012-11-07 16:42:17 +0000128 if (drawFilters[0] & PictureRenderer::kAAClip_DrawFilterFlag) {
129 canvas->setAllowSoftClip(false);
130 }
caryclark@google.coma3622372012-11-06 21:26:13 +0000131 }
caryclark@google.coma3622372012-11-06 21:26:13 +0000132}
133
keyar@chromium.orga474ce32012-08-20 15:03:57 +0000134SkCanvas* PictureRenderer::setupCanvas() {
scroggo@google.comc0d5e542012-12-13 21:40:48 +0000135 const int width = this->getViewWidth();
136 const int height = this->getViewHeight();
137 return this->setupCanvas(width, height);
keyar@chromium.orga474ce32012-08-20 15:03:57 +0000138}
139
140SkCanvas* PictureRenderer::setupCanvas(int width, int height) {
caryclark@google.coma3622372012-11-06 21:26:13 +0000141 SkCanvas* canvas;
keyar@chromium.org4ea96c52012-08-20 15:03:29 +0000142 switch(fDeviceType) {
143 case kBitmap_DeviceType: {
144 SkBitmap bitmap;
keyar@chromium.orga474ce32012-08-20 15:03:57 +0000145 sk_tools::setup_bitmap(&bitmap, width, height);
caryclark@google.coma3622372012-11-06 21:26:13 +0000146 canvas = SkNEW_ARGS(SkCanvas, (bitmap));
keyar@chromium.org4ea96c52012-08-20 15:03:29 +0000147 }
scroggo@google.com82ec0b02012-12-17 19:25:54 +0000148 break;
keyar@chromium.org4ea96c52012-08-20 15:03:29 +0000149#if SK_SUPPORT_GPU
scroggo@google.com0556ea02013-02-08 19:38:21 +0000150#if SK_ANGLE
151 case kAngle_DeviceType:
152 // fall through
153#endif
keyar@chromium.org4ea96c52012-08-20 15:03:29 +0000154 case kGPU_DeviceType: {
commit-bot@chromium.orgae403b92013-04-10 17:27:30 +0000155 SkAutoTUnref<GrSurface> target;
scroggo@google.com0556ea02013-02-08 19:38:21 +0000156 if (fGrContext) {
157 // create a render target to back the device
158 GrTextureDesc desc;
159 desc.fConfig = kSkia8888_GrPixelConfig;
160 desc.fFlags = kRenderTarget_GrTextureFlagBit;
161 desc.fWidth = width;
162 desc.fHeight = height;
jvanverth@google.comf6a90332013-05-02 12:39:37 +0000163 desc.fSampleCnt = fSampleCount;
commit-bot@chromium.orgae403b92013-04-10 17:27:30 +0000164 target.reset(fGrContext->createUncachedTexture(desc, NULL, 0));
scroggo@google.com0556ea02013-02-08 19:38:21 +0000165 }
commit-bot@chromium.orgae403b92013-04-10 17:27:30 +0000166 if (NULL == target.get()) {
scroggo@google.com0556ea02013-02-08 19:38:21 +0000167 SkASSERT(0);
168 return NULL;
169 }
170
commit-bot@chromium.orgae403b92013-04-10 17:27:30 +0000171 SkAutoTUnref<SkGpuDevice> device(SkGpuDevice::Create(target));
caryclark@google.coma3622372012-11-06 21:26:13 +0000172 canvas = SkNEW_ARGS(SkCanvas, (device.get()));
scroggo@google.com0556ea02013-02-08 19:38:21 +0000173 break;
keyar@chromium.org4ea96c52012-08-20 15:03:29 +0000174 }
175#endif
176 default:
177 SkASSERT(0);
scroggo@google.com82ec0b02012-12-17 19:25:54 +0000178 return NULL;
keyar@chromium.org4ea96c52012-08-20 15:03:29 +0000179 }
scroggo@google.com82ec0b02012-12-17 19:25:54 +0000180 setUpFilter(canvas, fDrawFilters);
181 this->scaleToScaleFactor(canvas);
182 return canvas;
183}
keyar@chromium.orga474ce32012-08-20 15:03:57 +0000184
scroggo@google.com82ec0b02012-12-17 19:25:54 +0000185void PictureRenderer::scaleToScaleFactor(SkCanvas* canvas) {
186 SkASSERT(canvas != NULL);
187 if (fScaleFactor != SK_Scalar1) {
188 canvas->scale(fScaleFactor, fScaleFactor);
189 }
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000190}
191
192void PictureRenderer::end() {
scroggo@google.com08085f82013-01-28 20:40:24 +0000193 this->resetState(true);
junov@chromium.org9313ca42012-11-02 18:11:49 +0000194 SkSafeUnref(fPicture);
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000195 fPicture = NULL;
196 fCanvas.reset(NULL);
197}
198
scroggo@google.comc0d5e542012-12-13 21:40:48 +0000199int PictureRenderer::getViewWidth() {
200 SkASSERT(fPicture != NULL);
robertphillips@google.com8ac811e2013-02-07 00:13:34 +0000201 int width = SkScalarCeilToInt(fPicture->width() * fScaleFactor);
scroggo@google.comc0d5e542012-12-13 21:40:48 +0000202 if (fViewport.width() > 0) {
203 width = SkMin32(width, fViewport.width());
204 }
205 return width;
206}
207
208int PictureRenderer::getViewHeight() {
209 SkASSERT(fPicture != NULL);
robertphillips@google.com8ac811e2013-02-07 00:13:34 +0000210 int height = SkScalarCeilToInt(fPicture->height() * fScaleFactor);
scroggo@google.comc0d5e542012-12-13 21:40:48 +0000211 if (fViewport.height() > 0) {
212 height = SkMin32(height, fViewport.height());
213 }
214 return height;
215}
216
junov@chromium.org9313ca42012-11-02 18:11:49 +0000217/** Converts fPicture to a picture that uses a BBoxHierarchy.
218 * PictureRenderer subclasses that are used to test picture playback
219 * should call this method during init.
220 */
221void PictureRenderer::buildBBoxHierarchy() {
222 SkASSERT(NULL != fPicture);
223 if (kNone_BBoxHierarchyType != fBBoxHierarchyType && NULL != fPicture) {
224 SkPicture* newPicture = this->createPicture();
225 SkCanvas* recorder = newPicture->beginRecording(fPicture->width(), fPicture->height(),
226 this->recordFlags());
227 fPicture->draw(recorder);
228 newPicture->endRecording();
229 fPicture->unref();
230 fPicture = newPicture;
231 }
232}
233
scroggo@google.com08085f82013-01-28 20:40:24 +0000234void PictureRenderer::resetState(bool callFinish) {
keyar@chromium.org28136b32012-08-20 15:04:15 +0000235#if SK_SUPPORT_GPU
robertphillips@google.com6177e692013-02-28 20:16:25 +0000236 SkGLContextHelper* glContext = this->getGLContext();
scroggo@google.com0556ea02013-02-08 19:38:21 +0000237 if (NULL == glContext) {
238 SkASSERT(kBitmap_DeviceType == fDeviceType);
239 return;
240 }
keyar@chromium.org28136b32012-08-20 15:04:15 +0000241
scroggo@google.com0556ea02013-02-08 19:38:21 +0000242 fGrContext->flush();
243 if (callFinish) {
244 SK_GL(*glContext, Finish());
keyar@chromium.org77a55222012-08-20 15:03:47 +0000245 }
keyar@chromium.orga40c20d2012-08-20 15:04:12 +0000246#endif
keyar@chromium.org77a55222012-08-20 15:03:47 +0000247}
248
junov@chromium.org9313ca42012-11-02 18:11:49 +0000249uint32_t PictureRenderer::recordFlags() {
junov@chromium.org100b1c52013-01-16 20:12:22 +0000250 return ((kNone_BBoxHierarchyType == fBBoxHierarchyType) ? 0 :
251 SkPicture::kOptimizeForClippedPlayback_RecordingFlag) |
252 SkPicture::kUsePathBoundsForClip_RecordingFlag;
junov@chromium.org9313ca42012-11-02 18:11:49 +0000253}
254
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000255/**
256 * Write the canvas to the specified path.
257 * @param canvas Must be non-null. Canvas to be written to a file.
258 * @param path Path for the file to be written. Should have no extension; write() will append
259 * an appropriate one. Passed in by value so it can be modified.
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +0000260 * @param jsonSummaryPtr If not null, add image results to this summary.
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000261 * @return bool True if the Canvas is written to a file.
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +0000262 *
263 * TODO(epoger): Right now, all canvases must pass through this function in order to be appended
264 * to the ImageResultsSummary. We need some way to add bitmaps to the ImageResultsSummary
265 * even if --writePath has not been specified (and thus this function is not called).
266 *
267 * One fix would be to pass in these path elements separately, and allow this function to be
268 * called even if --writePath was not specified...
269 * const char *outputDir // NULL if we don't want to write image files to disk
270 * const char *filename // name we use within JSON summary, and as the filename within outputDir
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000271 */
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +0000272static bool write(SkCanvas* canvas, const SkString* path, ImageResultsSummary *jsonSummaryPtr) {
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000273 SkASSERT(canvas != NULL);
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000274 if (NULL == canvas) {
keyar@chromium.org9299ede2012-08-21 19:05:08 +0000275 return false;
276 }
277
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +0000278 SkASSERT(path != NULL); // TODO(epoger): we want to remove this constraint, as noted above
279 SkString fullPathname(*path);
280 fullPathname.append(".png");
281
keyar@chromium.org9299ede2012-08-21 19:05:08 +0000282 SkBitmap bitmap;
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000283 SkISize size = canvas->getDeviceSize();
284 sk_tools::setup_bitmap(&bitmap, size.width(), size.height());
keyar@chromium.org9299ede2012-08-21 19:05:08 +0000285
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000286 canvas->readPixels(&bitmap, 0, 0);
keyar@chromium.org9299ede2012-08-21 19:05:08 +0000287 sk_tools::force_all_opaque(bitmap);
288
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +0000289 if (NULL != jsonSummaryPtr) {
290 // EPOGER: This is a hacky way of constructing the filename associated with the
291 // image checksum; we assume that outputDir is not NULL, and we remove outputDir
292 // from fullPathname.
293 //
294 // EPOGER: what about including the config type within hashFilename? That way,
295 // we could combine results of different config types without conflicting filenames.
296 SkString hashFilename;
297 sk_tools::get_basename(&hashFilename, fullPathname);
298 jsonSummaryPtr->add(hashFilename.c_str(), bitmap);
299 }
300
301 return SkImageEncoder::EncodeFile(fullPathname.c_str(), bitmap, SkImageEncoder::kPNG_Type, 100);
keyar@chromium.org9299ede2012-08-21 19:05:08 +0000302}
303
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000304/**
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +0000305 * If path is non NULL, append number to it, and call write() to write the
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000306 * provided canvas to a file. Returns true if path is NULL or if write() succeeds.
307 */
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +0000308static bool writeAppendNumber(SkCanvas* canvas, const SkString* path, int number,
309 ImageResultsSummary *jsonSummaryPtr) {
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000310 if (NULL == path) {
311 return true;
312 }
313 SkString pathWithNumber(*path);
314 pathWithNumber.appendf("%i", number);
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +0000315 return write(canvas, &pathWithNumber, jsonSummaryPtr);
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000316}
317
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000318///////////////////////////////////////////////////////////////////////////////////////////////
319
djsollen@google.comfd9720c2012-11-06 16:54:40 +0000320SkCanvas* RecordPictureRenderer::setupCanvas(int width, int height) {
321 // defer the canvas setup until the render step
322 return NULL;
323}
324
scroggo@google.com1b1bcc32013-05-21 20:31:23 +0000325static SkData* encode_bitmap_to_data(size_t* offset, const SkBitmap& bm) {
326 SkPixelRef* pr = bm.pixelRef();
327 if (pr != NULL) {
328 SkData* data = pr->refEncodedData();
329 if (data != NULL) {
330 *offset = bm.pixelRefOffset();
331 return data;
332 }
333 }
334 *offset = 0;
335 return SkImageEncoder::EncodeData(bm, SkImageEncoder::kPNG_Type, 100);
scroggo@google.coma9e3a362012-11-07 17:52:48 +0000336}
337
edisonn@google.com84f548c2012-12-18 22:24:03 +0000338bool RecordPictureRenderer::render(const SkString* path, SkBitmap** out) {
junov@chromium.org9313ca42012-11-02 18:11:49 +0000339 SkAutoTUnref<SkPicture> replayer(this->createPicture());
scroggo@google.com82ec0b02012-12-17 19:25:54 +0000340 SkCanvas* recorder = replayer->beginRecording(this->getViewWidth(), this->getViewHeight(),
junov@chromium.org9313ca42012-11-02 18:11:49 +0000341 this->recordFlags());
scroggo@google.com82ec0b02012-12-17 19:25:54 +0000342 this->scaleToScaleFactor(recorder);
scroggo@google.com9a412522012-09-07 15:21:18 +0000343 fPicture->draw(recorder);
junov@chromium.org9313ca42012-11-02 18:11:49 +0000344 replayer->endRecording();
scroggo@google.coma9e3a362012-11-07 17:52:48 +0000345 if (path != NULL) {
346 // Record the new picture as a new SKP with PNG encoded bitmaps.
347 SkString skpPath(*path);
348 // ".skp" was removed from 'path' before being passed in here.
349 skpPath.append(".skp");
350 SkFILEWStream stream(skpPath.c_str());
scroggo@google.com1b1bcc32013-05-21 20:31:23 +0000351 replayer->serialize(&stream, &encode_bitmap_to_data);
scroggo@google.coma9e3a362012-11-07 17:52:48 +0000352 return true;
353 }
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000354 return false;
scroggo@google.com9a412522012-09-07 15:21:18 +0000355}
356
scroggo@google.com0a049b82012-11-02 22:01:26 +0000357SkString RecordPictureRenderer::getConfigNameInternal() {
358 return SkString("record");
359}
360
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000361///////////////////////////////////////////////////////////////////////////////////////////////
362
edisonn@google.com84f548c2012-12-18 22:24:03 +0000363bool PipePictureRenderer::render(const SkString* path, SkBitmap** out) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000364 SkASSERT(fCanvas.get() != NULL);
365 SkASSERT(fPicture != NULL);
keyar@chromium.org78a35c52012-08-20 15:03:44 +0000366 if (NULL == fCanvas.get() || NULL == fPicture) {
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000367 return false;
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000368 }
369
370 PipeController pipeController(fCanvas.get());
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000371 SkGPipeWriter writer;
372 SkCanvas* pipeCanvas = writer.startRecording(&pipeController);
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000373 pipeCanvas->drawPicture(*fPicture);
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000374 writer.endRecording();
scroggo@google.com9a412522012-09-07 15:21:18 +0000375 fCanvas->flush();
borenet@google.com070d3542012-10-26 13:26:55 +0000376 if (NULL != path) {
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +0000377 return write(fCanvas, path, fJsonSummaryPtr);
borenet@google.com070d3542012-10-26 13:26:55 +0000378 }
edisonn@google.com84f548c2012-12-18 22:24:03 +0000379 if (NULL != out) {
380 *out = SkNEW(SkBitmap);
381 setup_bitmap(*out, fPicture->width(), fPicture->height());
382 fCanvas->readPixels(*out, 0, 0);
skia.committer@gmail.coma7d8e3e2012-12-19 02:01:38 +0000383 }
borenet@google.com070d3542012-10-26 13:26:55 +0000384 return true;
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000385}
386
scroggo@google.com0a049b82012-11-02 22:01:26 +0000387SkString PipePictureRenderer::getConfigNameInternal() {
388 return SkString("pipe");
389}
390
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000391///////////////////////////////////////////////////////////////////////////////////////////////
392
junov@chromium.org9313ca42012-11-02 18:11:49 +0000393void SimplePictureRenderer::init(SkPicture* picture) {
394 INHERITED::init(picture);
395 this->buildBBoxHierarchy();
396}
397
edisonn@google.com84f548c2012-12-18 22:24:03 +0000398bool SimplePictureRenderer::render(const SkString* path, SkBitmap** out) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000399 SkASSERT(fCanvas.get() != NULL);
400 SkASSERT(fPicture != NULL);
keyar@chromium.org78a35c52012-08-20 15:03:44 +0000401 if (NULL == fCanvas.get() || NULL == fPicture) {
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000402 return false;
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000403 }
404
405 fCanvas->drawPicture(*fPicture);
scroggo@google.com9a412522012-09-07 15:21:18 +0000406 fCanvas->flush();
borenet@google.com070d3542012-10-26 13:26:55 +0000407 if (NULL != path) {
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +0000408 return write(fCanvas, path, fJsonSummaryPtr);
borenet@google.com070d3542012-10-26 13:26:55 +0000409 }
skia.committer@gmail.coma7d8e3e2012-12-19 02:01:38 +0000410
edisonn@google.com84f548c2012-12-18 22:24:03 +0000411 if (NULL != out) {
412 *out = SkNEW(SkBitmap);
413 setup_bitmap(*out, fPicture->width(), fPicture->height());
414 fCanvas->readPixels(*out, 0, 0);
415 }
416
borenet@google.com070d3542012-10-26 13:26:55 +0000417 return true;
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000418}
419
scroggo@google.com0a049b82012-11-02 22:01:26 +0000420SkString SimplePictureRenderer::getConfigNameInternal() {
421 return SkString("simple");
422}
423
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000424///////////////////////////////////////////////////////////////////////////////////////////////
425
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000426TiledPictureRenderer::TiledPictureRenderer()
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000427 : fTileWidth(kDefaultTileWidth)
rileya@google.comb947b912012-08-29 17:35:07 +0000428 , fTileHeight(kDefaultTileHeight)
rileya@google.coma04dc022012-09-10 19:01:38 +0000429 , fTileWidthPercentage(0.0)
rileya@google.comb947b912012-08-29 17:35:07 +0000430 , fTileHeightPercentage(0.0)
scroggo@google.comcbcef702012-12-13 22:09:28 +0000431 , fTileMinPowerOf2Width(0)
432 , fCurrentTileOffset(-1)
433 , fTilesX(0)
434 , fTilesY(0) { }
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000435
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000436void TiledPictureRenderer::init(SkPicture* pict) {
437 SkASSERT(pict != NULL);
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000438 SkASSERT(0 == fTileRects.count());
439 if (NULL == pict || fTileRects.count() != 0) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000440 return;
441 }
442
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000443 // Do not call INHERITED::init(), which would create a (potentially large) canvas which is not
444 // used by bench_pictures.
445 fPicture = pict;
junov@chromium.org9313ca42012-11-02 18:11:49 +0000446 fPicture->ref();
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000447 this->buildBBoxHierarchy();
keyar@chromium.orgcc6e5ef2012-07-27 20:09:26 +0000448
449 if (fTileWidthPercentage > 0) {
robertphillips@google.com5d8d1862012-08-15 14:36:41 +0000450 fTileWidth = sk_float_ceil2int(float(fTileWidthPercentage * fPicture->width() / 100));
keyar@chromium.orgcc6e5ef2012-07-27 20:09:26 +0000451 }
452 if (fTileHeightPercentage > 0) {
robertphillips@google.com5d8d1862012-08-15 14:36:41 +0000453 fTileHeight = sk_float_ceil2int(float(fTileHeightPercentage * fPicture->height() / 100));
keyar@chromium.orgcc6e5ef2012-07-27 20:09:26 +0000454 }
455
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000456 if (fTileMinPowerOf2Width > 0) {
457 this->setupPowerOf2Tiles();
458 } else {
459 this->setupTiles();
460 }
scroggo@google.comcbcef702012-12-13 22:09:28 +0000461 fCanvas.reset(this->setupCanvas(fTileWidth, fTileHeight));
462 // Initialize to -1 so that the first call to nextTile will set this up to draw tile 0 on the
463 // first call to drawCurrentTile.
464 fCurrentTileOffset = -1;
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000465}
466
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000467void TiledPictureRenderer::end() {
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000468 fTileRects.reset();
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000469 this->INHERITED::end();
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000470}
471
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000472void TiledPictureRenderer::setupTiles() {
scroggo@google.comc0d5e542012-12-13 21:40:48 +0000473 // Only use enough tiles to cover the viewport
474 const int width = this->getViewWidth();
475 const int height = this->getViewHeight();
476
scroggo@google.comcbcef702012-12-13 22:09:28 +0000477 fTilesX = fTilesY = 0;
scroggo@google.comc0d5e542012-12-13 21:40:48 +0000478 for (int tile_y_start = 0; tile_y_start < height; tile_y_start += fTileHeight) {
scroggo@google.comcbcef702012-12-13 22:09:28 +0000479 fTilesY++;
scroggo@google.comc0d5e542012-12-13 21:40:48 +0000480 for (int tile_x_start = 0; tile_x_start < width; tile_x_start += fTileWidth) {
scroggo@google.comcbcef702012-12-13 22:09:28 +0000481 if (0 == tile_y_start) {
482 // Only count tiles in the X direction on the first pass.
483 fTilesX++;
484 }
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000485 *fTileRects.append() = SkRect::MakeXYWH(SkIntToScalar(tile_x_start),
486 SkIntToScalar(tile_y_start),
487 SkIntToScalar(fTileWidth),
488 SkIntToScalar(fTileHeight));
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000489 }
490 }
491}
492
scroggo@google.comcbcef702012-12-13 22:09:28 +0000493bool TiledPictureRenderer::tileDimensions(int &x, int &y) {
494 if (fTileRects.count() == 0 || NULL == fPicture) {
495 return false;
496 }
497 x = fTilesX;
498 y = fTilesY;
499 return true;
500}
501
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000502// The goal of the powers of two tiles is to minimize the amount of wasted tile
503// space in the width-wise direction and then minimize the number of tiles. The
504// constraints are that every tile must have a pixel width that is a power of
505// two and also be of some minimal width (that is also a power of two).
506//
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000507// This is solved by first taking our picture size and rounding it up to the
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000508// multiple of the minimal width. The binary representation of this rounded
509// value gives us the tiles we need: a bit of value one means we need a tile of
510// that size.
511void TiledPictureRenderer::setupPowerOf2Tiles() {
scroggo@google.comc0d5e542012-12-13 21:40:48 +0000512 // Only use enough tiles to cover the viewport
513 const int width = this->getViewWidth();
514 const int height = this->getViewHeight();
515
516 int rounded_value = width;
517 if (width % fTileMinPowerOf2Width != 0) {
518 rounded_value = width - (width % fTileMinPowerOf2Width) + fTileMinPowerOf2Width;
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000519 }
520
reed@google.come15b2f52013-12-18 04:59:26 +0000521 int num_bits = SkScalarCeilToInt(scalar_log2(SkIntToScalar(width)));
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000522 int largest_possible_tile_size = 1 << num_bits;
523
scroggo@google.comcbcef702012-12-13 22:09:28 +0000524 fTilesX = fTilesY = 0;
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000525 // The tile height is constant for a particular picture.
scroggo@google.comc0d5e542012-12-13 21:40:48 +0000526 for (int tile_y_start = 0; tile_y_start < height; tile_y_start += fTileHeight) {
scroggo@google.comcbcef702012-12-13 22:09:28 +0000527 fTilesY++;
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000528 int tile_x_start = 0;
529 int current_width = largest_possible_tile_size;
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000530 // Set fTileWidth to be the width of the widest tile, so that each canvas is large enough
531 // to draw each tile.
532 fTileWidth = current_width;
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000533
534 while (current_width >= fTileMinPowerOf2Width) {
535 // It is very important this is a bitwise AND.
536 if (current_width & rounded_value) {
scroggo@google.comcbcef702012-12-13 22:09:28 +0000537 if (0 == tile_y_start) {
538 // Only count tiles in the X direction on the first pass.
539 fTilesX++;
540 }
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000541 *fTileRects.append() = SkRect::MakeXYWH(SkIntToScalar(tile_x_start),
542 SkIntToScalar(tile_y_start),
543 SkIntToScalar(current_width),
544 SkIntToScalar(fTileHeight));
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000545 tile_x_start += current_width;
546 }
547
548 current_width >>= 1;
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000549 }
550 }
551}
552
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000553/**
554 * Draw the specified playback to the canvas translated to rectangle provided, so that this mini
555 * canvas represents the rectangle's portion of the overall picture.
556 * Saves and restores so that the initial clip and matrix return to their state before this function
557 * is called.
558 */
559template<class T>
560static void DrawTileToCanvas(SkCanvas* canvas, const SkRect& tileRect, T* playback) {
561 int saveCount = canvas->save();
scroggo@google.com82ec0b02012-12-17 19:25:54 +0000562 // Translate so that we draw the correct portion of the picture.
563 // Perform a postTranslate so that the scaleFactor does not interfere with the positioning.
564 SkMatrix mat(canvas->getTotalMatrix());
565 mat.postTranslate(-tileRect.fLeft, -tileRect.fTop);
566 canvas->setMatrix(mat);
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000567 playback->draw(canvas);
568 canvas->restoreToCount(saveCount);
569 canvas->flush();
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000570}
571
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000572///////////////////////////////////////////////////////////////////////////////////////////////
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000573
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +0000574/**
575 * Copies the entirety of the src bitmap (typically a tile) into a portion of the dst bitmap.
576 * If the src bitmap is too large to fit within the dst bitmap after the x and y
577 * offsets have been applied, any excess will be ignored (so only the top-left portion of the
578 * src bitmap will be copied).
579 *
580 * @param src source bitmap
581 * @param dst destination bitmap
582 * @param xOffset x-offset within destination bitmap
583 * @param yOffset y-offset within destination bitmap
584 */
585static void bitmapCopyAtOffset(const SkBitmap& src, SkBitmap* dst,
586 int xOffset, int yOffset) {
587 for (int y = 0; y <src.height() && y + yOffset < dst->height() ; y++) {
588 for (int x = 0; x < src.width() && x + xOffset < dst->width() ; x++) {
589 *dst->getAddr32(xOffset + x, yOffset + y) = *src.getAddr32(x, y);
edisonn@google.com84f548c2012-12-18 22:24:03 +0000590 }
591 }
592}
593
scroggo@google.comcbcef702012-12-13 22:09:28 +0000594bool TiledPictureRenderer::nextTile(int &i, int &j) {
595 if (++fCurrentTileOffset < fTileRects.count()) {
596 i = fCurrentTileOffset % fTilesX;
597 j = fCurrentTileOffset / fTilesX;
598 return true;
599 }
600 return false;
601}
602
603void TiledPictureRenderer::drawCurrentTile() {
604 SkASSERT(fCurrentTileOffset >= 0 && fCurrentTileOffset < fTileRects.count());
605 DrawTileToCanvas(fCanvas, fTileRects[fCurrentTileOffset], fPicture);
606}
607
edisonn@google.com84f548c2012-12-18 22:24:03 +0000608bool TiledPictureRenderer::render(const SkString* path, SkBitmap** out) {
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000609 SkASSERT(fPicture != NULL);
610 if (NULL == fPicture) {
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000611 return false;
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000612 }
613
edisonn@google.com84f548c2012-12-18 22:24:03 +0000614 SkBitmap bitmap;
615 if (out){
616 *out = SkNEW(SkBitmap);
617 setup_bitmap(*out, fPicture->width(), fPicture->height());
618 setup_bitmap(&bitmap, fTileWidth, fTileHeight);
619 }
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000620 bool success = true;
621 for (int i = 0; i < fTileRects.count(); ++i) {
scroggo@google.comcbcef702012-12-13 22:09:28 +0000622 DrawTileToCanvas(fCanvas, fTileRects[i], fPicture);
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000623 if (NULL != path) {
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +0000624 success &= writeAppendNumber(fCanvas, path, i, fJsonSummaryPtr);
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000625 }
edisonn@google.com84f548c2012-12-18 22:24:03 +0000626 if (NULL != out) {
627 if (fCanvas->readPixels(&bitmap, 0, 0)) {
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +0000628 // Add this tile to the entire bitmap.
629 bitmapCopyAtOffset(bitmap, *out, SkScalarFloorToInt(fTileRects[i].left()),
630 SkScalarFloorToInt(fTileRects[i].top()));
edisonn@google.com84f548c2012-12-18 22:24:03 +0000631 } else {
632 success = false;
633 }
634 }
keyar@chromium.org163b5672012-08-01 17:53:29 +0000635 }
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000636 return success;
keyar@chromium.org163b5672012-08-01 17:53:29 +0000637}
638
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000639SkCanvas* TiledPictureRenderer::setupCanvas(int width, int height) {
640 SkCanvas* canvas = this->INHERITED::setupCanvas(width, height);
641 SkASSERT(fPicture != NULL);
scroggo@google.com82ec0b02012-12-17 19:25:54 +0000642 // Clip the tile to an area that is completely inside both the SkPicture and the viewport. This
643 // is mostly important for tiles on the right and bottom edges as they may go over this area and
644 // the picture may have some commands that draw outside of this area and so should not actually
645 // be written.
646 // Uses a clipRegion so that it will be unaffected by the scale factor, which may have been set
647 // by INHERITED::setupCanvas.
648 SkRegion clipRegion;
649 clipRegion.setRect(0, 0, this->getViewWidth(), this->getViewHeight());
650 canvas->clipRegion(clipRegion);
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000651 return canvas;
652}
scroggo@google.com0a049b82012-11-02 22:01:26 +0000653
654SkString TiledPictureRenderer::getConfigNameInternal() {
655 SkString name;
656 if (fTileMinPowerOf2Width > 0) {
657 name.append("pow2tile_");
658 name.appendf("%i", fTileMinPowerOf2Width);
659 } else {
660 name.append("tile_");
661 if (fTileWidthPercentage > 0) {
662 name.appendf("%.f%%", fTileWidthPercentage);
663 } else {
664 name.appendf("%i", fTileWidth);
665 }
666 }
667 name.append("x");
668 if (fTileHeightPercentage > 0) {
669 name.appendf("%.f%%", fTileHeightPercentage);
670 } else {
671 name.appendf("%i", fTileHeight);
672 }
673 return name;
674}
675
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000676///////////////////////////////////////////////////////////////////////////////////////////////
677
678// Holds all of the information needed to draw a set of tiles.
679class CloneData : public SkRunnable {
680
681public:
682 CloneData(SkPicture* clone, SkCanvas* canvas, SkTDArray<SkRect>& rects, int start, int end,
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +0000683 SkRunnable* done, ImageResultsSummary* jsonSummaryPtr)
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000684 : fClone(clone)
685 , fCanvas(canvas)
686 , fPath(NULL)
687 , fRects(rects)
688 , fStart(start)
689 , fEnd(end)
690 , fSuccess(NULL)
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +0000691 , fDone(done)
692 , fJsonSummaryPtr(jsonSummaryPtr) {
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000693 SkASSERT(fDone != NULL);
694 }
695
696 virtual void run() SK_OVERRIDE {
697 SkGraphics::SetTLSFontCacheLimit(1024 * 1024);
edisonn@google.com84f548c2012-12-18 22:24:03 +0000698
699 SkBitmap bitmap;
700 if (fBitmap != NULL) {
701 // All tiles are the same size.
jvanverth@google.com9c4e5ac2013-01-07 18:41:28 +0000702 setup_bitmap(&bitmap, SkScalarFloorToInt(fRects[0].width()), SkScalarFloorToInt(fRects[0].height()));
edisonn@google.com84f548c2012-12-18 22:24:03 +0000703 }
skia.committer@gmail.coma7d8e3e2012-12-19 02:01:38 +0000704
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000705 for (int i = fStart; i < fEnd; i++) {
706 DrawTileToCanvas(fCanvas, fRects[i], fClone);
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +0000707 if ((fPath != NULL) && !writeAppendNumber(fCanvas, fPath, i, fJsonSummaryPtr)
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000708 && fSuccess != NULL) {
709 *fSuccess = false;
710 // If one tile fails to write to a file, do not continue drawing the rest.
711 break;
712 }
edisonn@google.com84f548c2012-12-18 22:24:03 +0000713 if (fBitmap != NULL) {
714 if (fCanvas->readPixels(&bitmap, 0, 0)) {
715 SkAutoLockPixels alp(*fBitmap);
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +0000716 bitmapCopyAtOffset(bitmap, fBitmap, SkScalarFloorToInt(fRects[i].left()),
717 SkScalarFloorToInt(fRects[i].top()));
edisonn@google.com84f548c2012-12-18 22:24:03 +0000718 } else {
719 *fSuccess = false;
720 // If one tile fails to read pixels, do not continue drawing the rest.
721 break;
722 }
723 }
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000724 }
725 fDone->run();
726 }
727
728 void setPathAndSuccess(const SkString* path, bool* success) {
729 fPath = path;
730 fSuccess = success;
731 }
732
edisonn@google.com84f548c2012-12-18 22:24:03 +0000733 void setBitmap(SkBitmap* bitmap) {
734 fBitmap = bitmap;
735 }
736
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000737private:
738 // All pointers unowned.
739 SkPicture* fClone; // Picture to draw from. Each CloneData has a unique one which
740 // is threadsafe.
741 SkCanvas* fCanvas; // Canvas to draw to. Reused for each tile.
742 const SkString* fPath; // If non-null, path to write the result to as a PNG.
743 SkTDArray<SkRect>& fRects; // All tiles of the picture.
744 const int fStart; // Range of tiles drawn by this thread.
745 const int fEnd;
746 bool* fSuccess; // Only meaningful if path is non-null. Shared by all threads,
747 // and only set to false upon failure to write to a PNG.
748 SkRunnable* fDone;
edisonn@google.com84f548c2012-12-18 22:24:03 +0000749 SkBitmap* fBitmap;
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +0000750 ImageResultsSummary* fJsonSummaryPtr;
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000751};
752
753MultiCorePictureRenderer::MultiCorePictureRenderer(int threadCount)
754: fNumThreads(threadCount)
755, fThreadPool(threadCount)
756, fCountdown(threadCount) {
757 // Only need to create fNumThreads - 1 clones, since one thread will use the base
758 // picture.
759 fPictureClones = SkNEW_ARRAY(SkPicture, fNumThreads - 1);
760 fCloneData = SkNEW_ARRAY(CloneData*, fNumThreads);
761}
762
763void MultiCorePictureRenderer::init(SkPicture *pict) {
764 // Set fPicture and the tiles.
765 this->INHERITED::init(pict);
766 for (int i = 0; i < fNumThreads; ++i) {
767 *fCanvasPool.append() = this->setupCanvas(this->getTileWidth(), this->getTileHeight());
768 }
769 // Only need to create fNumThreads - 1 clones, since one thread will use the base picture.
770 fPicture->clone(fPictureClones, fNumThreads - 1);
771 // Populate each thread with the appropriate data.
772 // Group the tiles into nearly equal size chunks, rounding up so we're sure to cover them all.
773 const int chunkSize = (fTileRects.count() + fNumThreads - 1) / fNumThreads;
774
775 for (int i = 0; i < fNumThreads; i++) {
776 SkPicture* pic;
777 if (i == fNumThreads-1) {
778 // The last set will use the original SkPicture.
779 pic = fPicture;
780 } else {
781 pic = &fPictureClones[i];
782 }
783 const int start = i * chunkSize;
784 const int end = SkMin32(start + chunkSize, fTileRects.count());
785 fCloneData[i] = SkNEW_ARGS(CloneData,
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +0000786 (pic, fCanvasPool[i], fTileRects, start, end, &fCountdown,
787 fJsonSummaryPtr));
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000788 }
789}
790
edisonn@google.com84f548c2012-12-18 22:24:03 +0000791bool MultiCorePictureRenderer::render(const SkString *path, SkBitmap** out) {
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000792 bool success = true;
793 if (path != NULL) {
794 for (int i = 0; i < fNumThreads-1; i++) {
795 fCloneData[i]->setPathAndSuccess(path, &success);
796 }
797 }
798
edisonn@google.com84f548c2012-12-18 22:24:03 +0000799 if (NULL != out) {
800 *out = SkNEW(SkBitmap);
801 setup_bitmap(*out, fPicture->width(), fPicture->height());
802 for (int i = 0; i < fNumThreads; i++) {
803 fCloneData[i]->setBitmap(*out);
804 }
805 } else {
806 for (int i = 0; i < fNumThreads; i++) {
807 fCloneData[i]->setBitmap(NULL);
808 }
809 }
810
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000811 fCountdown.reset(fNumThreads);
812 for (int i = 0; i < fNumThreads; i++) {
813 fThreadPool.add(fCloneData[i]);
814 }
815 fCountdown.wait();
816
817 return success;
818}
819
820void MultiCorePictureRenderer::end() {
821 for (int i = 0; i < fNumThreads - 1; i++) {
822 SkDELETE(fCloneData[i]);
823 fCloneData[i] = NULL;
824 }
825
826 fCanvasPool.unrefAll();
827
828 this->INHERITED::end();
829}
830
831MultiCorePictureRenderer::~MultiCorePictureRenderer() {
832 // Each individual CloneData was deleted in end.
833 SkDELETE_ARRAY(fCloneData);
834 SkDELETE_ARRAY(fPictureClones);
835}
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000836
scroggo@google.com0a049b82012-11-02 22:01:26 +0000837SkString MultiCorePictureRenderer::getConfigNameInternal() {
838 SkString name = this->INHERITED::getConfigNameInternal();
839 name.appendf("_multi_%i_threads", fNumThreads);
840 return name;
841}
842
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000843///////////////////////////////////////////////////////////////////////////////////////////////
scroggo@google.com9a412522012-09-07 15:21:18 +0000844
845void PlaybackCreationRenderer::setup() {
junov@chromium.org9313ca42012-11-02 18:11:49 +0000846 fReplayer.reset(this->createPicture());
scroggo@google.com82ec0b02012-12-17 19:25:54 +0000847 SkCanvas* recorder = fReplayer->beginRecording(this->getViewWidth(), this->getViewHeight(),
junov@chromium.org9313ca42012-11-02 18:11:49 +0000848 this->recordFlags());
scroggo@google.com82ec0b02012-12-17 19:25:54 +0000849 this->scaleToScaleFactor(recorder);
scroggo@google.com9a412522012-09-07 15:21:18 +0000850 fPicture->draw(recorder);
851}
852
edisonn@google.com84f548c2012-12-18 22:24:03 +0000853bool PlaybackCreationRenderer::render(const SkString*, SkBitmap** out) {
junov@chromium.org9313ca42012-11-02 18:11:49 +0000854 fReplayer->endRecording();
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000855 // Since this class does not actually render, return false.
856 return false;
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000857}
858
scroggo@google.com0a049b82012-11-02 22:01:26 +0000859SkString PlaybackCreationRenderer::getConfigNameInternal() {
860 return SkString("playback_creation");
861}
862
junov@chromium.org9313ca42012-11-02 18:11:49 +0000863///////////////////////////////////////////////////////////////////////////////////////////////
864// SkPicture variants for each BBoxHierarchy type
865
866class RTreePicture : public SkPicture {
867public:
868 virtual SkBBoxHierarchy* createBBoxHierarchy() const SK_OVERRIDE{
869 static const int kRTreeMinChildren = 6;
870 static const int kRTreeMaxChildren = 11;
871 SkScalar aspectRatio = SkScalarDiv(SkIntToScalar(fWidth),
872 SkIntToScalar(fHeight));
sglez@google.com8c902122013-08-30 17:27:47 +0000873 bool sortDraws = false;
junov@chromium.org9313ca42012-11-02 18:11:49 +0000874 return SkRTree::Create(kRTreeMinChildren, kRTreeMaxChildren,
sglez@google.com8c902122013-08-30 17:27:47 +0000875 aspectRatio, sortDraws);
junov@chromium.org9313ca42012-11-02 18:11:49 +0000876 }
877};
878
879SkPicture* PictureRenderer::createPicture() {
880 switch (fBBoxHierarchyType) {
881 case kNone_BBoxHierarchyType:
882 return SkNEW(SkPicture);
883 case kRTree_BBoxHierarchyType:
884 return SkNEW(RTreePicture);
junov@chromium.org7b537062012-11-06 18:58:43 +0000885 case kTileGrid_BBoxHierarchyType:
junov@chromium.org29b19e52013-02-27 18:35:16 +0000886 return SkNEW_ARGS(SkTileGridPicture, (fPicture->width(),
887 fPicture->height(), fGridInfo));
junov@chromium.org9313ca42012-11-02 18:11:49 +0000888 }
889 SkASSERT(0); // invalid bbhType
890 return NULL;
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000891}
junov@chromium.org9313ca42012-11-02 18:11:49 +0000892
reed@google.comfe7b1ed2012-11-29 21:00:39 +0000893///////////////////////////////////////////////////////////////////////////////
894
895class GatherRenderer : public PictureRenderer {
896public:
edisonn@google.com84f548c2012-12-18 22:24:03 +0000897 virtual bool render(const SkString* path, SkBitmap** out = NULL)
898 SK_OVERRIDE {
reed@google.comfe7b1ed2012-11-29 21:00:39 +0000899 SkRect bounds = SkRect::MakeWH(SkIntToScalar(fPicture->width()),
900 SkIntToScalar(fPicture->height()));
901 SkData* data = SkPictureUtils::GatherPixelRefs(fPicture, bounds);
902 SkSafeUnref(data);
skia.committer@gmail.comc7b4be72012-12-11 02:01:20 +0000903
reed@google.comfe7b1ed2012-11-29 21:00:39 +0000904 return NULL == path; // we don't have anything to write
905 }
skia.committer@gmail.comc7b4be72012-12-11 02:01:20 +0000906
reed@google.comfe7b1ed2012-11-29 21:00:39 +0000907private:
908 virtual SkString getConfigNameInternal() SK_OVERRIDE {
909 return SkString("gather_pixelrefs");
910 }
911};
912
913PictureRenderer* CreateGatherPixelRefsRenderer() {
914 return SkNEW(GatherRenderer);
915}
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +0000916
reed@google.com5a34fd32012-12-10 16:05:09 +0000917///////////////////////////////////////////////////////////////////////////////
918
919class PictureCloneRenderer : public PictureRenderer {
920public:
edisonn@google.com84f548c2012-12-18 22:24:03 +0000921 virtual bool render(const SkString* path, SkBitmap** out = NULL)
922 SK_OVERRIDE {
reed@google.com5a34fd32012-12-10 16:05:09 +0000923 for (int i = 0; i < 100; ++i) {
924 SkPicture* clone = fPicture->clone();
925 SkSafeUnref(clone);
926 }
skia.committer@gmail.comc7b4be72012-12-11 02:01:20 +0000927
reed@google.com5a34fd32012-12-10 16:05:09 +0000928 return NULL == path; // we don't have anything to write
929 }
skia.committer@gmail.comc7b4be72012-12-11 02:01:20 +0000930
reed@google.com5a34fd32012-12-10 16:05:09 +0000931private:
932 virtual SkString getConfigNameInternal() SK_OVERRIDE {
933 return SkString("picture_clone");
934 }
935};
936
937PictureRenderer* CreatePictureCloneRenderer() {
938 return SkNEW(PictureCloneRenderer);
939}
940
junov@chromium.org9313ca42012-11-02 18:11:49 +0000941} // namespace sk_tools