blob: 12f0afa879e04e5ee9ac2c8992e9331c9a8dca8f [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.org3f045172014-05-15 15:10:48 +000051void PictureRenderer::init(SkPicture* pict, const SkString* writePath, const SkString* mismatchPath,
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +000052 const SkString* inputFilename, bool useChecksumBasedFilenames) {
commit-bot@chromium.org3f045172014-05-15 15:10:48 +000053 this->CopyString(&fWritePath, writePath);
54 this->CopyString(&fMismatchPath, mismatchPath);
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +000055 this->CopyString(&fInputFilename, inputFilename);
56 fUseChecksumBasedFilenames = useChecksumBasedFilenames;
57
keyar@chromium.org78a35c52012-08-20 15:03:44 +000058 SkASSERT(NULL == fPicture);
59 SkASSERT(NULL == fCanvas.get());
robertphillips@google.com84b18c72014-04-13 19:09:42 +000060 if (NULL != fPicture || NULL != fCanvas.get()) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +000061 return;
62 }
63
64 SkASSERT(pict != NULL);
keyar@chromium.org78a35c52012-08-20 15:03:44 +000065 if (NULL == pict) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +000066 return;
67 }
68
robertphillips@google.com84b18c72014-04-13 19:09:42 +000069 fPicture.reset(pict)->ref();
keyar@chromium.orga474ce32012-08-20 15:03:57 +000070 fCanvas.reset(this->setupCanvas());
71}
72
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +000073void PictureRenderer::CopyString(SkString* dest, const SkString* src) {
74 if (NULL != src) {
75 dest->set(*src);
76 } else {
77 dest->reset();
78 }
79}
80
caryclark@google.coma3622372012-11-06 21:26:13 +000081class FlagsDrawFilter : public SkDrawFilter {
82public:
83 FlagsDrawFilter(PictureRenderer::DrawFilterFlags* flags) :
84 fFlags(flags) {}
85
reed@google.com971aca72012-11-26 20:26:54 +000086 virtual bool filter(SkPaint* paint, Type t) {
caryclark@google.coma3622372012-11-06 21:26:13 +000087 paint->setFlags(paint->getFlags() & ~fFlags[t] & SkPaint::kAllFlags);
robertphillips@google.com49149312013-07-03 15:34:35 +000088 if (PictureRenderer::kMaskFilter_DrawFilterFlag & fFlags[t]) {
caryclark@google.coma3622372012-11-06 21:26:13 +000089 SkMaskFilter* maskFilter = paint->getMaskFilter();
robertphillips@google.com49149312013-07-03 15:34:35 +000090 if (NULL != maskFilter) {
reed@google.com457d8a72012-12-18 18:20:44 +000091 paint->setMaskFilter(NULL);
caryclark@google.coma3622372012-11-06 21:26:13 +000092 }
93 }
94 if (PictureRenderer::kHinting_DrawFilterFlag & fFlags[t]) {
95 paint->setHinting(SkPaint::kNo_Hinting);
96 } else if (PictureRenderer::kSlightHinting_DrawFilterFlag & fFlags[t]) {
97 paint->setHinting(SkPaint::kSlight_Hinting);
98 }
reed@google.com971aca72012-11-26 20:26:54 +000099 return true;
caryclark@google.coma3622372012-11-06 21:26:13 +0000100 }
101
102private:
103 PictureRenderer::DrawFilterFlags* fFlags;
104};
105
scroggo@google.com82ec0b02012-12-17 19:25:54 +0000106static void setUpFilter(SkCanvas* canvas, PictureRenderer::DrawFilterFlags* drawFilters) {
caryclark@google.coma3622372012-11-06 21:26:13 +0000107 if (drawFilters && !canvas->getDrawFilter()) {
108 canvas->setDrawFilter(SkNEW_ARGS(FlagsDrawFilter, (drawFilters)))->unref();
caryclark@google.come3e940c2012-11-07 16:42:17 +0000109 if (drawFilters[0] & PictureRenderer::kAAClip_DrawFilterFlag) {
110 canvas->setAllowSoftClip(false);
111 }
caryclark@google.coma3622372012-11-06 21:26:13 +0000112 }
caryclark@google.coma3622372012-11-06 21:26:13 +0000113}
114
keyar@chromium.orga474ce32012-08-20 15:03:57 +0000115SkCanvas* PictureRenderer::setupCanvas() {
scroggo@google.comc0d5e542012-12-13 21:40:48 +0000116 const int width = this->getViewWidth();
117 const int height = this->getViewHeight();
118 return this->setupCanvas(width, height);
keyar@chromium.orga474ce32012-08-20 15:03:57 +0000119}
120
121SkCanvas* PictureRenderer::setupCanvas(int width, int height) {
caryclark@google.coma3622372012-11-06 21:26:13 +0000122 SkCanvas* canvas;
keyar@chromium.org4ea96c52012-08-20 15:03:29 +0000123 switch(fDeviceType) {
124 case kBitmap_DeviceType: {
125 SkBitmap bitmap;
keyar@chromium.orga474ce32012-08-20 15:03:57 +0000126 sk_tools::setup_bitmap(&bitmap, width, height);
caryclark@google.coma3622372012-11-06 21:26:13 +0000127 canvas = SkNEW_ARGS(SkCanvas, (bitmap));
keyar@chromium.org4ea96c52012-08-20 15:03:29 +0000128 }
scroggo@google.com82ec0b02012-12-17 19:25:54 +0000129 break;
keyar@chromium.org4ea96c52012-08-20 15:03:29 +0000130#if SK_SUPPORT_GPU
scroggo@google.com0556ea02013-02-08 19:38:21 +0000131#if SK_ANGLE
132 case kAngle_DeviceType:
133 // fall through
134#endif
rmistry@google.com6ab96732014-01-06 18:37:24 +0000135#if SK_MESA
136 case kMesa_DeviceType:
137 // fall through
138#endif
commit-bot@chromium.org0fd52702014-03-07 18:41:14 +0000139 case kGPU_DeviceType:
140 case kNVPR_DeviceType: {
commit-bot@chromium.orgae403b92013-04-10 17:27:30 +0000141 SkAutoTUnref<GrSurface> target;
scroggo@google.com0556ea02013-02-08 19:38:21 +0000142 if (fGrContext) {
143 // create a render target to back the device
144 GrTextureDesc desc;
145 desc.fConfig = kSkia8888_GrPixelConfig;
146 desc.fFlags = kRenderTarget_GrTextureFlagBit;
147 desc.fWidth = width;
148 desc.fHeight = height;
jvanverth@google.comf6a90332013-05-02 12:39:37 +0000149 desc.fSampleCnt = fSampleCount;
commit-bot@chromium.orgae403b92013-04-10 17:27:30 +0000150 target.reset(fGrContext->createUncachedTexture(desc, NULL, 0));
scroggo@google.com0556ea02013-02-08 19:38:21 +0000151 }
commit-bot@chromium.orgae403b92013-04-10 17:27:30 +0000152 if (NULL == target.get()) {
scroggo@google.com0556ea02013-02-08 19:38:21 +0000153 SkASSERT(0);
154 return NULL;
155 }
156
commit-bot@chromium.orgae403b92013-04-10 17:27:30 +0000157 SkAutoTUnref<SkGpuDevice> device(SkGpuDevice::Create(target));
caryclark@google.coma3622372012-11-06 21:26:13 +0000158 canvas = SkNEW_ARGS(SkCanvas, (device.get()));
scroggo@google.com0556ea02013-02-08 19:38:21 +0000159 break;
keyar@chromium.org4ea96c52012-08-20 15:03:29 +0000160 }
161#endif
162 default:
163 SkASSERT(0);
scroggo@google.com82ec0b02012-12-17 19:25:54 +0000164 return NULL;
keyar@chromium.org4ea96c52012-08-20 15:03:29 +0000165 }
scroggo@google.com82ec0b02012-12-17 19:25:54 +0000166 setUpFilter(canvas, fDrawFilters);
167 this->scaleToScaleFactor(canvas);
commit-bot@chromium.org17cc3ea2014-01-15 14:51:25 +0000168
169 // Pictures often lie about their extent (i.e., claim to be 100x100 but
170 // only ever draw to 90x100). Clear here so the undrawn portion will have
171 // a consistent color
172 canvas->clear(SK_ColorTRANSPARENT);
scroggo@google.com82ec0b02012-12-17 19:25:54 +0000173 return canvas;
174}
keyar@chromium.orga474ce32012-08-20 15:03:57 +0000175
scroggo@google.com82ec0b02012-12-17 19:25:54 +0000176void PictureRenderer::scaleToScaleFactor(SkCanvas* canvas) {
177 SkASSERT(canvas != NULL);
178 if (fScaleFactor != SK_Scalar1) {
179 canvas->scale(fScaleFactor, fScaleFactor);
180 }
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000181}
182
183void PictureRenderer::end() {
scroggo@google.com08085f82013-01-28 20:40:24 +0000184 this->resetState(true);
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000185 fPicture.reset(NULL);
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000186 fCanvas.reset(NULL);
187}
188
scroggo@google.comc0d5e542012-12-13 21:40:48 +0000189int PictureRenderer::getViewWidth() {
190 SkASSERT(fPicture != NULL);
robertphillips@google.com8ac811e2013-02-07 00:13:34 +0000191 int width = SkScalarCeilToInt(fPicture->width() * fScaleFactor);
scroggo@google.comc0d5e542012-12-13 21:40:48 +0000192 if (fViewport.width() > 0) {
193 width = SkMin32(width, fViewport.width());
194 }
195 return width;
196}
197
198int PictureRenderer::getViewHeight() {
199 SkASSERT(fPicture != NULL);
robertphillips@google.com8ac811e2013-02-07 00:13:34 +0000200 int height = SkScalarCeilToInt(fPicture->height() * fScaleFactor);
scroggo@google.comc0d5e542012-12-13 21:40:48 +0000201 if (fViewport.height() > 0) {
202 height = SkMin32(height, fViewport.height());
203 }
204 return height;
205}
206
junov@chromium.org9313ca42012-11-02 18:11:49 +0000207/** Converts fPicture to a picture that uses a BBoxHierarchy.
208 * PictureRenderer subclasses that are used to test picture playback
209 * should call this method during init.
210 */
211void PictureRenderer::buildBBoxHierarchy() {
212 SkASSERT(NULL != fPicture);
213 if (kNone_BBoxHierarchyType != fBBoxHierarchyType && NULL != fPicture) {
commit-bot@chromium.org5fb2ce32014-04-17 23:35:06 +0000214 SkAutoTDelete<SkBBHFactory> factory(this->getFactory());
215 SkPictureRecorder recorder;
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000216 SkCanvas* canvas = recorder.beginRecording(fPicture->width(), fPicture->height(),
commit-bot@chromium.org5fb2ce32014-04-17 23:35:06 +0000217 factory.get(),
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000218 this->recordFlags());
219 fPicture->draw(canvas);
220 fPicture.reset(recorder.endRecording());
junov@chromium.org9313ca42012-11-02 18:11:49 +0000221 }
222}
223
scroggo@google.com08085f82013-01-28 20:40:24 +0000224void PictureRenderer::resetState(bool callFinish) {
keyar@chromium.org28136b32012-08-20 15:04:15 +0000225#if SK_SUPPORT_GPU
robertphillips@google.com6177e692013-02-28 20:16:25 +0000226 SkGLContextHelper* glContext = this->getGLContext();
scroggo@google.com0556ea02013-02-08 19:38:21 +0000227 if (NULL == glContext) {
228 SkASSERT(kBitmap_DeviceType == fDeviceType);
229 return;
230 }
keyar@chromium.org28136b32012-08-20 15:04:15 +0000231
scroggo@google.com0556ea02013-02-08 19:38:21 +0000232 fGrContext->flush();
commit-bot@chromium.org51c040e2014-03-11 22:58:00 +0000233 glContext->swapBuffers();
scroggo@google.com0556ea02013-02-08 19:38:21 +0000234 if (callFinish) {
235 SK_GL(*glContext, Finish());
keyar@chromium.org77a55222012-08-20 15:03:47 +0000236 }
keyar@chromium.orga40c20d2012-08-20 15:04:12 +0000237#endif
keyar@chromium.org77a55222012-08-20 15:03:47 +0000238}
239
robertphillips@google.com94d8f1e2013-12-18 17:25:33 +0000240void PictureRenderer::purgeTextures() {
241 SkDiscardableMemoryPool* pool = SkGetGlobalDiscardableMemoryPool();
242
243 pool->dumpPool();
244
245#if SK_SUPPORT_GPU
246 SkGLContextHelper* glContext = this->getGLContext();
247 if (NULL == glContext) {
248 SkASSERT(kBitmap_DeviceType == fDeviceType);
249 return;
250 }
251
252 // resetState should've already done this
253 fGrContext->flush();
254
255 fGrContext->purgeAllUnlockedResources();
256#endif
257}
258
junov@chromium.org9313ca42012-11-02 18:11:49 +0000259uint32_t PictureRenderer::recordFlags() {
skia.committer@gmail.com667b98d2014-04-17 03:05:10 +0000260 return (kNone_BBoxHierarchyType == fBBoxHierarchyType)
commit-bot@chromium.orgd393b172014-04-16 16:02:10 +0000261 ? 0
262 : SkPicture::kUsePathBoundsForClip_RecordingFlag;
junov@chromium.org9313ca42012-11-02 18:11:49 +0000263}
264
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000265/**
commit-bot@chromium.org4610a462014-04-29 19:39:22 +0000266 * Write the canvas to an image file and/or JSON summary.
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000267 *
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000268 * @param canvas Must be non-null. Canvas to be written to a file.
commit-bot@chromium.org3f045172014-05-15 15:10:48 +0000269 * @param writePath If nonempty, write the binary image to a file within this directory.
270 * @param mismatchPath If nonempty, write the binary image to a file within this directory,
271 * but only if the image does not match expectations.
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000272 * @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 +0000273 * @param jsonSummaryPtr If not null, add image results (checksum) to this summary.
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000274 * @param useChecksumBasedFilenames If true, use checksum-based filenames when writing to disk.
commit-bot@chromium.org24c568c2014-04-10 15:39:02 +0000275 * @param tileNumberPtr If not null, which tile number this image contains.
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +0000276 *
commit-bot@chromium.org4610a462014-04-29 19:39:22 +0000277 * @return bool True if the operation completed successfully.
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000278 */
commit-bot@chromium.org3f045172014-05-15 15:10:48 +0000279static bool write(SkCanvas* canvas, const SkString& writePath, const SkString& mismatchPath,
280 const SkString& inputFilename, ImageResultsAndExpectations *jsonSummaryPtr,
281 bool useChecksumBasedFilenames, const int* tileNumberPtr=NULL) {
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000282 SkASSERT(canvas != NULL);
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000283 if (NULL == canvas) {
keyar@chromium.org9299ede2012-08-21 19:05:08 +0000284 return false;
285 }
286
287 SkBitmap bitmap;
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000288 SkISize size = canvas->getDeviceSize();
commit-bot@chromium.org205ce482014-05-12 15:37:20 +0000289 setup_bitmap(&bitmap, size.width(), size.height());
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000290
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000291 canvas->readPixels(&bitmap, 0, 0);
commit-bot@chromium.org205ce482014-05-12 15:37:20 +0000292 force_all_opaque(bitmap);
293 BitmapAndDigest bitmapAndDigest(bitmap);
keyar@chromium.org9299ede2012-08-21 19:05:08 +0000294
commit-bot@chromium.org24c568c2014-04-10 15:39:02 +0000295 SkString escapedInputFilename(inputFilename);
296 replace_char(&escapedInputFilename, '.', '_');
297
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000298 // TODO(epoger): what about including the config type within outputFilename? That way,
299 // we could combine results of different config types without conflicting filenames.
commit-bot@chromium.org24c568c2014-04-10 15:39:02 +0000300 SkString outputFilename;
commit-bot@chromium.org205ce482014-05-12 15:37:20 +0000301 const ImageDigest *imageDigestPtr = bitmapAndDigest.getImageDigestPtr();
commit-bot@chromium.org24c568c2014-04-10 15:39:02 +0000302 const char *outputSubdirPtr = NULL;
303 if (useChecksumBasedFilenames) {
commit-bot@chromium.org24c568c2014-04-10 15:39:02 +0000304 outputSubdirPtr = escapedInputFilename.c_str();
commit-bot@chromium.org205ce482014-05-12 15:37:20 +0000305 outputFilename.set(imageDigestPtr->getHashType());
306 outputFilename.append("_");
307 outputFilename.appendU64(imageDigestPtr->getHashValue());
commit-bot@chromium.org24c568c2014-04-10 15:39:02 +0000308 } else {
309 outputFilename.set(escapedInputFilename);
310 if (NULL != tileNumberPtr) {
311 outputFilename.append("-tile");
312 outputFilename.appendS32(*tileNumberPtr);
313 }
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +0000314 }
commit-bot@chromium.org24c568c2014-04-10 15:39:02 +0000315 outputFilename.append(".png");
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +0000316
commit-bot@chromium.org24c568c2014-04-10 15:39:02 +0000317 if (NULL != jsonSummaryPtr) {
commit-bot@chromium.org205ce482014-05-12 15:37:20 +0000318 const ImageDigest *imageDigestPtr = bitmapAndDigest.getImageDigestPtr();
commit-bot@chromium.org24c568c2014-04-10 15:39:02 +0000319 SkString outputRelativePath;
320 if (outputSubdirPtr) {
321 outputRelativePath.set(outputSubdirPtr);
322 outputRelativePath.append("/"); // always use "/", even on Windows
323 outputRelativePath.append(outputFilename);
324 } else {
325 outputRelativePath.set(outputFilename);
326 }
327
328 jsonSummaryPtr->add(inputFilename.c_str(), outputRelativePath.c_str(),
commit-bot@chromium.org205ce482014-05-12 15:37:20 +0000329 *imageDigestPtr, tileNumberPtr);
commit-bot@chromium.org3f045172014-05-15 15:10:48 +0000330 if (!mismatchPath.isEmpty() &&
331 !jsonSummaryPtr->matchesExpectation(inputFilename.c_str(), *imageDigestPtr,
332 tileNumberPtr)) {
333 if (!write_bitmap_to_disk(bitmap, mismatchPath, outputSubdirPtr, outputFilename)) {
334 return false;
335 }
336 }
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000337 }
338
commit-bot@chromium.org3f045172014-05-15 15:10:48 +0000339 if (writePath.isEmpty()) {
commit-bot@chromium.org4610a462014-04-29 19:39:22 +0000340 return true;
commit-bot@chromium.org24c568c2014-04-10 15:39:02 +0000341 } else {
commit-bot@chromium.org3f045172014-05-15 15:10:48 +0000342 return write_bitmap_to_disk(bitmap, writePath, outputSubdirPtr, outputFilename);
commit-bot@chromium.org24c568c2014-04-10 15:39:02 +0000343 }
keyar@chromium.org9299ede2012-08-21 19:05:08 +0000344}
345
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000346///////////////////////////////////////////////////////////////////////////////////////////////
347
djsollen@google.comfd9720c2012-11-06 16:54:40 +0000348SkCanvas* RecordPictureRenderer::setupCanvas(int width, int height) {
349 // defer the canvas setup until the render step
350 return NULL;
351}
352
reed@google.com672588b2014-01-08 15:42:01 +0000353// the size_t* parameter is deprecated, so we ignore it
354static SkData* encode_bitmap_to_data(size_t*, const SkBitmap& bm) {
scroggo@google.com1b1bcc32013-05-21 20:31:23 +0000355 return SkImageEncoder::EncodeData(bm, SkImageEncoder::kPNG_Type, 100);
scroggo@google.coma9e3a362012-11-07 17:52:48 +0000356}
357
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000358bool RecordPictureRenderer::render(SkBitmap** out) {
commit-bot@chromium.org5fb2ce32014-04-17 23:35:06 +0000359 SkAutoTDelete<SkBBHFactory> factory(this->getFactory());
360 SkPictureRecorder recorder;
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000361 SkCanvas* canvas = recorder.beginRecording(this->getViewWidth(), this->getViewHeight(),
commit-bot@chromium.org5fb2ce32014-04-17 23:35:06 +0000362 factory.get(),
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000363 this->recordFlags());
364 this->scaleToScaleFactor(canvas);
365 fPicture->draw(canvas);
366 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
commit-bot@chromium.org3f045172014-05-15 15:10:48 +0000367 if (!fWritePath.isEmpty()) {
scroggo@google.coma9e3a362012-11-07 17:52:48 +0000368 // Record the new picture as a new SKP with PNG encoded bitmaps.
commit-bot@chromium.org3f045172014-05-15 15:10:48 +0000369 SkString skpPath = SkOSPath::SkPathJoin(fWritePath.c_str(), fInputFilename.c_str());
scroggo@google.coma9e3a362012-11-07 17:52:48 +0000370 SkFILEWStream stream(skpPath.c_str());
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000371 picture->serialize(&stream, &encode_bitmap_to_data);
scroggo@google.coma9e3a362012-11-07 17:52:48 +0000372 return true;
373 }
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000374 return false;
scroggo@google.com9a412522012-09-07 15:21:18 +0000375}
376
scroggo@google.com0a049b82012-11-02 22:01:26 +0000377SkString RecordPictureRenderer::getConfigNameInternal() {
378 return SkString("record");
379}
380
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000381///////////////////////////////////////////////////////////////////////////////////////////////
382
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000383bool PipePictureRenderer::render(SkBitmap** out) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000384 SkASSERT(fCanvas.get() != NULL);
385 SkASSERT(fPicture != NULL);
keyar@chromium.org78a35c52012-08-20 15:03:44 +0000386 if (NULL == fCanvas.get() || NULL == fPicture) {
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000387 return false;
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000388 }
389
390 PipeController pipeController(fCanvas.get());
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000391 SkGPipeWriter writer;
392 SkCanvas* pipeCanvas = writer.startRecording(&pipeController);
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000393 pipeCanvas->drawPicture(*fPicture);
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000394 writer.endRecording();
scroggo@google.com9a412522012-09-07 15:21:18 +0000395 fCanvas->flush();
edisonn@google.com84f548c2012-12-18 22:24:03 +0000396 if (NULL != out) {
397 *out = SkNEW(SkBitmap);
398 setup_bitmap(*out, fPicture->width(), fPicture->height());
399 fCanvas->readPixels(*out, 0, 0);
skia.committer@gmail.coma7d8e3e2012-12-19 02:01:38 +0000400 }
commit-bot@chromium.org3f045172014-05-15 15:10:48 +0000401 return write(fCanvas, fWritePath, fMismatchPath, fInputFilename, fJsonSummaryPtr,
commit-bot@chromium.org4610a462014-04-29 19:39:22 +0000402 fUseChecksumBasedFilenames);
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000403}
404
scroggo@google.com0a049b82012-11-02 22:01:26 +0000405SkString PipePictureRenderer::getConfigNameInternal() {
406 return SkString("pipe");
407}
408
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000409///////////////////////////////////////////////////////////////////////////////////////////////
410
commit-bot@chromium.org3f045172014-05-15 15:10:48 +0000411void SimplePictureRenderer::init(SkPicture* picture, const SkString* writePath,
412 const SkString* mismatchPath, const SkString* inputFilename,
413 bool useChecksumBasedFilenames) {
414 INHERITED::init(picture, writePath, mismatchPath, inputFilename, useChecksumBasedFilenames);
junov@chromium.org9313ca42012-11-02 18:11:49 +0000415 this->buildBBoxHierarchy();
416}
417
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000418bool SimplePictureRenderer::render(SkBitmap** out) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000419 SkASSERT(fCanvas.get() != NULL);
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000420 SkASSERT(NULL != fPicture);
keyar@chromium.org78a35c52012-08-20 15:03:44 +0000421 if (NULL == fCanvas.get() || NULL == fPicture) {
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000422 return false;
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000423 }
424
425 fCanvas->drawPicture(*fPicture);
scroggo@google.com9a412522012-09-07 15:21:18 +0000426 fCanvas->flush();
edisonn@google.com84f548c2012-12-18 22:24:03 +0000427 if (NULL != out) {
428 *out = SkNEW(SkBitmap);
429 setup_bitmap(*out, fPicture->width(), fPicture->height());
430 fCanvas->readPixels(*out, 0, 0);
431 }
commit-bot@chromium.org3f045172014-05-15 15:10:48 +0000432 return write(fCanvas, fWritePath, fMismatchPath, fInputFilename, fJsonSummaryPtr,
commit-bot@chromium.org4610a462014-04-29 19:39:22 +0000433 fUseChecksumBasedFilenames);
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000434}
435
scroggo@google.com0a049b82012-11-02 22:01:26 +0000436SkString SimplePictureRenderer::getConfigNameInternal() {
437 return SkString("simple");
438}
439
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000440///////////////////////////////////////////////////////////////////////////////////////////////
441
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000442TiledPictureRenderer::TiledPictureRenderer()
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000443 : fTileWidth(kDefaultTileWidth)
rileya@google.comb947b912012-08-29 17:35:07 +0000444 , fTileHeight(kDefaultTileHeight)
rileya@google.coma04dc022012-09-10 19:01:38 +0000445 , fTileWidthPercentage(0.0)
rileya@google.comb947b912012-08-29 17:35:07 +0000446 , fTileHeightPercentage(0.0)
scroggo@google.comcbcef702012-12-13 22:09:28 +0000447 , fTileMinPowerOf2Width(0)
448 , fCurrentTileOffset(-1)
449 , fTilesX(0)
450 , fTilesY(0) { }
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000451
commit-bot@chromium.org3f045172014-05-15 15:10:48 +0000452void TiledPictureRenderer::init(SkPicture* pict, const SkString* writePath,
453 const SkString* mismatchPath, const SkString* inputFilename,
454 bool useChecksumBasedFilenames) {
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000455 SkASSERT(NULL != pict);
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000456 SkASSERT(0 == fTileRects.count());
457 if (NULL == pict || fTileRects.count() != 0) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000458 return;
459 }
460
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000461 // Do not call INHERITED::init(), which would create a (potentially large) canvas which is not
462 // used by bench_pictures.
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000463 fPicture.reset(pict)->ref();
commit-bot@chromium.org3f045172014-05-15 15:10:48 +0000464 this->CopyString(&fWritePath, writePath);
465 this->CopyString(&fMismatchPath, mismatchPath);
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000466 this->CopyString(&fInputFilename, inputFilename);
467 fUseChecksumBasedFilenames = useChecksumBasedFilenames;
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000468 this->buildBBoxHierarchy();
keyar@chromium.orgcc6e5ef2012-07-27 20:09:26 +0000469
470 if (fTileWidthPercentage > 0) {
robertphillips@google.com5d8d1862012-08-15 14:36:41 +0000471 fTileWidth = sk_float_ceil2int(float(fTileWidthPercentage * fPicture->width() / 100));
keyar@chromium.orgcc6e5ef2012-07-27 20:09:26 +0000472 }
473 if (fTileHeightPercentage > 0) {
robertphillips@google.com5d8d1862012-08-15 14:36:41 +0000474 fTileHeight = sk_float_ceil2int(float(fTileHeightPercentage * fPicture->height() / 100));
keyar@chromium.orgcc6e5ef2012-07-27 20:09:26 +0000475 }
476
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000477 if (fTileMinPowerOf2Width > 0) {
478 this->setupPowerOf2Tiles();
479 } else {
480 this->setupTiles();
481 }
scroggo@google.comcbcef702012-12-13 22:09:28 +0000482 fCanvas.reset(this->setupCanvas(fTileWidth, fTileHeight));
483 // Initialize to -1 so that the first call to nextTile will set this up to draw tile 0 on the
484 // first call to drawCurrentTile.
485 fCurrentTileOffset = -1;
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000486}
487
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000488void TiledPictureRenderer::end() {
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000489 fTileRects.reset();
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000490 this->INHERITED::end();
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000491}
492
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000493void TiledPictureRenderer::setupTiles() {
scroggo@google.comc0d5e542012-12-13 21:40:48 +0000494 // Only use enough tiles to cover the viewport
495 const int width = this->getViewWidth();
496 const int height = this->getViewHeight();
497
scroggo@google.comcbcef702012-12-13 22:09:28 +0000498 fTilesX = fTilesY = 0;
scroggo@google.comc0d5e542012-12-13 21:40:48 +0000499 for (int tile_y_start = 0; tile_y_start < height; tile_y_start += fTileHeight) {
scroggo@google.comcbcef702012-12-13 22:09:28 +0000500 fTilesY++;
scroggo@google.comc0d5e542012-12-13 21:40:48 +0000501 for (int tile_x_start = 0; tile_x_start < width; tile_x_start += fTileWidth) {
scroggo@google.comcbcef702012-12-13 22:09:28 +0000502 if (0 == tile_y_start) {
503 // Only count tiles in the X direction on the first pass.
504 fTilesX++;
505 }
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000506 *fTileRects.append() = SkRect::MakeXYWH(SkIntToScalar(tile_x_start),
507 SkIntToScalar(tile_y_start),
508 SkIntToScalar(fTileWidth),
509 SkIntToScalar(fTileHeight));
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000510 }
511 }
512}
513
scroggo@google.comcbcef702012-12-13 22:09:28 +0000514bool TiledPictureRenderer::tileDimensions(int &x, int &y) {
515 if (fTileRects.count() == 0 || NULL == fPicture) {
516 return false;
517 }
518 x = fTilesX;
519 y = fTilesY;
520 return true;
521}
522
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000523// The goal of the powers of two tiles is to minimize the amount of wasted tile
524// space in the width-wise direction and then minimize the number of tiles. The
525// constraints are that every tile must have a pixel width that is a power of
526// two and also be of some minimal width (that is also a power of two).
527//
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000528// This is solved by first taking our picture size and rounding it up to the
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000529// multiple of the minimal width. The binary representation of this rounded
530// value gives us the tiles we need: a bit of value one means we need a tile of
531// that size.
532void TiledPictureRenderer::setupPowerOf2Tiles() {
scroggo@google.comc0d5e542012-12-13 21:40:48 +0000533 // Only use enough tiles to cover the viewport
534 const int width = this->getViewWidth();
535 const int height = this->getViewHeight();
536
537 int rounded_value = width;
538 if (width % fTileMinPowerOf2Width != 0) {
539 rounded_value = width - (width % fTileMinPowerOf2Width) + fTileMinPowerOf2Width;
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000540 }
541
reed@google.come15b2f52013-12-18 04:59:26 +0000542 int num_bits = SkScalarCeilToInt(scalar_log2(SkIntToScalar(width)));
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000543 int largest_possible_tile_size = 1 << num_bits;
544
scroggo@google.comcbcef702012-12-13 22:09:28 +0000545 fTilesX = fTilesY = 0;
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000546 // The tile height is constant for a particular picture.
scroggo@google.comc0d5e542012-12-13 21:40:48 +0000547 for (int tile_y_start = 0; tile_y_start < height; tile_y_start += fTileHeight) {
scroggo@google.comcbcef702012-12-13 22:09:28 +0000548 fTilesY++;
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000549 int tile_x_start = 0;
550 int current_width = largest_possible_tile_size;
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000551 // Set fTileWidth to be the width of the widest tile, so that each canvas is large enough
552 // to draw each tile.
553 fTileWidth = current_width;
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000554
555 while (current_width >= fTileMinPowerOf2Width) {
556 // It is very important this is a bitwise AND.
557 if (current_width & rounded_value) {
scroggo@google.comcbcef702012-12-13 22:09:28 +0000558 if (0 == tile_y_start) {
559 // Only count tiles in the X direction on the first pass.
560 fTilesX++;
561 }
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000562 *fTileRects.append() = SkRect::MakeXYWH(SkIntToScalar(tile_x_start),
563 SkIntToScalar(tile_y_start),
564 SkIntToScalar(current_width),
565 SkIntToScalar(fTileHeight));
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000566 tile_x_start += current_width;
567 }
568
569 current_width >>= 1;
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000570 }
571 }
572}
573
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000574/**
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +0000575 * Draw the specified picture to the canvas translated to rectangle provided, so that this mini
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000576 * canvas represents the rectangle's portion of the overall picture.
577 * Saves and restores so that the initial clip and matrix return to their state before this function
578 * is called.
579 */
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +0000580static void draw_tile_to_canvas(SkCanvas* canvas, const SkRect& tileRect, SkPicture* picture) {
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000581 int saveCount = canvas->save();
scroggo@google.com82ec0b02012-12-17 19:25:54 +0000582 // Translate so that we draw the correct portion of the picture.
583 // Perform a postTranslate so that the scaleFactor does not interfere with the positioning.
584 SkMatrix mat(canvas->getTotalMatrix());
585 mat.postTranslate(-tileRect.fLeft, -tileRect.fTop);
586 canvas->setMatrix(mat);
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +0000587 canvas->drawPicture(*picture);
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000588 canvas->restoreToCount(saveCount);
589 canvas->flush();
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000590}
591
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000592///////////////////////////////////////////////////////////////////////////////////////////////
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000593
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +0000594/**
595 * Copies the entirety of the src bitmap (typically a tile) into a portion of the dst bitmap.
596 * If the src bitmap is too large to fit within the dst bitmap after the x and y
597 * offsets have been applied, any excess will be ignored (so only the top-left portion of the
598 * src bitmap will be copied).
599 *
600 * @param src source bitmap
601 * @param dst destination bitmap
602 * @param xOffset x-offset within destination bitmap
603 * @param yOffset y-offset within destination bitmap
604 */
605static void bitmapCopyAtOffset(const SkBitmap& src, SkBitmap* dst,
606 int xOffset, int yOffset) {
607 for (int y = 0; y <src.height() && y + yOffset < dst->height() ; y++) {
608 for (int x = 0; x < src.width() && x + xOffset < dst->width() ; x++) {
609 *dst->getAddr32(xOffset + x, yOffset + y) = *src.getAddr32(x, y);
edisonn@google.com84f548c2012-12-18 22:24:03 +0000610 }
611 }
612}
613
scroggo@google.comcbcef702012-12-13 22:09:28 +0000614bool TiledPictureRenderer::nextTile(int &i, int &j) {
615 if (++fCurrentTileOffset < fTileRects.count()) {
616 i = fCurrentTileOffset % fTilesX;
617 j = fCurrentTileOffset / fTilesX;
618 return true;
619 }
620 return false;
621}
622
623void TiledPictureRenderer::drawCurrentTile() {
624 SkASSERT(fCurrentTileOffset >= 0 && fCurrentTileOffset < fTileRects.count());
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +0000625 draw_tile_to_canvas(fCanvas, fTileRects[fCurrentTileOffset], fPicture);
scroggo@google.comcbcef702012-12-13 22:09:28 +0000626}
627
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000628bool TiledPictureRenderer::render(SkBitmap** out) {
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000629 SkASSERT(fPicture != NULL);
630 if (NULL == fPicture) {
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000631 return false;
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000632 }
633
edisonn@google.com84f548c2012-12-18 22:24:03 +0000634 SkBitmap bitmap;
635 if (out){
636 *out = SkNEW(SkBitmap);
637 setup_bitmap(*out, fPicture->width(), fPicture->height());
638 setup_bitmap(&bitmap, fTileWidth, fTileHeight);
639 }
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000640 bool success = true;
641 for (int i = 0; i < fTileRects.count(); ++i) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +0000642 draw_tile_to_canvas(fCanvas, fTileRects[i], fPicture);
commit-bot@chromium.org3f045172014-05-15 15:10:48 +0000643 success &= write(fCanvas, fWritePath, fMismatchPath, fInputFilename, fJsonSummaryPtr,
commit-bot@chromium.org4610a462014-04-29 19:39:22 +0000644 fUseChecksumBasedFilenames, &i);
edisonn@google.com84f548c2012-12-18 22:24:03 +0000645 if (NULL != out) {
646 if (fCanvas->readPixels(&bitmap, 0, 0)) {
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +0000647 // Add this tile to the entire bitmap.
648 bitmapCopyAtOffset(bitmap, *out, SkScalarFloorToInt(fTileRects[i].left()),
649 SkScalarFloorToInt(fTileRects[i].top()));
edisonn@google.com84f548c2012-12-18 22:24:03 +0000650 } else {
651 success = false;
652 }
653 }
keyar@chromium.org163b5672012-08-01 17:53:29 +0000654 }
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000655 return success;
keyar@chromium.org163b5672012-08-01 17:53:29 +0000656}
657
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000658SkCanvas* TiledPictureRenderer::setupCanvas(int width, int height) {
659 SkCanvas* canvas = this->INHERITED::setupCanvas(width, height);
commit-bot@chromium.org8ddc26b2014-03-31 17:55:12 +0000660 SkASSERT(NULL != fPicture);
scroggo@google.com82ec0b02012-12-17 19:25:54 +0000661 // Clip the tile to an area that is completely inside both the SkPicture and the viewport. This
662 // is mostly important for tiles on the right and bottom edges as they may go over this area and
663 // the picture may have some commands that draw outside of this area and so should not actually
664 // be written.
665 // Uses a clipRegion so that it will be unaffected by the scale factor, which may have been set
666 // by INHERITED::setupCanvas.
667 SkRegion clipRegion;
668 clipRegion.setRect(0, 0, this->getViewWidth(), this->getViewHeight());
669 canvas->clipRegion(clipRegion);
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000670 return canvas;
671}
scroggo@google.com0a049b82012-11-02 22:01:26 +0000672
673SkString TiledPictureRenderer::getConfigNameInternal() {
674 SkString name;
675 if (fTileMinPowerOf2Width > 0) {
676 name.append("pow2tile_");
677 name.appendf("%i", fTileMinPowerOf2Width);
678 } else {
679 name.append("tile_");
680 if (fTileWidthPercentage > 0) {
681 name.appendf("%.f%%", fTileWidthPercentage);
682 } else {
683 name.appendf("%i", fTileWidth);
684 }
685 }
686 name.append("x");
687 if (fTileHeightPercentage > 0) {
688 name.appendf("%.f%%", fTileHeightPercentage);
689 } else {
690 name.appendf("%i", fTileHeight);
691 }
692 return name;
693}
694
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000695///////////////////////////////////////////////////////////////////////////////////////////////
696
697// Holds all of the information needed to draw a set of tiles.
698class CloneData : public SkRunnable {
699
700public:
701 CloneData(SkPicture* clone, SkCanvas* canvas, SkTDArray<SkRect>& rects, int start, int end,
commit-bot@chromium.org205ce482014-05-12 15:37:20 +0000702 SkRunnable* done, ImageResultsAndExpectations* jsonSummaryPtr,
703 bool useChecksumBasedFilenames)
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000704 : fClone(clone)
705 , fCanvas(canvas)
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000706 , fRects(rects)
707 , fStart(start)
708 , fEnd(end)
709 , fSuccess(NULL)
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +0000710 , fDone(done)
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000711 , fJsonSummaryPtr(jsonSummaryPtr)
712 , fUseChecksumBasedFilenames(useChecksumBasedFilenames) {
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000713 SkASSERT(fDone != NULL);
714 }
715
716 virtual void run() SK_OVERRIDE {
717 SkGraphics::SetTLSFontCacheLimit(1024 * 1024);
edisonn@google.com84f548c2012-12-18 22:24:03 +0000718
719 SkBitmap bitmap;
720 if (fBitmap != NULL) {
721 // All tiles are the same size.
jvanverth@google.com9c4e5ac2013-01-07 18:41:28 +0000722 setup_bitmap(&bitmap, SkScalarFloorToInt(fRects[0].width()), SkScalarFloorToInt(fRects[0].height()));
edisonn@google.com84f548c2012-12-18 22:24:03 +0000723 }
skia.committer@gmail.coma7d8e3e2012-12-19 02:01:38 +0000724
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000725 for (int i = fStart; i < fEnd; i++) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +0000726 draw_tile_to_canvas(fCanvas, fRects[i], fClone);
commit-bot@chromium.org3f045172014-05-15 15:10:48 +0000727 if (!write(fCanvas, fWritePath, fMismatchPath, fInputFilename, fJsonSummaryPtr,
commit-bot@chromium.org4610a462014-04-29 19:39:22 +0000728 fUseChecksumBasedFilenames, &i)
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000729 && fSuccess != NULL) {
730 *fSuccess = false;
731 // If one tile fails to write to a file, do not continue drawing the rest.
732 break;
733 }
edisonn@google.com84f548c2012-12-18 22:24:03 +0000734 if (fBitmap != NULL) {
735 if (fCanvas->readPixels(&bitmap, 0, 0)) {
736 SkAutoLockPixels alp(*fBitmap);
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +0000737 bitmapCopyAtOffset(bitmap, fBitmap, SkScalarFloorToInt(fRects[i].left()),
738 SkScalarFloorToInt(fRects[i].top()));
edisonn@google.com84f548c2012-12-18 22:24:03 +0000739 } else {
740 *fSuccess = false;
741 // If one tile fails to read pixels, do not continue drawing the rest.
742 break;
743 }
744 }
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000745 }
746 fDone->run();
747 }
748
commit-bot@chromium.org3f045172014-05-15 15:10:48 +0000749 void setPathsAndSuccess(const SkString& writePath, const SkString& mismatchPath,
750 const SkString& inputFilename, bool* success) {
751 fWritePath.set(writePath);
752 fMismatchPath.set(mismatchPath);
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000753 fInputFilename.set(inputFilename);
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000754 fSuccess = success;
755 }
756
edisonn@google.com84f548c2012-12-18 22:24:03 +0000757 void setBitmap(SkBitmap* bitmap) {
758 fBitmap = bitmap;
759 }
760
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000761private:
762 // All pointers unowned.
763 SkPicture* fClone; // Picture to draw from. Each CloneData has a unique one which
764 // is threadsafe.
765 SkCanvas* fCanvas; // Canvas to draw to. Reused for each tile.
commit-bot@chromium.org3f045172014-05-15 15:10:48 +0000766 SkString fWritePath; // If not empty, write all results into this directory.
767 SkString fMismatchPath; // If not empty, write all unexpected results into this dir.
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000768 SkString fInputFilename; // Filename of input SkPicture file.
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000769 SkTDArray<SkRect>& fRects; // All tiles of the picture.
770 const int fStart; // Range of tiles drawn by this thread.
771 const int fEnd;
772 bool* fSuccess; // Only meaningful if path is non-null. Shared by all threads,
773 // and only set to false upon failure to write to a PNG.
774 SkRunnable* fDone;
edisonn@google.com84f548c2012-12-18 22:24:03 +0000775 SkBitmap* fBitmap;
commit-bot@chromium.org205ce482014-05-12 15:37:20 +0000776 ImageResultsAndExpectations* fJsonSummaryPtr;
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000777 bool fUseChecksumBasedFilenames;
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000778};
779
780MultiCorePictureRenderer::MultiCorePictureRenderer(int threadCount)
781: fNumThreads(threadCount)
782, fThreadPool(threadCount)
783, fCountdown(threadCount) {
784 // Only need to create fNumThreads - 1 clones, since one thread will use the base
785 // picture.
786 fPictureClones = SkNEW_ARRAY(SkPicture, fNumThreads - 1);
787 fCloneData = SkNEW_ARRAY(CloneData*, fNumThreads);
788}
789
commit-bot@chromium.org3f045172014-05-15 15:10:48 +0000790void MultiCorePictureRenderer::init(SkPicture *pict, const SkString* writePath,
791 const SkString* mismatchPath, const SkString* inputFilename,
792 bool useChecksumBasedFilenames) {
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000793 // Set fPicture and the tiles.
commit-bot@chromium.org3f045172014-05-15 15:10:48 +0000794 this->INHERITED::init(pict, writePath, mismatchPath, inputFilename, useChecksumBasedFilenames);
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000795 for (int i = 0; i < fNumThreads; ++i) {
796 *fCanvasPool.append() = this->setupCanvas(this->getTileWidth(), this->getTileHeight());
797 }
798 // Only need to create fNumThreads - 1 clones, since one thread will use the base picture.
799 fPicture->clone(fPictureClones, fNumThreads - 1);
800 // Populate each thread with the appropriate data.
801 // Group the tiles into nearly equal size chunks, rounding up so we're sure to cover them all.
802 const int chunkSize = (fTileRects.count() + fNumThreads - 1) / fNumThreads;
803
804 for (int i = 0; i < fNumThreads; i++) {
805 SkPicture* pic;
806 if (i == fNumThreads-1) {
807 // The last set will use the original SkPicture.
808 pic = fPicture;
809 } else {
810 pic = &fPictureClones[i];
811 }
812 const int start = i * chunkSize;
813 const int end = SkMin32(start + chunkSize, fTileRects.count());
814 fCloneData[i] = SkNEW_ARGS(CloneData,
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +0000815 (pic, fCanvasPool[i], fTileRects, start, end, &fCountdown,
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000816 fJsonSummaryPtr, useChecksumBasedFilenames));
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000817 }
818}
819
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000820bool MultiCorePictureRenderer::render(SkBitmap** out) {
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000821 bool success = true;
commit-bot@chromium.org3f045172014-05-15 15:10:48 +0000822 if (!fWritePath.isEmpty() || !fMismatchPath.isEmpty()) {
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000823 for (int i = 0; i < fNumThreads-1; i++) {
commit-bot@chromium.org3f045172014-05-15 15:10:48 +0000824 fCloneData[i]->setPathsAndSuccess(fWritePath, fMismatchPath, fInputFilename, &success);
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000825 }
826 }
827
edisonn@google.com84f548c2012-12-18 22:24:03 +0000828 if (NULL != out) {
829 *out = SkNEW(SkBitmap);
830 setup_bitmap(*out, fPicture->width(), fPicture->height());
831 for (int i = 0; i < fNumThreads; i++) {
832 fCloneData[i]->setBitmap(*out);
833 }
834 } else {
835 for (int i = 0; i < fNumThreads; i++) {
836 fCloneData[i]->setBitmap(NULL);
837 }
838 }
839
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000840 fCountdown.reset(fNumThreads);
841 for (int i = 0; i < fNumThreads; i++) {
842 fThreadPool.add(fCloneData[i]);
843 }
844 fCountdown.wait();
845
846 return success;
847}
848
849void MultiCorePictureRenderer::end() {
850 for (int i = 0; i < fNumThreads - 1; i++) {
851 SkDELETE(fCloneData[i]);
852 fCloneData[i] = NULL;
853 }
854
855 fCanvasPool.unrefAll();
856
857 this->INHERITED::end();
858}
859
860MultiCorePictureRenderer::~MultiCorePictureRenderer() {
861 // Each individual CloneData was deleted in end.
862 SkDELETE_ARRAY(fCloneData);
863 SkDELETE_ARRAY(fPictureClones);
864}
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000865
scroggo@google.com0a049b82012-11-02 22:01:26 +0000866SkString MultiCorePictureRenderer::getConfigNameInternal() {
867 SkString name = this->INHERITED::getConfigNameInternal();
868 name.appendf("_multi_%i_threads", fNumThreads);
869 return name;
870}
871
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000872///////////////////////////////////////////////////////////////////////////////////////////////
scroggo@google.com9a412522012-09-07 15:21:18 +0000873
874void PlaybackCreationRenderer::setup() {
commit-bot@chromium.org5fb2ce32014-04-17 23:35:06 +0000875 SkAutoTDelete<SkBBHFactory> factory(this->getFactory());
876 fRecorder.reset(SkNEW(SkPictureRecorder));
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000877 SkCanvas* canvas = fRecorder->beginRecording(this->getViewWidth(), this->getViewHeight(),
commit-bot@chromium.org5fb2ce32014-04-17 23:35:06 +0000878 factory.get(),
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000879 this->recordFlags());
880 this->scaleToScaleFactor(canvas);
881 canvas->drawPicture(*fPicture);
scroggo@google.com9a412522012-09-07 15:21:18 +0000882}
883
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000884bool PlaybackCreationRenderer::render(SkBitmap** out) {
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000885 fPicture.reset(fRecorder->endRecording());
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000886 // Since this class does not actually render, return false.
887 return false;
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000888}
889
scroggo@google.com0a049b82012-11-02 22:01:26 +0000890SkString PlaybackCreationRenderer::getConfigNameInternal() {
891 return SkString("playback_creation");
892}
893
junov@chromium.org9313ca42012-11-02 18:11:49 +0000894///////////////////////////////////////////////////////////////////////////////////////////////
895// SkPicture variants for each BBoxHierarchy type
896
commit-bot@chromium.org5fb2ce32014-04-17 23:35:06 +0000897SkBBHFactory* PictureRenderer::getFactory() {
junov@chromium.org9313ca42012-11-02 18:11:49 +0000898 switch (fBBoxHierarchyType) {
899 case kNone_BBoxHierarchyType:
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000900 return NULL;
commit-bot@chromium.orgc22d1392014-02-03 18:08:33 +0000901 case kQuadTree_BBoxHierarchyType:
commit-bot@chromium.org5fb2ce32014-04-17 23:35:06 +0000902 return SkNEW(SkQuadTreeFactory);
junov@chromium.org9313ca42012-11-02 18:11:49 +0000903 case kRTree_BBoxHierarchyType:
commit-bot@chromium.org5fb2ce32014-04-17 23:35:06 +0000904 return SkNEW(SkRTreeFactory);
junov@chromium.org7b537062012-11-06 18:58:43 +0000905 case kTileGrid_BBoxHierarchyType:
commit-bot@chromium.org5fb2ce32014-04-17 23:35:06 +0000906 return SkNEW_ARGS(SkTileGridFactory, (fGridInfo));
junov@chromium.org9313ca42012-11-02 18:11:49 +0000907 }
908 SkASSERT(0); // invalid bbhType
909 return NULL;
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000910}
junov@chromium.org9313ca42012-11-02 18:11:49 +0000911
reed@google.comfe7b1ed2012-11-29 21:00:39 +0000912///////////////////////////////////////////////////////////////////////////////
913
914class GatherRenderer : public PictureRenderer {
915public:
commit-bot@chromium.org8ddc26b2014-03-31 17:55:12 +0000916 virtual bool render(SkBitmap** out = NULL) SK_OVERRIDE {
reed@google.comfe7b1ed2012-11-29 21:00:39 +0000917 SkRect bounds = SkRect::MakeWH(SkIntToScalar(fPicture->width()),
918 SkIntToScalar(fPicture->height()));
919 SkData* data = SkPictureUtils::GatherPixelRefs(fPicture, bounds);
920 SkSafeUnref(data);
skia.committer@gmail.comc7b4be72012-12-11 02:01:20 +0000921
commit-bot@chromium.org3f045172014-05-15 15:10:48 +0000922 return (fWritePath.isEmpty()); // we don't have anything to write
reed@google.comfe7b1ed2012-11-29 21:00:39 +0000923 }
skia.committer@gmail.comc7b4be72012-12-11 02:01:20 +0000924
reed@google.comfe7b1ed2012-11-29 21:00:39 +0000925private:
926 virtual SkString getConfigNameInternal() SK_OVERRIDE {
927 return SkString("gather_pixelrefs");
928 }
929};
930
931PictureRenderer* CreateGatherPixelRefsRenderer() {
932 return SkNEW(GatherRenderer);
933}
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +0000934
reed@google.com5a34fd32012-12-10 16:05:09 +0000935///////////////////////////////////////////////////////////////////////////////
936
937class PictureCloneRenderer : public PictureRenderer {
938public:
commit-bot@chromium.org8ddc26b2014-03-31 17:55:12 +0000939 virtual bool render(SkBitmap** out = NULL) SK_OVERRIDE {
reed@google.com5a34fd32012-12-10 16:05:09 +0000940 for (int i = 0; i < 100; ++i) {
941 SkPicture* clone = fPicture->clone();
942 SkSafeUnref(clone);
943 }
skia.committer@gmail.comc7b4be72012-12-11 02:01:20 +0000944
commit-bot@chromium.org3f045172014-05-15 15:10:48 +0000945 return (fWritePath.isEmpty()); // we don't have anything to write
reed@google.com5a34fd32012-12-10 16:05:09 +0000946 }
skia.committer@gmail.comc7b4be72012-12-11 02:01:20 +0000947
reed@google.com5a34fd32012-12-10 16:05:09 +0000948private:
949 virtual SkString getConfigNameInternal() SK_OVERRIDE {
950 return SkString("picture_clone");
951 }
952};
953
954PictureRenderer* CreatePictureCloneRenderer() {
955 return SkNEW(PictureCloneRenderer);
956}
957
junov@chromium.org9313ca42012-11-02 18:11:49 +0000958} // namespace sk_tools