blob: 466e31d28e159ba4bdeae3d080e0f50b9fa7458b [file] [log] [blame]
keyar@chromium.orgb3fb7c12012-08-20 21:02:49 +00001/*
2 * Copyright 2012 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
keyar@chromium.org451bb9f2012-07-26 17:27:57 +00008#include "PictureRenderer.h"
scroggo@google.com58b4ead2012-08-31 16:15:22 +00009#include "picture_utils.h"
keyar@chromium.org451bb9f2012-07-26 17:27:57 +000010#include "SamplePipeControllers.h"
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +000011#include "SkBitmapHasher.h"
keyar@chromium.org451bb9f2012-07-26 17:27:57 +000012#include "SkCanvas.h"
scroggo@google.com1b1bcc32013-05-21 20:31:23 +000013#include "SkData.h"
keyar@chromium.org451bb9f2012-07-26 17:27:57 +000014#include "SkDevice.h"
robertphillips@google.com94d8f1e2013-12-18 17:25:33 +000015#include "SkDiscardableMemoryPool.h"
keyar@chromium.org451bb9f2012-07-26 17:27:57 +000016#include "SkGPipe.h"
scroggo@google.com58b4ead2012-08-31 16:15:22 +000017#if SK_SUPPORT_GPU
robertphillips@google.comfe1b5362013-02-07 19:45:46 +000018#include "gl/GrGLDefines.h"
scroggo@google.com58b4ead2012-08-31 16:15:22 +000019#include "SkGpuDevice.h"
20#endif
21#include "SkGraphics.h"
22#include "SkImageEncoder.h"
caryclark@google.coma3622372012-11-06 21:26:13 +000023#include "SkMaskFilter.h"
keyar@chromium.orgea826952012-08-23 15:24:13 +000024#include "SkMatrix.h"
keyar@chromium.org451bb9f2012-07-26 17:27:57 +000025#include "SkPicture.h"
scroggo@google.com1b1bcc32013-05-21 20:31:23 +000026#include "SkPictureUtils.h"
27#include "SkPixelRef.h"
commit-bot@chromium.orgc22d1392014-02-03 18:08:33 +000028#include "SkQuadTree.h"
29#include "SkQuadTreePicture.h"
junov@chromium.org9313ca42012-11-02 18:11:49 +000030#include "SkRTree.h"
keyar@chromium.orgea826952012-08-23 15:24:13 +000031#include "SkScalar.h"
scroggo@google.coma9e3a362012-11-07 17:52:48 +000032#include "SkStream.h"
keyar@chromium.org9299ede2012-08-21 19:05:08 +000033#include "SkString.h"
scroggo@google.com58b4ead2012-08-31 16:15:22 +000034#include "SkTemplates.h"
junov@chromium.org3cb834b2012-12-13 16:39:53 +000035#include "SkTileGridPicture.h"
keyar@chromium.org451bb9f2012-07-26 17:27:57 +000036#include "SkTDArray.h"
scroggo@google.com58b4ead2012-08-31 16:15:22 +000037#include "SkThreadUtils.h"
keyar@chromium.org451bb9f2012-07-26 17:27:57 +000038#include "SkTypes.h"
keyar@chromium.org4ea96c52012-08-20 15:03:29 +000039
reed@google.come15b2f52013-12-18 04:59:26 +000040static inline SkScalar scalar_log2(SkScalar x) {
41 static const SkScalar log2_conversion_factor = SkScalarDiv(1, SkScalarLog(2));
skia.committer@gmail.com3b85deb2013-12-18 07:01:56 +000042
reed@google.come15b2f52013-12-18 04:59:26 +000043 return SkScalarLog(x) * log2_conversion_factor;
44}
45
keyar@chromium.org451bb9f2012-07-26 17:27:57 +000046namespace sk_tools {
47
48enum {
49 kDefaultTileWidth = 256,
50 kDefaultTileHeight = 256
51};
52
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +000053/* TODO(epoger): These constants are already maintained in 2 other places:
54 * gm/gm_json.py and gm/gm_expectations.cpp. We shouldn't add yet a third place.
55 * Figure out a way to share the definitions instead.
56 */
57const static char kJsonKey_ActualResults[] = "actual-results";
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +000058const static char kJsonKey_ActualResults_NoComparison[] = "no-comparison";
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +000059const static char kJsonKey_Hashtype_Bitmap_64bitMD5[] = "bitmap-64bitMD5";
60
61void ImageResultsSummary::add(const char *testName, const SkBitmap& bitmap) {
62 uint64_t hash;
63 SkAssertResult(SkBitmapHasher::ComputeDigest(bitmap, &hash));
64 Json::Value jsonTypeValuePair;
65 jsonTypeValuePair.append(Json::Value(kJsonKey_Hashtype_Bitmap_64bitMD5));
66 jsonTypeValuePair.append(Json::UInt64(hash));
67 fActualResultsNoComparison[testName] = jsonTypeValuePair;
68}
69
70void ImageResultsSummary::writeToFile(const char *filename) {
71 Json::Value actualResults;
72 actualResults[kJsonKey_ActualResults_NoComparison] = fActualResultsNoComparison;
73 Json::Value root;
74 root[kJsonKey_ActualResults] = actualResults;
75 std::string jsonStdString = root.toStyledString();
76 SkFILEWStream stream(filename);
77 stream.write(jsonStdString.c_str(), jsonStdString.length());
78}
79
keyar@chromium.org9d696c02012-08-07 17:11:33 +000080void PictureRenderer::init(SkPicture* pict) {
keyar@chromium.org78a35c52012-08-20 15:03:44 +000081 SkASSERT(NULL == fPicture);
82 SkASSERT(NULL == fCanvas.get());
83 if (fPicture != NULL || NULL != fCanvas.get()) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +000084 return;
85 }
86
87 SkASSERT(pict != NULL);
keyar@chromium.org78a35c52012-08-20 15:03:44 +000088 if (NULL == pict) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +000089 return;
90 }
91
92 fPicture = pict;
junov@chromium.org9313ca42012-11-02 18:11:49 +000093 fPicture->ref();
keyar@chromium.orga474ce32012-08-20 15:03:57 +000094 fCanvas.reset(this->setupCanvas());
95}
96
caryclark@google.coma3622372012-11-06 21:26:13 +000097class FlagsDrawFilter : public SkDrawFilter {
98public:
99 FlagsDrawFilter(PictureRenderer::DrawFilterFlags* flags) :
100 fFlags(flags) {}
101
reed@google.com971aca72012-11-26 20:26:54 +0000102 virtual bool filter(SkPaint* paint, Type t) {
caryclark@google.coma3622372012-11-06 21:26:13 +0000103 paint->setFlags(paint->getFlags() & ~fFlags[t] & SkPaint::kAllFlags);
robertphillips@google.com49149312013-07-03 15:34:35 +0000104 if (PictureRenderer::kMaskFilter_DrawFilterFlag & fFlags[t]) {
caryclark@google.coma3622372012-11-06 21:26:13 +0000105 SkMaskFilter* maskFilter = paint->getMaskFilter();
robertphillips@google.com49149312013-07-03 15:34:35 +0000106 if (NULL != maskFilter) {
reed@google.com457d8a72012-12-18 18:20:44 +0000107 paint->setMaskFilter(NULL);
caryclark@google.coma3622372012-11-06 21:26:13 +0000108 }
109 }
110 if (PictureRenderer::kHinting_DrawFilterFlag & fFlags[t]) {
111 paint->setHinting(SkPaint::kNo_Hinting);
112 } else if (PictureRenderer::kSlightHinting_DrawFilterFlag & fFlags[t]) {
113 paint->setHinting(SkPaint::kSlight_Hinting);
114 }
reed@google.com971aca72012-11-26 20:26:54 +0000115 return true;
caryclark@google.coma3622372012-11-06 21:26:13 +0000116 }
117
118private:
119 PictureRenderer::DrawFilterFlags* fFlags;
120};
121
scroggo@google.com82ec0b02012-12-17 19:25:54 +0000122static void setUpFilter(SkCanvas* canvas, PictureRenderer::DrawFilterFlags* drawFilters) {
caryclark@google.coma3622372012-11-06 21:26:13 +0000123 if (drawFilters && !canvas->getDrawFilter()) {
124 canvas->setDrawFilter(SkNEW_ARGS(FlagsDrawFilter, (drawFilters)))->unref();
caryclark@google.come3e940c2012-11-07 16:42:17 +0000125 if (drawFilters[0] & PictureRenderer::kAAClip_DrawFilterFlag) {
126 canvas->setAllowSoftClip(false);
127 }
caryclark@google.coma3622372012-11-06 21:26:13 +0000128 }
caryclark@google.coma3622372012-11-06 21:26:13 +0000129}
130
keyar@chromium.orga474ce32012-08-20 15:03:57 +0000131SkCanvas* PictureRenderer::setupCanvas() {
scroggo@google.comc0d5e542012-12-13 21:40:48 +0000132 const int width = this->getViewWidth();
133 const int height = this->getViewHeight();
134 return this->setupCanvas(width, height);
keyar@chromium.orga474ce32012-08-20 15:03:57 +0000135}
136
137SkCanvas* PictureRenderer::setupCanvas(int width, int height) {
caryclark@google.coma3622372012-11-06 21:26:13 +0000138 SkCanvas* canvas;
keyar@chromium.org4ea96c52012-08-20 15:03:29 +0000139 switch(fDeviceType) {
140 case kBitmap_DeviceType: {
141 SkBitmap bitmap;
keyar@chromium.orga474ce32012-08-20 15:03:57 +0000142 sk_tools::setup_bitmap(&bitmap, width, height);
caryclark@google.coma3622372012-11-06 21:26:13 +0000143 canvas = SkNEW_ARGS(SkCanvas, (bitmap));
keyar@chromium.org4ea96c52012-08-20 15:03:29 +0000144 }
scroggo@google.com82ec0b02012-12-17 19:25:54 +0000145 break;
keyar@chromium.org4ea96c52012-08-20 15:03:29 +0000146#if SK_SUPPORT_GPU
scroggo@google.com0556ea02013-02-08 19:38:21 +0000147#if SK_ANGLE
148 case kAngle_DeviceType:
149 // fall through
150#endif
rmistry@google.com6ab96732014-01-06 18:37:24 +0000151#if SK_MESA
152 case kMesa_DeviceType:
153 // fall through
154#endif
commit-bot@chromium.org0fd52702014-03-07 18:41:14 +0000155 case kGPU_DeviceType:
156 case kNVPR_DeviceType: {
commit-bot@chromium.orgae403b92013-04-10 17:27:30 +0000157 SkAutoTUnref<GrSurface> target;
scroggo@google.com0556ea02013-02-08 19:38:21 +0000158 if (fGrContext) {
159 // create a render target to back the device
160 GrTextureDesc desc;
161 desc.fConfig = kSkia8888_GrPixelConfig;
162 desc.fFlags = kRenderTarget_GrTextureFlagBit;
163 desc.fWidth = width;
164 desc.fHeight = height;
jvanverth@google.comf6a90332013-05-02 12:39:37 +0000165 desc.fSampleCnt = fSampleCount;
commit-bot@chromium.orgae403b92013-04-10 17:27:30 +0000166 target.reset(fGrContext->createUncachedTexture(desc, NULL, 0));
scroggo@google.com0556ea02013-02-08 19:38:21 +0000167 }
commit-bot@chromium.orgae403b92013-04-10 17:27:30 +0000168 if (NULL == target.get()) {
scroggo@google.com0556ea02013-02-08 19:38:21 +0000169 SkASSERT(0);
170 return NULL;
171 }
172
commit-bot@chromium.orgae403b92013-04-10 17:27:30 +0000173 SkAutoTUnref<SkGpuDevice> device(SkGpuDevice::Create(target));
caryclark@google.coma3622372012-11-06 21:26:13 +0000174 canvas = SkNEW_ARGS(SkCanvas, (device.get()));
scroggo@google.com0556ea02013-02-08 19:38:21 +0000175 break;
keyar@chromium.org4ea96c52012-08-20 15:03:29 +0000176 }
177#endif
178 default:
179 SkASSERT(0);
scroggo@google.com82ec0b02012-12-17 19:25:54 +0000180 return NULL;
keyar@chromium.org4ea96c52012-08-20 15:03:29 +0000181 }
scroggo@google.com82ec0b02012-12-17 19:25:54 +0000182 setUpFilter(canvas, fDrawFilters);
183 this->scaleToScaleFactor(canvas);
commit-bot@chromium.org17cc3ea2014-01-15 14:51:25 +0000184
185 // Pictures often lie about their extent (i.e., claim to be 100x100 but
186 // only ever draw to 90x100). Clear here so the undrawn portion will have
187 // a consistent color
188 canvas->clear(SK_ColorTRANSPARENT);
scroggo@google.com82ec0b02012-12-17 19:25:54 +0000189 return canvas;
190}
keyar@chromium.orga474ce32012-08-20 15:03:57 +0000191
scroggo@google.com82ec0b02012-12-17 19:25:54 +0000192void PictureRenderer::scaleToScaleFactor(SkCanvas* canvas) {
193 SkASSERT(canvas != NULL);
194 if (fScaleFactor != SK_Scalar1) {
195 canvas->scale(fScaleFactor, fScaleFactor);
196 }
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000197}
198
199void PictureRenderer::end() {
scroggo@google.com08085f82013-01-28 20:40:24 +0000200 this->resetState(true);
junov@chromium.org9313ca42012-11-02 18:11:49 +0000201 SkSafeUnref(fPicture);
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000202 fPicture = NULL;
203 fCanvas.reset(NULL);
204}
205
scroggo@google.comc0d5e542012-12-13 21:40:48 +0000206int PictureRenderer::getViewWidth() {
207 SkASSERT(fPicture != NULL);
robertphillips@google.com8ac811e2013-02-07 00:13:34 +0000208 int width = SkScalarCeilToInt(fPicture->width() * fScaleFactor);
scroggo@google.comc0d5e542012-12-13 21:40:48 +0000209 if (fViewport.width() > 0) {
210 width = SkMin32(width, fViewport.width());
211 }
212 return width;
213}
214
215int PictureRenderer::getViewHeight() {
216 SkASSERT(fPicture != NULL);
robertphillips@google.com8ac811e2013-02-07 00:13:34 +0000217 int height = SkScalarCeilToInt(fPicture->height() * fScaleFactor);
scroggo@google.comc0d5e542012-12-13 21:40:48 +0000218 if (fViewport.height() > 0) {
219 height = SkMin32(height, fViewport.height());
220 }
221 return height;
222}
223
junov@chromium.org9313ca42012-11-02 18:11:49 +0000224/** Converts fPicture to a picture that uses a BBoxHierarchy.
225 * PictureRenderer subclasses that are used to test picture playback
226 * should call this method during init.
227 */
228void PictureRenderer::buildBBoxHierarchy() {
229 SkASSERT(NULL != fPicture);
230 if (kNone_BBoxHierarchyType != fBBoxHierarchyType && NULL != fPicture) {
231 SkPicture* newPicture = this->createPicture();
232 SkCanvas* recorder = newPicture->beginRecording(fPicture->width(), fPicture->height(),
233 this->recordFlags());
234 fPicture->draw(recorder);
235 newPicture->endRecording();
236 fPicture->unref();
237 fPicture = newPicture;
238 }
239}
240
scroggo@google.com08085f82013-01-28 20:40:24 +0000241void PictureRenderer::resetState(bool callFinish) {
keyar@chromium.org28136b32012-08-20 15:04:15 +0000242#if SK_SUPPORT_GPU
robertphillips@google.com6177e692013-02-28 20:16:25 +0000243 SkGLContextHelper* glContext = this->getGLContext();
scroggo@google.com0556ea02013-02-08 19:38:21 +0000244 if (NULL == glContext) {
245 SkASSERT(kBitmap_DeviceType == fDeviceType);
246 return;
247 }
keyar@chromium.org28136b32012-08-20 15:04:15 +0000248
scroggo@google.com0556ea02013-02-08 19:38:21 +0000249 fGrContext->flush();
commit-bot@chromium.org51c040e2014-03-11 22:58:00 +0000250 glContext->swapBuffers();
scroggo@google.com0556ea02013-02-08 19:38:21 +0000251 if (callFinish) {
252 SK_GL(*glContext, Finish());
keyar@chromium.org77a55222012-08-20 15:03:47 +0000253 }
keyar@chromium.orga40c20d2012-08-20 15:04:12 +0000254#endif
keyar@chromium.org77a55222012-08-20 15:03:47 +0000255}
256
robertphillips@google.com94d8f1e2013-12-18 17:25:33 +0000257void PictureRenderer::purgeTextures() {
258 SkDiscardableMemoryPool* pool = SkGetGlobalDiscardableMemoryPool();
259
260 pool->dumpPool();
261
262#if SK_SUPPORT_GPU
263 SkGLContextHelper* glContext = this->getGLContext();
264 if (NULL == glContext) {
265 SkASSERT(kBitmap_DeviceType == fDeviceType);
266 return;
267 }
268
269 // resetState should've already done this
270 fGrContext->flush();
271
272 fGrContext->purgeAllUnlockedResources();
273#endif
274}
275
junov@chromium.org9313ca42012-11-02 18:11:49 +0000276uint32_t PictureRenderer::recordFlags() {
junov@chromium.org100b1c52013-01-16 20:12:22 +0000277 return ((kNone_BBoxHierarchyType == fBBoxHierarchyType) ? 0 :
278 SkPicture::kOptimizeForClippedPlayback_RecordingFlag) |
279 SkPicture::kUsePathBoundsForClip_RecordingFlag;
junov@chromium.org9313ca42012-11-02 18:11:49 +0000280}
281
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000282/**
283 * Write the canvas to the specified path.
284 * @param canvas Must be non-null. Canvas to be written to a file.
285 * @param path Path for the file to be written. Should have no extension; write() will append
286 * an appropriate one. Passed in by value so it can be modified.
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +0000287 * @param jsonSummaryPtr If not null, add image results to this summary.
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000288 * @return bool True if the Canvas is written to a file.
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +0000289 *
290 * TODO(epoger): Right now, all canvases must pass through this function in order to be appended
291 * to the ImageResultsSummary. We need some way to add bitmaps to the ImageResultsSummary
292 * even if --writePath has not been specified (and thus this function is not called).
293 *
294 * One fix would be to pass in these path elements separately, and allow this function to be
295 * called even if --writePath was not specified...
296 * const char *outputDir // NULL if we don't want to write image files to disk
297 * const char *filename // name we use within JSON summary, and as the filename within outputDir
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000298 */
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +0000299static bool write(SkCanvas* canvas, const SkString* path, ImageResultsSummary *jsonSummaryPtr) {
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000300 SkASSERT(canvas != NULL);
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000301 if (NULL == canvas) {
keyar@chromium.org9299ede2012-08-21 19:05:08 +0000302 return false;
303 }
304
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +0000305 SkASSERT(path != NULL); // TODO(epoger): we want to remove this constraint, as noted above
306 SkString fullPathname(*path);
307 fullPathname.append(".png");
308
keyar@chromium.org9299ede2012-08-21 19:05:08 +0000309 SkBitmap bitmap;
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000310 SkISize size = canvas->getDeviceSize();
311 sk_tools::setup_bitmap(&bitmap, size.width(), size.height());
keyar@chromium.org9299ede2012-08-21 19:05:08 +0000312
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000313 canvas->readPixels(&bitmap, 0, 0);
keyar@chromium.org9299ede2012-08-21 19:05:08 +0000314 sk_tools::force_all_opaque(bitmap);
315
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +0000316 if (NULL != jsonSummaryPtr) {
317 // EPOGER: This is a hacky way of constructing the filename associated with the
318 // image checksum; we assume that outputDir is not NULL, and we remove outputDir
319 // from fullPathname.
320 //
321 // EPOGER: what about including the config type within hashFilename? That way,
322 // we could combine results of different config types without conflicting filenames.
323 SkString hashFilename;
324 sk_tools::get_basename(&hashFilename, fullPathname);
325 jsonSummaryPtr->add(hashFilename.c_str(), bitmap);
326 }
327
328 return SkImageEncoder::EncodeFile(fullPathname.c_str(), bitmap, SkImageEncoder::kPNG_Type, 100);
keyar@chromium.org9299ede2012-08-21 19:05:08 +0000329}
330
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000331/**
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +0000332 * If path is non NULL, append number to it, and call write() to write the
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000333 * provided canvas to a file. Returns true if path is NULL or if write() succeeds.
334 */
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +0000335static bool writeAppendNumber(SkCanvas* canvas, const SkString* path, int number,
336 ImageResultsSummary *jsonSummaryPtr) {
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000337 if (NULL == path) {
338 return true;
339 }
340 SkString pathWithNumber(*path);
341 pathWithNumber.appendf("%i", number);
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +0000342 return write(canvas, &pathWithNumber, jsonSummaryPtr);
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000343}
344
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000345///////////////////////////////////////////////////////////////////////////////////////////////
346
djsollen@google.comfd9720c2012-11-06 16:54:40 +0000347SkCanvas* RecordPictureRenderer::setupCanvas(int width, int height) {
348 // defer the canvas setup until the render step
349 return NULL;
350}
351
reed@google.com672588b2014-01-08 15:42:01 +0000352// the size_t* parameter is deprecated, so we ignore it
353static SkData* encode_bitmap_to_data(size_t*, const SkBitmap& bm) {
scroggo@google.com1b1bcc32013-05-21 20:31:23 +0000354 return SkImageEncoder::EncodeData(bm, SkImageEncoder::kPNG_Type, 100);
scroggo@google.coma9e3a362012-11-07 17:52:48 +0000355}
356
edisonn@google.com84f548c2012-12-18 22:24:03 +0000357bool RecordPictureRenderer::render(const SkString* path, SkBitmap** out) {
junov@chromium.org9313ca42012-11-02 18:11:49 +0000358 SkAutoTUnref<SkPicture> replayer(this->createPicture());
scroggo@google.com82ec0b02012-12-17 19:25:54 +0000359 SkCanvas* recorder = replayer->beginRecording(this->getViewWidth(), this->getViewHeight(),
junov@chromium.org9313ca42012-11-02 18:11:49 +0000360 this->recordFlags());
scroggo@google.com82ec0b02012-12-17 19:25:54 +0000361 this->scaleToScaleFactor(recorder);
scroggo@google.com9a412522012-09-07 15:21:18 +0000362 fPicture->draw(recorder);
junov@chromium.org9313ca42012-11-02 18:11:49 +0000363 replayer->endRecording();
scroggo@google.coma9e3a362012-11-07 17:52:48 +0000364 if (path != NULL) {
365 // Record the new picture as a new SKP with PNG encoded bitmaps.
366 SkString skpPath(*path);
367 // ".skp" was removed from 'path' before being passed in here.
368 skpPath.append(".skp");
369 SkFILEWStream stream(skpPath.c_str());
scroggo@google.com1b1bcc32013-05-21 20:31:23 +0000370 replayer->serialize(&stream, &encode_bitmap_to_data);
scroggo@google.coma9e3a362012-11-07 17:52:48 +0000371 return true;
372 }
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000373 return false;
scroggo@google.com9a412522012-09-07 15:21:18 +0000374}
375
scroggo@google.com0a049b82012-11-02 22:01:26 +0000376SkString RecordPictureRenderer::getConfigNameInternal() {
377 return SkString("record");
378}
379
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000380///////////////////////////////////////////////////////////////////////////////////////////////
381
edisonn@google.com84f548c2012-12-18 22:24:03 +0000382bool PipePictureRenderer::render(const SkString* path, SkBitmap** out) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000383 SkASSERT(fCanvas.get() != NULL);
384 SkASSERT(fPicture != NULL);
keyar@chromium.org78a35c52012-08-20 15:03:44 +0000385 if (NULL == fCanvas.get() || NULL == fPicture) {
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000386 return false;
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000387 }
388
389 PipeController pipeController(fCanvas.get());
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000390 SkGPipeWriter writer;
391 SkCanvas* pipeCanvas = writer.startRecording(&pipeController);
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000392 pipeCanvas->drawPicture(*fPicture);
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000393 writer.endRecording();
scroggo@google.com9a412522012-09-07 15:21:18 +0000394 fCanvas->flush();
borenet@google.com070d3542012-10-26 13:26:55 +0000395 if (NULL != path) {
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +0000396 return write(fCanvas, path, fJsonSummaryPtr);
borenet@google.com070d3542012-10-26 13:26:55 +0000397 }
edisonn@google.com84f548c2012-12-18 22:24:03 +0000398 if (NULL != out) {
399 *out = SkNEW(SkBitmap);
400 setup_bitmap(*out, fPicture->width(), fPicture->height());
401 fCanvas->readPixels(*out, 0, 0);
skia.committer@gmail.coma7d8e3e2012-12-19 02:01:38 +0000402 }
borenet@google.com070d3542012-10-26 13:26:55 +0000403 return true;
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000404}
405
scroggo@google.com0a049b82012-11-02 22:01:26 +0000406SkString PipePictureRenderer::getConfigNameInternal() {
407 return SkString("pipe");
408}
409
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000410///////////////////////////////////////////////////////////////////////////////////////////////
411
junov@chromium.org9313ca42012-11-02 18:11:49 +0000412void SimplePictureRenderer::init(SkPicture* picture) {
413 INHERITED::init(picture);
414 this->buildBBoxHierarchy();
415}
416
edisonn@google.com84f548c2012-12-18 22:24:03 +0000417bool SimplePictureRenderer::render(const SkString* path, SkBitmap** out) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000418 SkASSERT(fCanvas.get() != NULL);
419 SkASSERT(fPicture != NULL);
keyar@chromium.org78a35c52012-08-20 15:03:44 +0000420 if (NULL == fCanvas.get() || NULL == fPicture) {
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000421 return false;
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000422 }
423
424 fCanvas->drawPicture(*fPicture);
scroggo@google.com9a412522012-09-07 15:21:18 +0000425 fCanvas->flush();
borenet@google.com070d3542012-10-26 13:26:55 +0000426 if (NULL != path) {
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +0000427 return write(fCanvas, path, fJsonSummaryPtr);
borenet@google.com070d3542012-10-26 13:26:55 +0000428 }
skia.committer@gmail.coma7d8e3e2012-12-19 02:01:38 +0000429
edisonn@google.com84f548c2012-12-18 22:24:03 +0000430 if (NULL != out) {
431 *out = SkNEW(SkBitmap);
432 setup_bitmap(*out, fPicture->width(), fPicture->height());
433 fCanvas->readPixels(*out, 0, 0);
434 }
435
borenet@google.com070d3542012-10-26 13:26:55 +0000436 return true;
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000437}
438
scroggo@google.com0a049b82012-11-02 22:01:26 +0000439SkString SimplePictureRenderer::getConfigNameInternal() {
440 return SkString("simple");
441}
442
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000443///////////////////////////////////////////////////////////////////////////////////////////////
444
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000445TiledPictureRenderer::TiledPictureRenderer()
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000446 : fTileWidth(kDefaultTileWidth)
rileya@google.comb947b912012-08-29 17:35:07 +0000447 , fTileHeight(kDefaultTileHeight)
rileya@google.coma04dc022012-09-10 19:01:38 +0000448 , fTileWidthPercentage(0.0)
rileya@google.comb947b912012-08-29 17:35:07 +0000449 , fTileHeightPercentage(0.0)
scroggo@google.comcbcef702012-12-13 22:09:28 +0000450 , fTileMinPowerOf2Width(0)
451 , fCurrentTileOffset(-1)
452 , fTilesX(0)
453 , fTilesY(0) { }
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000454
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000455void TiledPictureRenderer::init(SkPicture* pict) {
456 SkASSERT(pict != NULL);
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000457 SkASSERT(0 == fTileRects.count());
458 if (NULL == pict || fTileRects.count() != 0) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000459 return;
460 }
461
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000462 // Do not call INHERITED::init(), which would create a (potentially large) canvas which is not
463 // used by bench_pictures.
464 fPicture = pict;
junov@chromium.org9313ca42012-11-02 18:11:49 +0000465 fPicture->ref();
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000466 this->buildBBoxHierarchy();
keyar@chromium.orgcc6e5ef2012-07-27 20:09:26 +0000467
468 if (fTileWidthPercentage > 0) {
robertphillips@google.com5d8d1862012-08-15 14:36:41 +0000469 fTileWidth = sk_float_ceil2int(float(fTileWidthPercentage * fPicture->width() / 100));
keyar@chromium.orgcc6e5ef2012-07-27 20:09:26 +0000470 }
471 if (fTileHeightPercentage > 0) {
robertphillips@google.com5d8d1862012-08-15 14:36:41 +0000472 fTileHeight = sk_float_ceil2int(float(fTileHeightPercentage * fPicture->height() / 100));
keyar@chromium.orgcc6e5ef2012-07-27 20:09:26 +0000473 }
474
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000475 if (fTileMinPowerOf2Width > 0) {
476 this->setupPowerOf2Tiles();
477 } else {
478 this->setupTiles();
479 }
scroggo@google.comcbcef702012-12-13 22:09:28 +0000480 fCanvas.reset(this->setupCanvas(fTileWidth, fTileHeight));
481 // Initialize to -1 so that the first call to nextTile will set this up to draw tile 0 on the
482 // first call to drawCurrentTile.
483 fCurrentTileOffset = -1;
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000484}
485
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000486void TiledPictureRenderer::end() {
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000487 fTileRects.reset();
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000488 this->INHERITED::end();
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000489}
490
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000491void TiledPictureRenderer::setupTiles() {
scroggo@google.comc0d5e542012-12-13 21:40:48 +0000492 // Only use enough tiles to cover the viewport
493 const int width = this->getViewWidth();
494 const int height = this->getViewHeight();
495
scroggo@google.comcbcef702012-12-13 22:09:28 +0000496 fTilesX = fTilesY = 0;
scroggo@google.comc0d5e542012-12-13 21:40:48 +0000497 for (int tile_y_start = 0; tile_y_start < height; tile_y_start += fTileHeight) {
scroggo@google.comcbcef702012-12-13 22:09:28 +0000498 fTilesY++;
scroggo@google.comc0d5e542012-12-13 21:40:48 +0000499 for (int tile_x_start = 0; tile_x_start < width; tile_x_start += fTileWidth) {
scroggo@google.comcbcef702012-12-13 22:09:28 +0000500 if (0 == tile_y_start) {
501 // Only count tiles in the X direction on the first pass.
502 fTilesX++;
503 }
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000504 *fTileRects.append() = SkRect::MakeXYWH(SkIntToScalar(tile_x_start),
505 SkIntToScalar(tile_y_start),
506 SkIntToScalar(fTileWidth),
507 SkIntToScalar(fTileHeight));
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000508 }
509 }
510}
511
scroggo@google.comcbcef702012-12-13 22:09:28 +0000512bool TiledPictureRenderer::tileDimensions(int &x, int &y) {
513 if (fTileRects.count() == 0 || NULL == fPicture) {
514 return false;
515 }
516 x = fTilesX;
517 y = fTilesY;
518 return true;
519}
520
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000521// The goal of the powers of two tiles is to minimize the amount of wasted tile
522// space in the width-wise direction and then minimize the number of tiles. The
523// constraints are that every tile must have a pixel width that is a power of
524// two and also be of some minimal width (that is also a power of two).
525//
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000526// This is solved by first taking our picture size and rounding it up to the
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000527// multiple of the minimal width. The binary representation of this rounded
528// value gives us the tiles we need: a bit of value one means we need a tile of
529// that size.
530void TiledPictureRenderer::setupPowerOf2Tiles() {
scroggo@google.comc0d5e542012-12-13 21:40:48 +0000531 // Only use enough tiles to cover the viewport
532 const int width = this->getViewWidth();
533 const int height = this->getViewHeight();
534
535 int rounded_value = width;
536 if (width % fTileMinPowerOf2Width != 0) {
537 rounded_value = width - (width % fTileMinPowerOf2Width) + fTileMinPowerOf2Width;
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000538 }
539
reed@google.come15b2f52013-12-18 04:59:26 +0000540 int num_bits = SkScalarCeilToInt(scalar_log2(SkIntToScalar(width)));
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000541 int largest_possible_tile_size = 1 << num_bits;
542
scroggo@google.comcbcef702012-12-13 22:09:28 +0000543 fTilesX = fTilesY = 0;
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000544 // The tile height is constant for a particular picture.
scroggo@google.comc0d5e542012-12-13 21:40:48 +0000545 for (int tile_y_start = 0; tile_y_start < height; tile_y_start += fTileHeight) {
scroggo@google.comcbcef702012-12-13 22:09:28 +0000546 fTilesY++;
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000547 int tile_x_start = 0;
548 int current_width = largest_possible_tile_size;
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000549 // Set fTileWidth to be the width of the widest tile, so that each canvas is large enough
550 // to draw each tile.
551 fTileWidth = current_width;
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000552
553 while (current_width >= fTileMinPowerOf2Width) {
554 // It is very important this is a bitwise AND.
555 if (current_width & rounded_value) {
scroggo@google.comcbcef702012-12-13 22:09:28 +0000556 if (0 == tile_y_start) {
557 // Only count tiles in the X direction on the first pass.
558 fTilesX++;
559 }
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000560 *fTileRects.append() = SkRect::MakeXYWH(SkIntToScalar(tile_x_start),
561 SkIntToScalar(tile_y_start),
562 SkIntToScalar(current_width),
563 SkIntToScalar(fTileHeight));
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000564 tile_x_start += current_width;
565 }
566
567 current_width >>= 1;
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000568 }
569 }
570}
571
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000572/**
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +0000573 * Draw the specified picture to the canvas translated to rectangle provided, so that this mini
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000574 * canvas represents the rectangle's portion of the overall picture.
575 * Saves and restores so that the initial clip and matrix return to their state before this function
576 * is called.
577 */
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +0000578static void draw_tile_to_canvas(SkCanvas* canvas, const SkRect& tileRect, SkPicture* picture) {
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000579 int saveCount = canvas->save();
scroggo@google.com82ec0b02012-12-17 19:25:54 +0000580 // Translate so that we draw the correct portion of the picture.
581 // Perform a postTranslate so that the scaleFactor does not interfere with the positioning.
582 SkMatrix mat(canvas->getTotalMatrix());
583 mat.postTranslate(-tileRect.fLeft, -tileRect.fTop);
584 canvas->setMatrix(mat);
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +0000585 canvas->drawPicture(*picture);
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000586 canvas->restoreToCount(saveCount);
587 canvas->flush();
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000588}
589
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000590///////////////////////////////////////////////////////////////////////////////////////////////
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000591
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +0000592/**
593 * Copies the entirety of the src bitmap (typically a tile) into a portion of the dst bitmap.
594 * If the src bitmap is too large to fit within the dst bitmap after the x and y
595 * offsets have been applied, any excess will be ignored (so only the top-left portion of the
596 * src bitmap will be copied).
597 *
598 * @param src source bitmap
599 * @param dst destination bitmap
600 * @param xOffset x-offset within destination bitmap
601 * @param yOffset y-offset within destination bitmap
602 */
603static void bitmapCopyAtOffset(const SkBitmap& src, SkBitmap* dst,
604 int xOffset, int yOffset) {
605 for (int y = 0; y <src.height() && y + yOffset < dst->height() ; y++) {
606 for (int x = 0; x < src.width() && x + xOffset < dst->width() ; x++) {
607 *dst->getAddr32(xOffset + x, yOffset + y) = *src.getAddr32(x, y);
edisonn@google.com84f548c2012-12-18 22:24:03 +0000608 }
609 }
610}
611
scroggo@google.comcbcef702012-12-13 22:09:28 +0000612bool TiledPictureRenderer::nextTile(int &i, int &j) {
613 if (++fCurrentTileOffset < fTileRects.count()) {
614 i = fCurrentTileOffset % fTilesX;
615 j = fCurrentTileOffset / fTilesX;
616 return true;
617 }
618 return false;
619}
620
621void TiledPictureRenderer::drawCurrentTile() {
622 SkASSERT(fCurrentTileOffset >= 0 && fCurrentTileOffset < fTileRects.count());
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +0000623 draw_tile_to_canvas(fCanvas, fTileRects[fCurrentTileOffset], fPicture);
scroggo@google.comcbcef702012-12-13 22:09:28 +0000624}
625
edisonn@google.com84f548c2012-12-18 22:24:03 +0000626bool TiledPictureRenderer::render(const SkString* path, SkBitmap** out) {
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000627 SkASSERT(fPicture != NULL);
628 if (NULL == fPicture) {
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000629 return false;
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000630 }
631
edisonn@google.com84f548c2012-12-18 22:24:03 +0000632 SkBitmap bitmap;
633 if (out){
634 *out = SkNEW(SkBitmap);
635 setup_bitmap(*out, fPicture->width(), fPicture->height());
636 setup_bitmap(&bitmap, fTileWidth, fTileHeight);
637 }
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000638 bool success = true;
639 for (int i = 0; i < fTileRects.count(); ++i) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +0000640 draw_tile_to_canvas(fCanvas, fTileRects[i], fPicture);
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000641 if (NULL != path) {
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +0000642 success &= writeAppendNumber(fCanvas, path, i, fJsonSummaryPtr);
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000643 }
edisonn@google.com84f548c2012-12-18 22:24:03 +0000644 if (NULL != out) {
645 if (fCanvas->readPixels(&bitmap, 0, 0)) {
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +0000646 // Add this tile to the entire bitmap.
647 bitmapCopyAtOffset(bitmap, *out, SkScalarFloorToInt(fTileRects[i].left()),
648 SkScalarFloorToInt(fTileRects[i].top()));
edisonn@google.com84f548c2012-12-18 22:24:03 +0000649 } else {
650 success = false;
651 }
652 }
keyar@chromium.org163b5672012-08-01 17:53:29 +0000653 }
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000654 return success;
keyar@chromium.org163b5672012-08-01 17:53:29 +0000655}
656
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000657SkCanvas* TiledPictureRenderer::setupCanvas(int width, int height) {
658 SkCanvas* canvas = this->INHERITED::setupCanvas(width, height);
659 SkASSERT(fPicture != NULL);
scroggo@google.com82ec0b02012-12-17 19:25:54 +0000660 // Clip the tile to an area that is completely inside both the SkPicture and the viewport. This
661 // is mostly important for tiles on the right and bottom edges as they may go over this area and
662 // the picture may have some commands that draw outside of this area and so should not actually
663 // be written.
664 // Uses a clipRegion so that it will be unaffected by the scale factor, which may have been set
665 // by INHERITED::setupCanvas.
666 SkRegion clipRegion;
667 clipRegion.setRect(0, 0, this->getViewWidth(), this->getViewHeight());
668 canvas->clipRegion(clipRegion);
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000669 return canvas;
670}
scroggo@google.com0a049b82012-11-02 22:01:26 +0000671
672SkString TiledPictureRenderer::getConfigNameInternal() {
673 SkString name;
674 if (fTileMinPowerOf2Width > 0) {
675 name.append("pow2tile_");
676 name.appendf("%i", fTileMinPowerOf2Width);
677 } else {
678 name.append("tile_");
679 if (fTileWidthPercentage > 0) {
680 name.appendf("%.f%%", fTileWidthPercentage);
681 } else {
682 name.appendf("%i", fTileWidth);
683 }
684 }
685 name.append("x");
686 if (fTileHeightPercentage > 0) {
687 name.appendf("%.f%%", fTileHeightPercentage);
688 } else {
689 name.appendf("%i", fTileHeight);
690 }
691 return name;
692}
693
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000694///////////////////////////////////////////////////////////////////////////////////////////////
695
696// Holds all of the information needed to draw a set of tiles.
697class CloneData : public SkRunnable {
698
699public:
700 CloneData(SkPicture* clone, SkCanvas* canvas, SkTDArray<SkRect>& rects, int start, int end,
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +0000701 SkRunnable* done, ImageResultsSummary* jsonSummaryPtr)
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000702 : fClone(clone)
703 , fCanvas(canvas)
704 , fPath(NULL)
705 , fRects(rects)
706 , fStart(start)
707 , fEnd(end)
708 , fSuccess(NULL)
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +0000709 , fDone(done)
710 , fJsonSummaryPtr(jsonSummaryPtr) {
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000711 SkASSERT(fDone != NULL);
712 }
713
714 virtual void run() SK_OVERRIDE {
715 SkGraphics::SetTLSFontCacheLimit(1024 * 1024);
edisonn@google.com84f548c2012-12-18 22:24:03 +0000716
717 SkBitmap bitmap;
718 if (fBitmap != NULL) {
719 // All tiles are the same size.
jvanverth@google.com9c4e5ac2013-01-07 18:41:28 +0000720 setup_bitmap(&bitmap, SkScalarFloorToInt(fRects[0].width()), SkScalarFloorToInt(fRects[0].height()));
edisonn@google.com84f548c2012-12-18 22:24:03 +0000721 }
skia.committer@gmail.coma7d8e3e2012-12-19 02:01:38 +0000722
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000723 for (int i = fStart; i < fEnd; i++) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +0000724 draw_tile_to_canvas(fCanvas, fRects[i], fClone);
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +0000725 if ((fPath != NULL) && !writeAppendNumber(fCanvas, fPath, i, fJsonSummaryPtr)
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000726 && fSuccess != NULL) {
727 *fSuccess = false;
728 // If one tile fails to write to a file, do not continue drawing the rest.
729 break;
730 }
edisonn@google.com84f548c2012-12-18 22:24:03 +0000731 if (fBitmap != NULL) {
732 if (fCanvas->readPixels(&bitmap, 0, 0)) {
733 SkAutoLockPixels alp(*fBitmap);
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +0000734 bitmapCopyAtOffset(bitmap, fBitmap, SkScalarFloorToInt(fRects[i].left()),
735 SkScalarFloorToInt(fRects[i].top()));
edisonn@google.com84f548c2012-12-18 22:24:03 +0000736 } else {
737 *fSuccess = false;
738 // If one tile fails to read pixels, do not continue drawing the rest.
739 break;
740 }
741 }
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000742 }
743 fDone->run();
744 }
745
746 void setPathAndSuccess(const SkString* path, bool* success) {
747 fPath = path;
748 fSuccess = success;
749 }
750
edisonn@google.com84f548c2012-12-18 22:24:03 +0000751 void setBitmap(SkBitmap* bitmap) {
752 fBitmap = bitmap;
753 }
754
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000755private:
756 // All pointers unowned.
757 SkPicture* fClone; // Picture to draw from. Each CloneData has a unique one which
758 // is threadsafe.
759 SkCanvas* fCanvas; // Canvas to draw to. Reused for each tile.
760 const SkString* fPath; // If non-null, path to write the result to as a PNG.
761 SkTDArray<SkRect>& fRects; // All tiles of the picture.
762 const int fStart; // Range of tiles drawn by this thread.
763 const int fEnd;
764 bool* fSuccess; // Only meaningful if path is non-null. Shared by all threads,
765 // and only set to false upon failure to write to a PNG.
766 SkRunnable* fDone;
edisonn@google.com84f548c2012-12-18 22:24:03 +0000767 SkBitmap* fBitmap;
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +0000768 ImageResultsSummary* fJsonSummaryPtr;
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000769};
770
771MultiCorePictureRenderer::MultiCorePictureRenderer(int threadCount)
772: fNumThreads(threadCount)
773, fThreadPool(threadCount)
774, fCountdown(threadCount) {
775 // Only need to create fNumThreads - 1 clones, since one thread will use the base
776 // picture.
777 fPictureClones = SkNEW_ARRAY(SkPicture, fNumThreads - 1);
778 fCloneData = SkNEW_ARRAY(CloneData*, fNumThreads);
779}
780
781void MultiCorePictureRenderer::init(SkPicture *pict) {
782 // Set fPicture and the tiles.
783 this->INHERITED::init(pict);
784 for (int i = 0; i < fNumThreads; ++i) {
785 *fCanvasPool.append() = this->setupCanvas(this->getTileWidth(), this->getTileHeight());
786 }
787 // Only need to create fNumThreads - 1 clones, since one thread will use the base picture.
788 fPicture->clone(fPictureClones, fNumThreads - 1);
789 // Populate each thread with the appropriate data.
790 // Group the tiles into nearly equal size chunks, rounding up so we're sure to cover them all.
791 const int chunkSize = (fTileRects.count() + fNumThreads - 1) / fNumThreads;
792
793 for (int i = 0; i < fNumThreads; i++) {
794 SkPicture* pic;
795 if (i == fNumThreads-1) {
796 // The last set will use the original SkPicture.
797 pic = fPicture;
798 } else {
799 pic = &fPictureClones[i];
800 }
801 const int start = i * chunkSize;
802 const int end = SkMin32(start + chunkSize, fTileRects.count());
803 fCloneData[i] = SkNEW_ARGS(CloneData,
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +0000804 (pic, fCanvasPool[i], fTileRects, start, end, &fCountdown,
805 fJsonSummaryPtr));
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000806 }
807}
808
edisonn@google.com84f548c2012-12-18 22:24:03 +0000809bool MultiCorePictureRenderer::render(const SkString *path, SkBitmap** out) {
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000810 bool success = true;
811 if (path != NULL) {
812 for (int i = 0; i < fNumThreads-1; i++) {
813 fCloneData[i]->setPathAndSuccess(path, &success);
814 }
815 }
816
edisonn@google.com84f548c2012-12-18 22:24:03 +0000817 if (NULL != out) {
818 *out = SkNEW(SkBitmap);
819 setup_bitmap(*out, fPicture->width(), fPicture->height());
820 for (int i = 0; i < fNumThreads; i++) {
821 fCloneData[i]->setBitmap(*out);
822 }
823 } else {
824 for (int i = 0; i < fNumThreads; i++) {
825 fCloneData[i]->setBitmap(NULL);
826 }
827 }
828
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000829 fCountdown.reset(fNumThreads);
830 for (int i = 0; i < fNumThreads; i++) {
831 fThreadPool.add(fCloneData[i]);
832 }
833 fCountdown.wait();
834
835 return success;
836}
837
838void MultiCorePictureRenderer::end() {
839 for (int i = 0; i < fNumThreads - 1; i++) {
840 SkDELETE(fCloneData[i]);
841 fCloneData[i] = NULL;
842 }
843
844 fCanvasPool.unrefAll();
845
846 this->INHERITED::end();
847}
848
849MultiCorePictureRenderer::~MultiCorePictureRenderer() {
850 // Each individual CloneData was deleted in end.
851 SkDELETE_ARRAY(fCloneData);
852 SkDELETE_ARRAY(fPictureClones);
853}
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000854
scroggo@google.com0a049b82012-11-02 22:01:26 +0000855SkString MultiCorePictureRenderer::getConfigNameInternal() {
856 SkString name = this->INHERITED::getConfigNameInternal();
857 name.appendf("_multi_%i_threads", fNumThreads);
858 return name;
859}
860
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000861///////////////////////////////////////////////////////////////////////////////////////////////
scroggo@google.com9a412522012-09-07 15:21:18 +0000862
863void PlaybackCreationRenderer::setup() {
junov@chromium.org9313ca42012-11-02 18:11:49 +0000864 fReplayer.reset(this->createPicture());
scroggo@google.com82ec0b02012-12-17 19:25:54 +0000865 SkCanvas* recorder = fReplayer->beginRecording(this->getViewWidth(), this->getViewHeight(),
junov@chromium.org9313ca42012-11-02 18:11:49 +0000866 this->recordFlags());
scroggo@google.com82ec0b02012-12-17 19:25:54 +0000867 this->scaleToScaleFactor(recorder);
scroggo@google.com9a412522012-09-07 15:21:18 +0000868 fPicture->draw(recorder);
869}
870
edisonn@google.com84f548c2012-12-18 22:24:03 +0000871bool PlaybackCreationRenderer::render(const SkString*, SkBitmap** out) {
junov@chromium.org9313ca42012-11-02 18:11:49 +0000872 fReplayer->endRecording();
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000873 // Since this class does not actually render, return false.
874 return false;
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000875}
876
scroggo@google.com0a049b82012-11-02 22:01:26 +0000877SkString PlaybackCreationRenderer::getConfigNameInternal() {
878 return SkString("playback_creation");
879}
880
junov@chromium.org9313ca42012-11-02 18:11:49 +0000881///////////////////////////////////////////////////////////////////////////////////////////////
882// SkPicture variants for each BBoxHierarchy type
883
884class RTreePicture : public SkPicture {
885public:
886 virtual SkBBoxHierarchy* createBBoxHierarchy() const SK_OVERRIDE{
887 static const int kRTreeMinChildren = 6;
888 static const int kRTreeMaxChildren = 11;
889 SkScalar aspectRatio = SkScalarDiv(SkIntToScalar(fWidth),
890 SkIntToScalar(fHeight));
sglez@google.com8c902122013-08-30 17:27:47 +0000891 bool sortDraws = false;
junov@chromium.org9313ca42012-11-02 18:11:49 +0000892 return SkRTree::Create(kRTreeMinChildren, kRTreeMaxChildren,
sglez@google.com8c902122013-08-30 17:27:47 +0000893 aspectRatio, sortDraws);
junov@chromium.org9313ca42012-11-02 18:11:49 +0000894 }
895};
896
897SkPicture* PictureRenderer::createPicture() {
898 switch (fBBoxHierarchyType) {
899 case kNone_BBoxHierarchyType:
900 return SkNEW(SkPicture);
commit-bot@chromium.orgc22d1392014-02-03 18:08:33 +0000901 case kQuadTree_BBoxHierarchyType:
902 return SkNEW_ARGS(SkQuadTreePicture, (SkIRect::MakeWH(fPicture->width(),
903 fPicture->height())));
junov@chromium.org9313ca42012-11-02 18:11:49 +0000904 case kRTree_BBoxHierarchyType:
905 return SkNEW(RTreePicture);
junov@chromium.org7b537062012-11-06 18:58:43 +0000906 case kTileGrid_BBoxHierarchyType:
junov@chromium.org29b19e52013-02-27 18:35:16 +0000907 return SkNEW_ARGS(SkTileGridPicture, (fPicture->width(),
908 fPicture->height(), fGridInfo));
junov@chromium.org9313ca42012-11-02 18:11:49 +0000909 }
910 SkASSERT(0); // invalid bbhType
911 return NULL;
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000912}
junov@chromium.org9313ca42012-11-02 18:11:49 +0000913
reed@google.comfe7b1ed2012-11-29 21:00:39 +0000914///////////////////////////////////////////////////////////////////////////////
915
916class GatherRenderer : public PictureRenderer {
917public:
edisonn@google.com84f548c2012-12-18 22:24:03 +0000918 virtual bool render(const SkString* path, SkBitmap** out = NULL)
919 SK_OVERRIDE {
reed@google.comfe7b1ed2012-11-29 21:00:39 +0000920 SkRect bounds = SkRect::MakeWH(SkIntToScalar(fPicture->width()),
921 SkIntToScalar(fPicture->height()));
922 SkData* data = SkPictureUtils::GatherPixelRefs(fPicture, bounds);
923 SkSafeUnref(data);
skia.committer@gmail.comc7b4be72012-12-11 02:01:20 +0000924
reed@google.comfe7b1ed2012-11-29 21:00:39 +0000925 return NULL == path; // we don't have anything to write
926 }
skia.committer@gmail.comc7b4be72012-12-11 02:01:20 +0000927
reed@google.comfe7b1ed2012-11-29 21:00:39 +0000928private:
929 virtual SkString getConfigNameInternal() SK_OVERRIDE {
930 return SkString("gather_pixelrefs");
931 }
932};
933
934PictureRenderer* CreateGatherPixelRefsRenderer() {
935 return SkNEW(GatherRenderer);
936}
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +0000937
reed@google.com5a34fd32012-12-10 16:05:09 +0000938///////////////////////////////////////////////////////////////////////////////
939
940class PictureCloneRenderer : public PictureRenderer {
941public:
edisonn@google.com84f548c2012-12-18 22:24:03 +0000942 virtual bool render(const SkString* path, SkBitmap** out = NULL)
943 SK_OVERRIDE {
reed@google.com5a34fd32012-12-10 16:05:09 +0000944 for (int i = 0; i < 100; ++i) {
945 SkPicture* clone = fPicture->clone();
946 SkSafeUnref(clone);
947 }
skia.committer@gmail.comc7b4be72012-12-11 02:01:20 +0000948
reed@google.com5a34fd32012-12-10 16:05:09 +0000949 return NULL == path; // we don't have anything to write
950 }
skia.committer@gmail.comc7b4be72012-12-11 02:01:20 +0000951
reed@google.com5a34fd32012-12-10 16:05:09 +0000952private:
953 virtual SkString getConfigNameInternal() SK_OVERRIDE {
954 return SkString("picture_clone");
955 }
956};
957
958PictureRenderer* CreatePictureCloneRenderer() {
959 return SkNEW(PictureCloneRenderer);
960}
961
junov@chromium.org9313ca42012-11-02 18:11:49 +0000962} // namespace sk_tools