blob: b5dc0fc93dad973a2d763731e46d195aeb836e92 [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"
commit-bot@chromium.org24c568c2014-04-10 15:39:02 +000025#include "SkOSFile.h"
keyar@chromium.org451bb9f2012-07-26 17:27:57 +000026#include "SkPicture.h"
robertphillips@google.com770963f2014-04-18 18:04:41 +000027#include "SkPictureRecorder.h"
scroggo@google.com1b1bcc32013-05-21 20:31:23 +000028#include "SkPictureUtils.h"
29#include "SkPixelRef.h"
keyar@chromium.orgea826952012-08-23 15:24:13 +000030#include "SkScalar.h"
scroggo@google.coma9e3a362012-11-07 17:52:48 +000031#include "SkStream.h"
keyar@chromium.org9299ede2012-08-21 19:05:08 +000032#include "SkString.h"
scroggo@google.com58b4ead2012-08-31 16:15:22 +000033#include "SkTemplates.h"
keyar@chromium.org451bb9f2012-07-26 17:27:57 +000034#include "SkTDArray.h"
scroggo@google.com58b4ead2012-08-31 16:15:22 +000035#include "SkThreadUtils.h"
keyar@chromium.org451bb9f2012-07-26 17:27:57 +000036#include "SkTypes.h"
keyar@chromium.org4ea96c52012-08-20 15:03:29 +000037
reed@google.come15b2f52013-12-18 04:59:26 +000038static inline SkScalar scalar_log2(SkScalar x) {
39 static const SkScalar log2_conversion_factor = SkScalarDiv(1, SkScalarLog(2));
skia.committer@gmail.com3b85deb2013-12-18 07:01:56 +000040
reed@google.come15b2f52013-12-18 04:59:26 +000041 return SkScalarLog(x) * log2_conversion_factor;
42}
43
keyar@chromium.org451bb9f2012-07-26 17:27:57 +000044namespace sk_tools {
45
46enum {
47 kDefaultTileWidth = 256,
48 kDefaultTileHeight = 256
49};
50
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +000051void PictureRenderer::init(SkPicture* pict, const SkString* outputDir,
52 const SkString* inputFilename, bool useChecksumBasedFilenames) {
53 this->CopyString(&fOutputDir, outputDir);
54 this->CopyString(&fInputFilename, inputFilename);
55 fUseChecksumBasedFilenames = useChecksumBasedFilenames;
56
keyar@chromium.org78a35c52012-08-20 15:03:44 +000057 SkASSERT(NULL == fPicture);
58 SkASSERT(NULL == fCanvas.get());
robertphillips@google.com84b18c72014-04-13 19:09:42 +000059 if (NULL != fPicture || NULL != fCanvas.get()) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +000060 return;
61 }
62
63 SkASSERT(pict != NULL);
keyar@chromium.org78a35c52012-08-20 15:03:44 +000064 if (NULL == pict) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +000065 return;
66 }
67
robertphillips@google.com84b18c72014-04-13 19:09:42 +000068 fPicture.reset(pict)->ref();
keyar@chromium.orga474ce32012-08-20 15:03:57 +000069 fCanvas.reset(this->setupCanvas());
70}
71
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +000072void PictureRenderer::CopyString(SkString* dest, const SkString* src) {
73 if (NULL != src) {
74 dest->set(*src);
75 } else {
76 dest->reset();
77 }
78}
79
caryclark@google.coma3622372012-11-06 21:26:13 +000080class FlagsDrawFilter : public SkDrawFilter {
81public:
82 FlagsDrawFilter(PictureRenderer::DrawFilterFlags* flags) :
83 fFlags(flags) {}
84
reed@google.com971aca72012-11-26 20:26:54 +000085 virtual bool filter(SkPaint* paint, Type t) {
caryclark@google.coma3622372012-11-06 21:26:13 +000086 paint->setFlags(paint->getFlags() & ~fFlags[t] & SkPaint::kAllFlags);
robertphillips@google.com49149312013-07-03 15:34:35 +000087 if (PictureRenderer::kMaskFilter_DrawFilterFlag & fFlags[t]) {
caryclark@google.coma3622372012-11-06 21:26:13 +000088 SkMaskFilter* maskFilter = paint->getMaskFilter();
robertphillips@google.com49149312013-07-03 15:34:35 +000089 if (NULL != maskFilter) {
reed@google.com457d8a72012-12-18 18:20:44 +000090 paint->setMaskFilter(NULL);
caryclark@google.coma3622372012-11-06 21:26:13 +000091 }
92 }
93 if (PictureRenderer::kHinting_DrawFilterFlag & fFlags[t]) {
94 paint->setHinting(SkPaint::kNo_Hinting);
95 } else if (PictureRenderer::kSlightHinting_DrawFilterFlag & fFlags[t]) {
96 paint->setHinting(SkPaint::kSlight_Hinting);
97 }
reed@google.com971aca72012-11-26 20:26:54 +000098 return true;
caryclark@google.coma3622372012-11-06 21:26:13 +000099 }
100
101private:
102 PictureRenderer::DrawFilterFlags* fFlags;
103};
104
scroggo@google.com82ec0b02012-12-17 19:25:54 +0000105static void setUpFilter(SkCanvas* canvas, PictureRenderer::DrawFilterFlags* drawFilters) {
caryclark@google.coma3622372012-11-06 21:26:13 +0000106 if (drawFilters && !canvas->getDrawFilter()) {
107 canvas->setDrawFilter(SkNEW_ARGS(FlagsDrawFilter, (drawFilters)))->unref();
caryclark@google.come3e940c2012-11-07 16:42:17 +0000108 if (drawFilters[0] & PictureRenderer::kAAClip_DrawFilterFlag) {
109 canvas->setAllowSoftClip(false);
110 }
caryclark@google.coma3622372012-11-06 21:26:13 +0000111 }
caryclark@google.coma3622372012-11-06 21:26:13 +0000112}
113
keyar@chromium.orga474ce32012-08-20 15:03:57 +0000114SkCanvas* PictureRenderer::setupCanvas() {
scroggo@google.comc0d5e542012-12-13 21:40:48 +0000115 const int width = this->getViewWidth();
116 const int height = this->getViewHeight();
117 return this->setupCanvas(width, height);
keyar@chromium.orga474ce32012-08-20 15:03:57 +0000118}
119
120SkCanvas* PictureRenderer::setupCanvas(int width, int height) {
caryclark@google.coma3622372012-11-06 21:26:13 +0000121 SkCanvas* canvas;
keyar@chromium.org4ea96c52012-08-20 15:03:29 +0000122 switch(fDeviceType) {
123 case kBitmap_DeviceType: {
124 SkBitmap bitmap;
keyar@chromium.orga474ce32012-08-20 15:03:57 +0000125 sk_tools::setup_bitmap(&bitmap, width, height);
caryclark@google.coma3622372012-11-06 21:26:13 +0000126 canvas = SkNEW_ARGS(SkCanvas, (bitmap));
keyar@chromium.org4ea96c52012-08-20 15:03:29 +0000127 }
scroggo@google.com82ec0b02012-12-17 19:25:54 +0000128 break;
keyar@chromium.org4ea96c52012-08-20 15:03:29 +0000129#if SK_SUPPORT_GPU
scroggo@google.com0556ea02013-02-08 19:38:21 +0000130#if SK_ANGLE
131 case kAngle_DeviceType:
132 // fall through
133#endif
rmistry@google.com6ab96732014-01-06 18:37:24 +0000134#if SK_MESA
135 case kMesa_DeviceType:
136 // fall through
137#endif
commit-bot@chromium.org0fd52702014-03-07 18:41:14 +0000138 case kGPU_DeviceType:
139 case kNVPR_DeviceType: {
commit-bot@chromium.orgae403b92013-04-10 17:27:30 +0000140 SkAutoTUnref<GrSurface> target;
scroggo@google.com0556ea02013-02-08 19:38:21 +0000141 if (fGrContext) {
142 // create a render target to back the device
143 GrTextureDesc desc;
144 desc.fConfig = kSkia8888_GrPixelConfig;
145 desc.fFlags = kRenderTarget_GrTextureFlagBit;
146 desc.fWidth = width;
147 desc.fHeight = height;
jvanverth@google.comf6a90332013-05-02 12:39:37 +0000148 desc.fSampleCnt = fSampleCount;
commit-bot@chromium.orgae403b92013-04-10 17:27:30 +0000149 target.reset(fGrContext->createUncachedTexture(desc, NULL, 0));
scroggo@google.com0556ea02013-02-08 19:38:21 +0000150 }
commit-bot@chromium.orgae403b92013-04-10 17:27:30 +0000151 if (NULL == target.get()) {
scroggo@google.com0556ea02013-02-08 19:38:21 +0000152 SkASSERT(0);
153 return NULL;
154 }
155
commit-bot@chromium.orgae403b92013-04-10 17:27:30 +0000156 SkAutoTUnref<SkGpuDevice> device(SkGpuDevice::Create(target));
caryclark@google.coma3622372012-11-06 21:26:13 +0000157 canvas = SkNEW_ARGS(SkCanvas, (device.get()));
scroggo@google.com0556ea02013-02-08 19:38:21 +0000158 break;
keyar@chromium.org4ea96c52012-08-20 15:03:29 +0000159 }
160#endif
161 default:
162 SkASSERT(0);
scroggo@google.com82ec0b02012-12-17 19:25:54 +0000163 return NULL;
keyar@chromium.org4ea96c52012-08-20 15:03:29 +0000164 }
scroggo@google.com82ec0b02012-12-17 19:25:54 +0000165 setUpFilter(canvas, fDrawFilters);
166 this->scaleToScaleFactor(canvas);
commit-bot@chromium.org17cc3ea2014-01-15 14:51:25 +0000167
168 // Pictures often lie about their extent (i.e., claim to be 100x100 but
169 // only ever draw to 90x100). Clear here so the undrawn portion will have
170 // a consistent color
171 canvas->clear(SK_ColorTRANSPARENT);
scroggo@google.com82ec0b02012-12-17 19:25:54 +0000172 return canvas;
173}
keyar@chromium.orga474ce32012-08-20 15:03:57 +0000174
scroggo@google.com82ec0b02012-12-17 19:25:54 +0000175void PictureRenderer::scaleToScaleFactor(SkCanvas* canvas) {
176 SkASSERT(canvas != NULL);
177 if (fScaleFactor != SK_Scalar1) {
178 canvas->scale(fScaleFactor, fScaleFactor);
179 }
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000180}
181
182void PictureRenderer::end() {
scroggo@google.com08085f82013-01-28 20:40:24 +0000183 this->resetState(true);
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000184 fPicture.reset(NULL);
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000185 fCanvas.reset(NULL);
186}
187
scroggo@google.comc0d5e542012-12-13 21:40:48 +0000188int PictureRenderer::getViewWidth() {
189 SkASSERT(fPicture != NULL);
robertphillips@google.com8ac811e2013-02-07 00:13:34 +0000190 int width = SkScalarCeilToInt(fPicture->width() * fScaleFactor);
scroggo@google.comc0d5e542012-12-13 21:40:48 +0000191 if (fViewport.width() > 0) {
192 width = SkMin32(width, fViewport.width());
193 }
194 return width;
195}
196
197int PictureRenderer::getViewHeight() {
198 SkASSERT(fPicture != NULL);
robertphillips@google.com8ac811e2013-02-07 00:13:34 +0000199 int height = SkScalarCeilToInt(fPicture->height() * fScaleFactor);
scroggo@google.comc0d5e542012-12-13 21:40:48 +0000200 if (fViewport.height() > 0) {
201 height = SkMin32(height, fViewport.height());
202 }
203 return height;
204}
205
junov@chromium.org9313ca42012-11-02 18:11:49 +0000206/** Converts fPicture to a picture that uses a BBoxHierarchy.
207 * PictureRenderer subclasses that are used to test picture playback
208 * should call this method during init.
209 */
210void PictureRenderer::buildBBoxHierarchy() {
211 SkASSERT(NULL != fPicture);
212 if (kNone_BBoxHierarchyType != fBBoxHierarchyType && NULL != fPicture) {
commit-bot@chromium.org5fb2ce32014-04-17 23:35:06 +0000213 SkAutoTDelete<SkBBHFactory> factory(this->getFactory());
214 SkPictureRecorder recorder;
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000215 SkCanvas* canvas = recorder.beginRecording(fPicture->width(), fPicture->height(),
commit-bot@chromium.org5fb2ce32014-04-17 23:35:06 +0000216 factory.get(),
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000217 this->recordFlags());
218 fPicture->draw(canvas);
219 fPicture.reset(recorder.endRecording());
junov@chromium.org9313ca42012-11-02 18:11:49 +0000220 }
221}
222
scroggo@google.com08085f82013-01-28 20:40:24 +0000223void PictureRenderer::resetState(bool callFinish) {
keyar@chromium.org28136b32012-08-20 15:04:15 +0000224#if SK_SUPPORT_GPU
robertphillips@google.com6177e692013-02-28 20:16:25 +0000225 SkGLContextHelper* glContext = this->getGLContext();
scroggo@google.com0556ea02013-02-08 19:38:21 +0000226 if (NULL == glContext) {
227 SkASSERT(kBitmap_DeviceType == fDeviceType);
228 return;
229 }
keyar@chromium.org28136b32012-08-20 15:04:15 +0000230
scroggo@google.com0556ea02013-02-08 19:38:21 +0000231 fGrContext->flush();
commit-bot@chromium.org51c040e2014-03-11 22:58:00 +0000232 glContext->swapBuffers();
scroggo@google.com0556ea02013-02-08 19:38:21 +0000233 if (callFinish) {
234 SK_GL(*glContext, Finish());
keyar@chromium.org77a55222012-08-20 15:03:47 +0000235 }
keyar@chromium.orga40c20d2012-08-20 15:04:12 +0000236#endif
keyar@chromium.org77a55222012-08-20 15:03:47 +0000237}
238
robertphillips@google.com94d8f1e2013-12-18 17:25:33 +0000239void PictureRenderer::purgeTextures() {
240 SkDiscardableMemoryPool* pool = SkGetGlobalDiscardableMemoryPool();
241
242 pool->dumpPool();
243
244#if SK_SUPPORT_GPU
245 SkGLContextHelper* glContext = this->getGLContext();
246 if (NULL == glContext) {
247 SkASSERT(kBitmap_DeviceType == fDeviceType);
248 return;
249 }
250
251 // resetState should've already done this
252 fGrContext->flush();
253
254 fGrContext->purgeAllUnlockedResources();
255#endif
256}
257
junov@chromium.org9313ca42012-11-02 18:11:49 +0000258uint32_t PictureRenderer::recordFlags() {
skia.committer@gmail.com667b98d2014-04-17 03:05:10 +0000259 return (kNone_BBoxHierarchyType == fBBoxHierarchyType)
commit-bot@chromium.orgd393b172014-04-16 16:02:10 +0000260 ? 0
261 : SkPicture::kUsePathBoundsForClip_RecordingFlag;
junov@chromium.org9313ca42012-11-02 18:11:49 +0000262}
263
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000264/**
commit-bot@chromium.org4610a462014-04-29 19:39:22 +0000265 * Write the canvas to an image file and/or JSON summary.
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000266 *
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000267 * @param canvas Must be non-null. Canvas to be written to a file.
commit-bot@chromium.org4610a462014-04-29 19:39:22 +0000268 * @param outputDir If nonempty, write the binary image to a file within this directory;
269 * if empty, don't write out the image at all.
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000270 * @param inputFilename If we are writing out a binary image, use this to build its filename.
commit-bot@chromium.org4610a462014-04-29 19:39:22 +0000271 * @param jsonSummaryPtr If not null, add image results (checksum) to this summary.
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000272 * @param useChecksumBasedFilenames If true, use checksum-based filenames when writing to disk.
commit-bot@chromium.org24c568c2014-04-10 15:39:02 +0000273 * @param tileNumberPtr If not null, which tile number this image contains.
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +0000274 *
commit-bot@chromium.org4610a462014-04-29 19:39:22 +0000275 * @return bool True if the operation completed successfully.
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000276 */
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000277static bool write(SkCanvas* canvas, const SkString& outputDir, const SkString& inputFilename,
278 ImageResultsSummary *jsonSummaryPtr, bool useChecksumBasedFilenames,
commit-bot@chromium.org24c568c2014-04-10 15:39:02 +0000279 const int* tileNumberPtr=NULL) {
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000280 SkASSERT(canvas != NULL);
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000281 if (NULL == canvas) {
keyar@chromium.org9299ede2012-08-21 19:05:08 +0000282 return false;
283 }
284
285 SkBitmap bitmap;
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000286 SkISize size = canvas->getDeviceSize();
287 sk_tools::setup_bitmap(&bitmap, size.width(), size.height());
keyar@chromium.org9299ede2012-08-21 19:05:08 +0000288
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000289 // Make sure we only compute the bitmap hash once (at most).
290 uint64_t hash;
291 bool generatedHash = false;
292
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000293 canvas->readPixels(&bitmap, 0, 0);
keyar@chromium.org9299ede2012-08-21 19:05:08 +0000294 sk_tools::force_all_opaque(bitmap);
295
commit-bot@chromium.org24c568c2014-04-10 15:39:02 +0000296 SkString escapedInputFilename(inputFilename);
297 replace_char(&escapedInputFilename, '.', '_');
298
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000299 // TODO(epoger): what about including the config type within outputFilename? That way,
300 // we could combine results of different config types without conflicting filenames.
commit-bot@chromium.org24c568c2014-04-10 15:39:02 +0000301 SkString outputFilename;
302 const char *outputSubdirPtr = NULL;
303 if (useChecksumBasedFilenames) {
commit-bot@chromium.orgb470c212014-03-19 22:58:52 +0000304 SkASSERT(!generatedHash);
305 SkAssertResult(SkBitmapHasher::ComputeDigest(bitmap, &hash));
306 generatedHash = true;
307
commit-bot@chromium.org24c568c2014-04-10 15:39:02 +0000308 outputSubdirPtr = escapedInputFilename.c_str();
commit-bot@chromium.org90c0fbd2014-05-09 03:18:41 +0000309 // TODO(epoger): The string constant below will be removed when I land
310 // the second part of https://codereview.chromium.org/261313004/
311 // ('add --readJsonSummaryPath to render_pictures')
312 outputFilename.set("bitmap-64bitMD5_");
commit-bot@chromium.org24c568c2014-04-10 15:39:02 +0000313 outputFilename.appendU64(hash);
314 } else {
315 outputFilename.set(escapedInputFilename);
316 if (NULL != tileNumberPtr) {
317 outputFilename.append("-tile");
318 outputFilename.appendS32(*tileNumberPtr);
319 }
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +0000320 }
commit-bot@chromium.org24c568c2014-04-10 15:39:02 +0000321 outputFilename.append(".png");
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +0000322
commit-bot@chromium.org24c568c2014-04-10 15:39:02 +0000323 if (NULL != jsonSummaryPtr) {
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000324 if (!generatedHash) {
325 SkAssertResult(SkBitmapHasher::ComputeDigest(bitmap, &hash));
326 generatedHash = true;
327 }
commit-bot@chromium.org24c568c2014-04-10 15:39:02 +0000328
329 SkString outputRelativePath;
330 if (outputSubdirPtr) {
331 outputRelativePath.set(outputSubdirPtr);
332 outputRelativePath.append("/"); // always use "/", even on Windows
333 outputRelativePath.append(outputFilename);
334 } else {
335 outputRelativePath.set(outputFilename);
336 }
337
338 jsonSummaryPtr->add(inputFilename.c_str(), outputRelativePath.c_str(),
339 hash, tileNumberPtr);
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000340 }
341
commit-bot@chromium.org4610a462014-04-29 19:39:22 +0000342 if (outputDir.isEmpty()) {
343 return true;
344 }
345
commit-bot@chromium.org24c568c2014-04-10 15:39:02 +0000346 SkString dirPath;
347 if (outputSubdirPtr) {
348 dirPath = SkOSPath::SkPathJoin(outputDir.c_str(), outputSubdirPtr);
349 sk_mkdir(dirPath.c_str());
350 } else {
351 dirPath.set(outputDir);
352 }
353 SkString fullPath = SkOSPath::SkPathJoin(dirPath.c_str(), outputFilename.c_str());
354 return SkImageEncoder::EncodeFile(fullPath.c_str(), bitmap, SkImageEncoder::kPNG_Type, 100);
keyar@chromium.org9299ede2012-08-21 19:05:08 +0000355}
356
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000357///////////////////////////////////////////////////////////////////////////////////////////////
358
djsollen@google.comfd9720c2012-11-06 16:54:40 +0000359SkCanvas* RecordPictureRenderer::setupCanvas(int width, int height) {
360 // defer the canvas setup until the render step
361 return NULL;
362}
363
reed@google.com672588b2014-01-08 15:42:01 +0000364// the size_t* parameter is deprecated, so we ignore it
365static SkData* encode_bitmap_to_data(size_t*, const SkBitmap& bm) {
scroggo@google.com1b1bcc32013-05-21 20:31:23 +0000366 return SkImageEncoder::EncodeData(bm, SkImageEncoder::kPNG_Type, 100);
scroggo@google.coma9e3a362012-11-07 17:52:48 +0000367}
368
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000369bool RecordPictureRenderer::render(SkBitmap** out) {
commit-bot@chromium.org5fb2ce32014-04-17 23:35:06 +0000370 SkAutoTDelete<SkBBHFactory> factory(this->getFactory());
371 SkPictureRecorder recorder;
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000372 SkCanvas* canvas = recorder.beginRecording(this->getViewWidth(), this->getViewHeight(),
commit-bot@chromium.org5fb2ce32014-04-17 23:35:06 +0000373 factory.get(),
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000374 this->recordFlags());
375 this->scaleToScaleFactor(canvas);
376 fPicture->draw(canvas);
377 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000378 if (!fOutputDir.isEmpty()) {
scroggo@google.coma9e3a362012-11-07 17:52:48 +0000379 // Record the new picture as a new SKP with PNG encoded bitmaps.
commit-bot@chromium.org24c568c2014-04-10 15:39:02 +0000380 SkString skpPath = SkOSPath::SkPathJoin(fOutputDir.c_str(), fInputFilename.c_str());
scroggo@google.coma9e3a362012-11-07 17:52:48 +0000381 SkFILEWStream stream(skpPath.c_str());
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000382 picture->serialize(&stream, &encode_bitmap_to_data);
scroggo@google.coma9e3a362012-11-07 17:52:48 +0000383 return true;
384 }
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000385 return false;
scroggo@google.com9a412522012-09-07 15:21:18 +0000386}
387
scroggo@google.com0a049b82012-11-02 22:01:26 +0000388SkString RecordPictureRenderer::getConfigNameInternal() {
389 return SkString("record");
390}
391
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000392///////////////////////////////////////////////////////////////////////////////////////////////
393
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000394bool PipePictureRenderer::render(SkBitmap** out) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000395 SkASSERT(fCanvas.get() != NULL);
396 SkASSERT(fPicture != NULL);
keyar@chromium.org78a35c52012-08-20 15:03:44 +0000397 if (NULL == fCanvas.get() || NULL == fPicture) {
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000398 return false;
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000399 }
400
401 PipeController pipeController(fCanvas.get());
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000402 SkGPipeWriter writer;
403 SkCanvas* pipeCanvas = writer.startRecording(&pipeController);
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000404 pipeCanvas->drawPicture(*fPicture);
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000405 writer.endRecording();
scroggo@google.com9a412522012-09-07 15:21:18 +0000406 fCanvas->flush();
edisonn@google.com84f548c2012-12-18 22:24:03 +0000407 if (NULL != out) {
408 *out = SkNEW(SkBitmap);
409 setup_bitmap(*out, fPicture->width(), fPicture->height());
410 fCanvas->readPixels(*out, 0, 0);
skia.committer@gmail.coma7d8e3e2012-12-19 02:01:38 +0000411 }
commit-bot@chromium.org4610a462014-04-29 19:39:22 +0000412 return write(fCanvas, fOutputDir, fInputFilename, fJsonSummaryPtr,
413 fUseChecksumBasedFilenames);
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000414}
415
scroggo@google.com0a049b82012-11-02 22:01:26 +0000416SkString PipePictureRenderer::getConfigNameInternal() {
417 return SkString("pipe");
418}
419
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000420///////////////////////////////////////////////////////////////////////////////////////////////
421
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000422void SimplePictureRenderer::init(SkPicture* picture, const SkString* outputDir,
423 const SkString* inputFilename, bool useChecksumBasedFilenames) {
424 INHERITED::init(picture, outputDir, inputFilename, useChecksumBasedFilenames);
junov@chromium.org9313ca42012-11-02 18:11:49 +0000425 this->buildBBoxHierarchy();
426}
427
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000428bool SimplePictureRenderer::render(SkBitmap** out) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000429 SkASSERT(fCanvas.get() != NULL);
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000430 SkASSERT(NULL != fPicture);
keyar@chromium.org78a35c52012-08-20 15:03:44 +0000431 if (NULL == fCanvas.get() || NULL == fPicture) {
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000432 return false;
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000433 }
434
435 fCanvas->drawPicture(*fPicture);
scroggo@google.com9a412522012-09-07 15:21:18 +0000436 fCanvas->flush();
edisonn@google.com84f548c2012-12-18 22:24:03 +0000437 if (NULL != out) {
438 *out = SkNEW(SkBitmap);
439 setup_bitmap(*out, fPicture->width(), fPicture->height());
440 fCanvas->readPixels(*out, 0, 0);
441 }
commit-bot@chromium.org4610a462014-04-29 19:39:22 +0000442 return write(fCanvas, fOutputDir, fInputFilename, fJsonSummaryPtr,
443 fUseChecksumBasedFilenames);
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000444}
445
scroggo@google.com0a049b82012-11-02 22:01:26 +0000446SkString SimplePictureRenderer::getConfigNameInternal() {
447 return SkString("simple");
448}
449
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000450///////////////////////////////////////////////////////////////////////////////////////////////
451
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000452TiledPictureRenderer::TiledPictureRenderer()
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000453 : fTileWidth(kDefaultTileWidth)
rileya@google.comb947b912012-08-29 17:35:07 +0000454 , fTileHeight(kDefaultTileHeight)
rileya@google.coma04dc022012-09-10 19:01:38 +0000455 , fTileWidthPercentage(0.0)
rileya@google.comb947b912012-08-29 17:35:07 +0000456 , fTileHeightPercentage(0.0)
scroggo@google.comcbcef702012-12-13 22:09:28 +0000457 , fTileMinPowerOf2Width(0)
458 , fCurrentTileOffset(-1)
459 , fTilesX(0)
460 , fTilesY(0) { }
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000461
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000462void TiledPictureRenderer::init(SkPicture* pict, const SkString* outputDir,
463 const SkString* inputFilename, bool useChecksumBasedFilenames) {
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000464 SkASSERT(NULL != pict);
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000465 SkASSERT(0 == fTileRects.count());
466 if (NULL == pict || fTileRects.count() != 0) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000467 return;
468 }
469
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000470 // Do not call INHERITED::init(), which would create a (potentially large) canvas which is not
471 // used by bench_pictures.
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000472 fPicture.reset(pict)->ref();
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000473 this->CopyString(&fOutputDir, outputDir);
474 this->CopyString(&fInputFilename, inputFilename);
475 fUseChecksumBasedFilenames = useChecksumBasedFilenames;
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000476 this->buildBBoxHierarchy();
keyar@chromium.orgcc6e5ef2012-07-27 20:09:26 +0000477
478 if (fTileWidthPercentage > 0) {
robertphillips@google.com5d8d1862012-08-15 14:36:41 +0000479 fTileWidth = sk_float_ceil2int(float(fTileWidthPercentage * fPicture->width() / 100));
keyar@chromium.orgcc6e5ef2012-07-27 20:09:26 +0000480 }
481 if (fTileHeightPercentage > 0) {
robertphillips@google.com5d8d1862012-08-15 14:36:41 +0000482 fTileHeight = sk_float_ceil2int(float(fTileHeightPercentage * fPicture->height() / 100));
keyar@chromium.orgcc6e5ef2012-07-27 20:09:26 +0000483 }
484
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000485 if (fTileMinPowerOf2Width > 0) {
486 this->setupPowerOf2Tiles();
487 } else {
488 this->setupTiles();
489 }
scroggo@google.comcbcef702012-12-13 22:09:28 +0000490 fCanvas.reset(this->setupCanvas(fTileWidth, fTileHeight));
491 // Initialize to -1 so that the first call to nextTile will set this up to draw tile 0 on the
492 // first call to drawCurrentTile.
493 fCurrentTileOffset = -1;
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000494}
495
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000496void TiledPictureRenderer::end() {
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000497 fTileRects.reset();
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000498 this->INHERITED::end();
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000499}
500
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000501void TiledPictureRenderer::setupTiles() {
scroggo@google.comc0d5e542012-12-13 21:40:48 +0000502 // Only use enough tiles to cover the viewport
503 const int width = this->getViewWidth();
504 const int height = this->getViewHeight();
505
scroggo@google.comcbcef702012-12-13 22:09:28 +0000506 fTilesX = fTilesY = 0;
scroggo@google.comc0d5e542012-12-13 21:40:48 +0000507 for (int tile_y_start = 0; tile_y_start < height; tile_y_start += fTileHeight) {
scroggo@google.comcbcef702012-12-13 22:09:28 +0000508 fTilesY++;
scroggo@google.comc0d5e542012-12-13 21:40:48 +0000509 for (int tile_x_start = 0; tile_x_start < width; tile_x_start += fTileWidth) {
scroggo@google.comcbcef702012-12-13 22:09:28 +0000510 if (0 == tile_y_start) {
511 // Only count tiles in the X direction on the first pass.
512 fTilesX++;
513 }
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000514 *fTileRects.append() = SkRect::MakeXYWH(SkIntToScalar(tile_x_start),
515 SkIntToScalar(tile_y_start),
516 SkIntToScalar(fTileWidth),
517 SkIntToScalar(fTileHeight));
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000518 }
519 }
520}
521
scroggo@google.comcbcef702012-12-13 22:09:28 +0000522bool TiledPictureRenderer::tileDimensions(int &x, int &y) {
523 if (fTileRects.count() == 0 || NULL == fPicture) {
524 return false;
525 }
526 x = fTilesX;
527 y = fTilesY;
528 return true;
529}
530
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000531// The goal of the powers of two tiles is to minimize the amount of wasted tile
532// space in the width-wise direction and then minimize the number of tiles. The
533// constraints are that every tile must have a pixel width that is a power of
534// two and also be of some minimal width (that is also a power of two).
535//
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000536// This is solved by first taking our picture size and rounding it up to the
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000537// multiple of the minimal width. The binary representation of this rounded
538// value gives us the tiles we need: a bit of value one means we need a tile of
539// that size.
540void TiledPictureRenderer::setupPowerOf2Tiles() {
scroggo@google.comc0d5e542012-12-13 21:40:48 +0000541 // Only use enough tiles to cover the viewport
542 const int width = this->getViewWidth();
543 const int height = this->getViewHeight();
544
545 int rounded_value = width;
546 if (width % fTileMinPowerOf2Width != 0) {
547 rounded_value = width - (width % fTileMinPowerOf2Width) + fTileMinPowerOf2Width;
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000548 }
549
reed@google.come15b2f52013-12-18 04:59:26 +0000550 int num_bits = SkScalarCeilToInt(scalar_log2(SkIntToScalar(width)));
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000551 int largest_possible_tile_size = 1 << num_bits;
552
scroggo@google.comcbcef702012-12-13 22:09:28 +0000553 fTilesX = fTilesY = 0;
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000554 // The tile height is constant for a particular picture.
scroggo@google.comc0d5e542012-12-13 21:40:48 +0000555 for (int tile_y_start = 0; tile_y_start < height; tile_y_start += fTileHeight) {
scroggo@google.comcbcef702012-12-13 22:09:28 +0000556 fTilesY++;
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000557 int tile_x_start = 0;
558 int current_width = largest_possible_tile_size;
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000559 // Set fTileWidth to be the width of the widest tile, so that each canvas is large enough
560 // to draw each tile.
561 fTileWidth = current_width;
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000562
563 while (current_width >= fTileMinPowerOf2Width) {
564 // It is very important this is a bitwise AND.
565 if (current_width & rounded_value) {
scroggo@google.comcbcef702012-12-13 22:09:28 +0000566 if (0 == tile_y_start) {
567 // Only count tiles in the X direction on the first pass.
568 fTilesX++;
569 }
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000570 *fTileRects.append() = SkRect::MakeXYWH(SkIntToScalar(tile_x_start),
571 SkIntToScalar(tile_y_start),
572 SkIntToScalar(current_width),
573 SkIntToScalar(fTileHeight));
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000574 tile_x_start += current_width;
575 }
576
577 current_width >>= 1;
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000578 }
579 }
580}
581
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000582/**
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +0000583 * Draw the specified picture to the canvas translated to rectangle provided, so that this mini
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000584 * canvas represents the rectangle's portion of the overall picture.
585 * Saves and restores so that the initial clip and matrix return to their state before this function
586 * is called.
587 */
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +0000588static void draw_tile_to_canvas(SkCanvas* canvas, const SkRect& tileRect, SkPicture* picture) {
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000589 int saveCount = canvas->save();
scroggo@google.com82ec0b02012-12-17 19:25:54 +0000590 // Translate so that we draw the correct portion of the picture.
591 // Perform a postTranslate so that the scaleFactor does not interfere with the positioning.
592 SkMatrix mat(canvas->getTotalMatrix());
593 mat.postTranslate(-tileRect.fLeft, -tileRect.fTop);
594 canvas->setMatrix(mat);
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +0000595 canvas->drawPicture(*picture);
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000596 canvas->restoreToCount(saveCount);
597 canvas->flush();
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000598}
599
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000600///////////////////////////////////////////////////////////////////////////////////////////////
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000601
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +0000602/**
603 * Copies the entirety of the src bitmap (typically a tile) into a portion of the dst bitmap.
604 * If the src bitmap is too large to fit within the dst bitmap after the x and y
605 * offsets have been applied, any excess will be ignored (so only the top-left portion of the
606 * src bitmap will be copied).
607 *
608 * @param src source bitmap
609 * @param dst destination bitmap
610 * @param xOffset x-offset within destination bitmap
611 * @param yOffset y-offset within destination bitmap
612 */
613static void bitmapCopyAtOffset(const SkBitmap& src, SkBitmap* dst,
614 int xOffset, int yOffset) {
615 for (int y = 0; y <src.height() && y + yOffset < dst->height() ; y++) {
616 for (int x = 0; x < src.width() && x + xOffset < dst->width() ; x++) {
617 *dst->getAddr32(xOffset + x, yOffset + y) = *src.getAddr32(x, y);
edisonn@google.com84f548c2012-12-18 22:24:03 +0000618 }
619 }
620}
621
scroggo@google.comcbcef702012-12-13 22:09:28 +0000622bool TiledPictureRenderer::nextTile(int &i, int &j) {
623 if (++fCurrentTileOffset < fTileRects.count()) {
624 i = fCurrentTileOffset % fTilesX;
625 j = fCurrentTileOffset / fTilesX;
626 return true;
627 }
628 return false;
629}
630
631void TiledPictureRenderer::drawCurrentTile() {
632 SkASSERT(fCurrentTileOffset >= 0 && fCurrentTileOffset < fTileRects.count());
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +0000633 draw_tile_to_canvas(fCanvas, fTileRects[fCurrentTileOffset], fPicture);
scroggo@google.comcbcef702012-12-13 22:09:28 +0000634}
635
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000636bool TiledPictureRenderer::render(SkBitmap** out) {
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000637 SkASSERT(fPicture != NULL);
638 if (NULL == fPicture) {
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000639 return false;
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000640 }
641
edisonn@google.com84f548c2012-12-18 22:24:03 +0000642 SkBitmap bitmap;
643 if (out){
644 *out = SkNEW(SkBitmap);
645 setup_bitmap(*out, fPicture->width(), fPicture->height());
646 setup_bitmap(&bitmap, fTileWidth, fTileHeight);
647 }
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000648 bool success = true;
649 for (int i = 0; i < fTileRects.count(); ++i) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +0000650 draw_tile_to_canvas(fCanvas, fTileRects[i], fPicture);
commit-bot@chromium.org4610a462014-04-29 19:39:22 +0000651 success &= write(fCanvas, fOutputDir, fInputFilename, fJsonSummaryPtr,
652 fUseChecksumBasedFilenames, &i);
edisonn@google.com84f548c2012-12-18 22:24:03 +0000653 if (NULL != out) {
654 if (fCanvas->readPixels(&bitmap, 0, 0)) {
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +0000655 // Add this tile to the entire bitmap.
656 bitmapCopyAtOffset(bitmap, *out, SkScalarFloorToInt(fTileRects[i].left()),
657 SkScalarFloorToInt(fTileRects[i].top()));
edisonn@google.com84f548c2012-12-18 22:24:03 +0000658 } else {
659 success = false;
660 }
661 }
keyar@chromium.org163b5672012-08-01 17:53:29 +0000662 }
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000663 return success;
keyar@chromium.org163b5672012-08-01 17:53:29 +0000664}
665
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000666SkCanvas* TiledPictureRenderer::setupCanvas(int width, int height) {
667 SkCanvas* canvas = this->INHERITED::setupCanvas(width, height);
commit-bot@chromium.org8ddc26b2014-03-31 17:55:12 +0000668 SkASSERT(NULL != fPicture);
scroggo@google.com82ec0b02012-12-17 19:25:54 +0000669 // Clip the tile to an area that is completely inside both the SkPicture and the viewport. This
670 // is mostly important for tiles on the right and bottom edges as they may go over this area and
671 // the picture may have some commands that draw outside of this area and so should not actually
672 // be written.
673 // Uses a clipRegion so that it will be unaffected by the scale factor, which may have been set
674 // by INHERITED::setupCanvas.
675 SkRegion clipRegion;
676 clipRegion.setRect(0, 0, this->getViewWidth(), this->getViewHeight());
677 canvas->clipRegion(clipRegion);
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000678 return canvas;
679}
scroggo@google.com0a049b82012-11-02 22:01:26 +0000680
681SkString TiledPictureRenderer::getConfigNameInternal() {
682 SkString name;
683 if (fTileMinPowerOf2Width > 0) {
684 name.append("pow2tile_");
685 name.appendf("%i", fTileMinPowerOf2Width);
686 } else {
687 name.append("tile_");
688 if (fTileWidthPercentage > 0) {
689 name.appendf("%.f%%", fTileWidthPercentage);
690 } else {
691 name.appendf("%i", fTileWidth);
692 }
693 }
694 name.append("x");
695 if (fTileHeightPercentage > 0) {
696 name.appendf("%.f%%", fTileHeightPercentage);
697 } else {
698 name.appendf("%i", fTileHeight);
699 }
700 return name;
701}
702
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000703///////////////////////////////////////////////////////////////////////////////////////////////
704
705// Holds all of the information needed to draw a set of tiles.
706class CloneData : public SkRunnable {
707
708public:
709 CloneData(SkPicture* clone, SkCanvas* canvas, SkTDArray<SkRect>& rects, int start, int end,
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000710 SkRunnable* done, ImageResultsSummary* jsonSummaryPtr, bool useChecksumBasedFilenames)
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000711 : fClone(clone)
712 , fCanvas(canvas)
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000713 , fRects(rects)
714 , fStart(start)
715 , fEnd(end)
716 , fSuccess(NULL)
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +0000717 , fDone(done)
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000718 , fJsonSummaryPtr(jsonSummaryPtr)
719 , fUseChecksumBasedFilenames(useChecksumBasedFilenames) {
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000720 SkASSERT(fDone != NULL);
721 }
722
723 virtual void run() SK_OVERRIDE {
724 SkGraphics::SetTLSFontCacheLimit(1024 * 1024);
edisonn@google.com84f548c2012-12-18 22:24:03 +0000725
726 SkBitmap bitmap;
727 if (fBitmap != NULL) {
728 // All tiles are the same size.
jvanverth@google.com9c4e5ac2013-01-07 18:41:28 +0000729 setup_bitmap(&bitmap, SkScalarFloorToInt(fRects[0].width()), SkScalarFloorToInt(fRects[0].height()));
edisonn@google.com84f548c2012-12-18 22:24:03 +0000730 }
skia.committer@gmail.coma7d8e3e2012-12-19 02:01:38 +0000731
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000732 for (int i = fStart; i < fEnd; i++) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +0000733 draw_tile_to_canvas(fCanvas, fRects[i], fClone);
commit-bot@chromium.org4610a462014-04-29 19:39:22 +0000734 if (!write(fCanvas, fOutputDir, fInputFilename, fJsonSummaryPtr,
735 fUseChecksumBasedFilenames, &i)
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000736 && fSuccess != NULL) {
737 *fSuccess = false;
738 // If one tile fails to write to a file, do not continue drawing the rest.
739 break;
740 }
edisonn@google.com84f548c2012-12-18 22:24:03 +0000741 if (fBitmap != NULL) {
742 if (fCanvas->readPixels(&bitmap, 0, 0)) {
743 SkAutoLockPixels alp(*fBitmap);
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +0000744 bitmapCopyAtOffset(bitmap, fBitmap, SkScalarFloorToInt(fRects[i].left()),
745 SkScalarFloorToInt(fRects[i].top()));
edisonn@google.com84f548c2012-12-18 22:24:03 +0000746 } else {
747 *fSuccess = false;
748 // If one tile fails to read pixels, do not continue drawing the rest.
749 break;
750 }
751 }
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000752 }
753 fDone->run();
754 }
755
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000756 void setPathsAndSuccess(const SkString& outputDir, const SkString& inputFilename,
757 bool* success) {
758 fOutputDir.set(outputDir);
759 fInputFilename.set(inputFilename);
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000760 fSuccess = success;
761 }
762
edisonn@google.com84f548c2012-12-18 22:24:03 +0000763 void setBitmap(SkBitmap* bitmap) {
764 fBitmap = bitmap;
765 }
766
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000767private:
768 // All pointers unowned.
769 SkPicture* fClone; // Picture to draw from. Each CloneData has a unique one which
770 // is threadsafe.
771 SkCanvas* fCanvas; // Canvas to draw to. Reused for each tile.
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000772 SkString fOutputDir; // If not empty, write results into this directory.
773 SkString fInputFilename; // Filename of input SkPicture file.
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000774 SkTDArray<SkRect>& fRects; // All tiles of the picture.
775 const int fStart; // Range of tiles drawn by this thread.
776 const int fEnd;
777 bool* fSuccess; // Only meaningful if path is non-null. Shared by all threads,
778 // and only set to false upon failure to write to a PNG.
779 SkRunnable* fDone;
edisonn@google.com84f548c2012-12-18 22:24:03 +0000780 SkBitmap* fBitmap;
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +0000781 ImageResultsSummary* fJsonSummaryPtr;
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000782 bool fUseChecksumBasedFilenames;
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000783};
784
785MultiCorePictureRenderer::MultiCorePictureRenderer(int threadCount)
786: fNumThreads(threadCount)
787, fThreadPool(threadCount)
788, fCountdown(threadCount) {
789 // Only need to create fNumThreads - 1 clones, since one thread will use the base
790 // picture.
791 fPictureClones = SkNEW_ARRAY(SkPicture, fNumThreads - 1);
792 fCloneData = SkNEW_ARRAY(CloneData*, fNumThreads);
793}
794
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000795void MultiCorePictureRenderer::init(SkPicture *pict, const SkString* outputDir,
796 const SkString* inputFilename, bool useChecksumBasedFilenames) {
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000797 // Set fPicture and the tiles.
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000798 this->INHERITED::init(pict, outputDir, inputFilename, useChecksumBasedFilenames);
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000799 for (int i = 0; i < fNumThreads; ++i) {
800 *fCanvasPool.append() = this->setupCanvas(this->getTileWidth(), this->getTileHeight());
801 }
802 // Only need to create fNumThreads - 1 clones, since one thread will use the base picture.
803 fPicture->clone(fPictureClones, fNumThreads - 1);
804 // Populate each thread with the appropriate data.
805 // Group the tiles into nearly equal size chunks, rounding up so we're sure to cover them all.
806 const int chunkSize = (fTileRects.count() + fNumThreads - 1) / fNumThreads;
807
808 for (int i = 0; i < fNumThreads; i++) {
809 SkPicture* pic;
810 if (i == fNumThreads-1) {
811 // The last set will use the original SkPicture.
812 pic = fPicture;
813 } else {
814 pic = &fPictureClones[i];
815 }
816 const int start = i * chunkSize;
817 const int end = SkMin32(start + chunkSize, fTileRects.count());
818 fCloneData[i] = SkNEW_ARGS(CloneData,
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +0000819 (pic, fCanvasPool[i], fTileRects, start, end, &fCountdown,
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000820 fJsonSummaryPtr, useChecksumBasedFilenames));
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000821 }
822}
823
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000824bool MultiCorePictureRenderer::render(SkBitmap** out) {
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000825 bool success = true;
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000826 if (!fOutputDir.isEmpty()) {
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000827 for (int i = 0; i < fNumThreads-1; i++) {
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000828 fCloneData[i]->setPathsAndSuccess(fOutputDir, fInputFilename, &success);
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000829 }
830 }
831
edisonn@google.com84f548c2012-12-18 22:24:03 +0000832 if (NULL != out) {
833 *out = SkNEW(SkBitmap);
834 setup_bitmap(*out, fPicture->width(), fPicture->height());
835 for (int i = 0; i < fNumThreads; i++) {
836 fCloneData[i]->setBitmap(*out);
837 }
838 } else {
839 for (int i = 0; i < fNumThreads; i++) {
840 fCloneData[i]->setBitmap(NULL);
841 }
842 }
843
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000844 fCountdown.reset(fNumThreads);
845 for (int i = 0; i < fNumThreads; i++) {
846 fThreadPool.add(fCloneData[i]);
847 }
848 fCountdown.wait();
849
850 return success;
851}
852
853void MultiCorePictureRenderer::end() {
854 for (int i = 0; i < fNumThreads - 1; i++) {
855 SkDELETE(fCloneData[i]);
856 fCloneData[i] = NULL;
857 }
858
859 fCanvasPool.unrefAll();
860
861 this->INHERITED::end();
862}
863
864MultiCorePictureRenderer::~MultiCorePictureRenderer() {
865 // Each individual CloneData was deleted in end.
866 SkDELETE_ARRAY(fCloneData);
867 SkDELETE_ARRAY(fPictureClones);
868}
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000869
scroggo@google.com0a049b82012-11-02 22:01:26 +0000870SkString MultiCorePictureRenderer::getConfigNameInternal() {
871 SkString name = this->INHERITED::getConfigNameInternal();
872 name.appendf("_multi_%i_threads", fNumThreads);
873 return name;
874}
875
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000876///////////////////////////////////////////////////////////////////////////////////////////////
scroggo@google.com9a412522012-09-07 15:21:18 +0000877
878void PlaybackCreationRenderer::setup() {
commit-bot@chromium.org5fb2ce32014-04-17 23:35:06 +0000879 SkAutoTDelete<SkBBHFactory> factory(this->getFactory());
880 fRecorder.reset(SkNEW(SkPictureRecorder));
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000881 SkCanvas* canvas = fRecorder->beginRecording(this->getViewWidth(), this->getViewHeight(),
commit-bot@chromium.org5fb2ce32014-04-17 23:35:06 +0000882 factory.get(),
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000883 this->recordFlags());
884 this->scaleToScaleFactor(canvas);
885 canvas->drawPicture(*fPicture);
scroggo@google.com9a412522012-09-07 15:21:18 +0000886}
887
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000888bool PlaybackCreationRenderer::render(SkBitmap** out) {
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000889 fPicture.reset(fRecorder->endRecording());
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000890 // Since this class does not actually render, return false.
891 return false;
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000892}
893
scroggo@google.com0a049b82012-11-02 22:01:26 +0000894SkString PlaybackCreationRenderer::getConfigNameInternal() {
895 return SkString("playback_creation");
896}
897
junov@chromium.org9313ca42012-11-02 18:11:49 +0000898///////////////////////////////////////////////////////////////////////////////////////////////
899// SkPicture variants for each BBoxHierarchy type
900
commit-bot@chromium.org5fb2ce32014-04-17 23:35:06 +0000901SkBBHFactory* PictureRenderer::getFactory() {
junov@chromium.org9313ca42012-11-02 18:11:49 +0000902 switch (fBBoxHierarchyType) {
903 case kNone_BBoxHierarchyType:
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000904 return NULL;
commit-bot@chromium.orgc22d1392014-02-03 18:08:33 +0000905 case kQuadTree_BBoxHierarchyType:
commit-bot@chromium.org5fb2ce32014-04-17 23:35:06 +0000906 return SkNEW(SkQuadTreeFactory);
junov@chromium.org9313ca42012-11-02 18:11:49 +0000907 case kRTree_BBoxHierarchyType:
commit-bot@chromium.org5fb2ce32014-04-17 23:35:06 +0000908 return SkNEW(SkRTreeFactory);
junov@chromium.org7b537062012-11-06 18:58:43 +0000909 case kTileGrid_BBoxHierarchyType:
commit-bot@chromium.org5fb2ce32014-04-17 23:35:06 +0000910 return SkNEW_ARGS(SkTileGridFactory, (fGridInfo));
junov@chromium.org9313ca42012-11-02 18:11:49 +0000911 }
912 SkASSERT(0); // invalid bbhType
913 return NULL;
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000914}
junov@chromium.org9313ca42012-11-02 18:11:49 +0000915
reed@google.comfe7b1ed2012-11-29 21:00:39 +0000916///////////////////////////////////////////////////////////////////////////////
917
918class GatherRenderer : public PictureRenderer {
919public:
commit-bot@chromium.org8ddc26b2014-03-31 17:55:12 +0000920 virtual bool render(SkBitmap** out = NULL) SK_OVERRIDE {
reed@google.comfe7b1ed2012-11-29 21:00:39 +0000921 SkRect bounds = SkRect::MakeWH(SkIntToScalar(fPicture->width()),
922 SkIntToScalar(fPicture->height()));
923 SkData* data = SkPictureUtils::GatherPixelRefs(fPicture, bounds);
924 SkSafeUnref(data);
skia.committer@gmail.comc7b4be72012-12-11 02:01:20 +0000925
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000926 return (fOutputDir.isEmpty()); // we don't have anything to write
reed@google.comfe7b1ed2012-11-29 21:00:39 +0000927 }
skia.committer@gmail.comc7b4be72012-12-11 02:01:20 +0000928
reed@google.comfe7b1ed2012-11-29 21:00:39 +0000929private:
930 virtual SkString getConfigNameInternal() SK_OVERRIDE {
931 return SkString("gather_pixelrefs");
932 }
933};
934
935PictureRenderer* CreateGatherPixelRefsRenderer() {
936 return SkNEW(GatherRenderer);
937}
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +0000938
reed@google.com5a34fd32012-12-10 16:05:09 +0000939///////////////////////////////////////////////////////////////////////////////
940
941class PictureCloneRenderer : public PictureRenderer {
942public:
commit-bot@chromium.org8ddc26b2014-03-31 17:55:12 +0000943 virtual bool render(SkBitmap** out = NULL) 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
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000949 return (fOutputDir.isEmpty()); // we don't have anything to write
reed@google.com5a34fd32012-12-10 16:05:09 +0000950 }
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