blob: c712ae672fbfae196bc16504fef214b9d3bcbdd2 [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.org24c568c2014-04-10 15:39:02 +000051/*
52 * TODO(epoger): Make constant strings consistent instead of mixing hypenated and camel-caps.
53 *
54 * TODO(epoger): Similar constants are already maintained in 2 other places:
55 * gm/gm_json.py and gm/gm_expectations.cpp. We shouldn't add yet a third place.
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +000056 * Figure out a way to share the definitions instead.
commit-bot@chromium.org24c568c2014-04-10 15:39:02 +000057 *
58 * Note that, as of https://codereview.chromium.org/226293002 , the JSON
59 * schema used here has started to differ from the one in gm_expectations.cpp .
60 * TODO(epoger): Consider getting GM and render_pictures to use the same JSON
61 * output module.
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +000062 */
commit-bot@chromium.org24c568c2014-04-10 15:39:02 +000063const static char kJsonKey_ActualResults[] = "actual-results";
64const static char kJsonKey_Header[] = "header";
65const static char kJsonKey_Header_Type[] = "type";
66const static char kJsonKey_Header_Revision[] = "revision"; // unique within Type
67const static char kJsonKey_Image_ChecksumAlgorithm[] = "checksumAlgorithm";
68const static char kJsonKey_Image_ChecksumValue[] = "checksumValue";
69const static char kJsonKey_Image_ComparisonResult[] = "comparisonResult";
70const static char kJsonKey_Image_Filepath[] = "filepath";
71const static char kJsonKey_Source_TiledImages[] = "tiled-images";
72const static char kJsonKey_Source_WholeImage[] = "whole-image";
73// Values (not keys) that are written out by this JSON generator
74const static char kJsonValue_Header_Type[] = "ChecksummedImages";
75const static int kJsonValue_Header_Revision = 1;
76const static char kJsonValue_Image_ChecksumAlgorithm_Bitmap64bitMD5[] = "bitmap-64bitMD5";
77const static char kJsonValue_Image_ComparisonResult_NoComparison[] = "no-comparison";
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +000078
commit-bot@chromium.org24c568c2014-04-10 15:39:02 +000079void ImageResultsSummary::add(const char *sourceName, const char *fileName, uint64_t hash,
80 const int *tileNumber) {
81 Json::Value image;
82 image[kJsonKey_Image_ChecksumAlgorithm] = kJsonValue_Image_ChecksumAlgorithm_Bitmap64bitMD5;
83 image[kJsonKey_Image_ChecksumValue] = Json::UInt64(hash);
84 image[kJsonKey_Image_ComparisonResult] = kJsonValue_Image_ComparisonResult_NoComparison;
85 image[kJsonKey_Image_Filepath] = fileName;
86 if (NULL == tileNumber) {
87 fActualResults[sourceName][kJsonKey_Source_WholeImage] = image;
88 } else {
89 fActualResults[sourceName][kJsonKey_Source_TiledImages][*tileNumber] = image;
90 }
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +000091}
92
commit-bot@chromium.org24c568c2014-04-10 15:39:02 +000093void ImageResultsSummary::add(const char *sourceName, const char *fileName, const SkBitmap& bitmap,
94 const int *tileNumber) {
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +000095 uint64_t hash;
96 SkAssertResult(SkBitmapHasher::ComputeDigest(bitmap, &hash));
commit-bot@chromium.org24c568c2014-04-10 15:39:02 +000097 this->add(sourceName, fileName, hash, tileNumber);
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +000098}
99
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +0000100void ImageResultsSummary::writeToFile(const char *filename) {
commit-bot@chromium.org24c568c2014-04-10 15:39:02 +0000101 Json::Value header;
102 header[kJsonKey_Header_Type] = kJsonValue_Header_Type;
103 header[kJsonKey_Header_Revision] = kJsonValue_Header_Revision;
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +0000104 Json::Value root;
commit-bot@chromium.org24c568c2014-04-10 15:39:02 +0000105 root[kJsonKey_Header] = header;
106 root[kJsonKey_ActualResults] = fActualResults;
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +0000107 std::string jsonStdString = root.toStyledString();
108 SkFILEWStream stream(filename);
109 stream.write(jsonStdString.c_str(), jsonStdString.length());
110}
111
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000112void PictureRenderer::init(SkPicture* pict, const SkString* outputDir,
113 const SkString* inputFilename, bool useChecksumBasedFilenames) {
114 this->CopyString(&fOutputDir, outputDir);
115 this->CopyString(&fInputFilename, inputFilename);
116 fUseChecksumBasedFilenames = useChecksumBasedFilenames;
117
keyar@chromium.org78a35c52012-08-20 15:03:44 +0000118 SkASSERT(NULL == fPicture);
119 SkASSERT(NULL == fCanvas.get());
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000120 if (NULL != fPicture || NULL != fCanvas.get()) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000121 return;
122 }
123
124 SkASSERT(pict != NULL);
keyar@chromium.org78a35c52012-08-20 15:03:44 +0000125 if (NULL == pict) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000126 return;
127 }
128
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000129 fPicture.reset(pict)->ref();
keyar@chromium.orga474ce32012-08-20 15:03:57 +0000130 fCanvas.reset(this->setupCanvas());
131}
132
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000133void PictureRenderer::CopyString(SkString* dest, const SkString* src) {
134 if (NULL != src) {
135 dest->set(*src);
136 } else {
137 dest->reset();
138 }
139}
140
caryclark@google.coma3622372012-11-06 21:26:13 +0000141class FlagsDrawFilter : public SkDrawFilter {
142public:
143 FlagsDrawFilter(PictureRenderer::DrawFilterFlags* flags) :
144 fFlags(flags) {}
145
reed@google.com971aca72012-11-26 20:26:54 +0000146 virtual bool filter(SkPaint* paint, Type t) {
caryclark@google.coma3622372012-11-06 21:26:13 +0000147 paint->setFlags(paint->getFlags() & ~fFlags[t] & SkPaint::kAllFlags);
robertphillips@google.com49149312013-07-03 15:34:35 +0000148 if (PictureRenderer::kMaskFilter_DrawFilterFlag & fFlags[t]) {
caryclark@google.coma3622372012-11-06 21:26:13 +0000149 SkMaskFilter* maskFilter = paint->getMaskFilter();
robertphillips@google.com49149312013-07-03 15:34:35 +0000150 if (NULL != maskFilter) {
reed@google.com457d8a72012-12-18 18:20:44 +0000151 paint->setMaskFilter(NULL);
caryclark@google.coma3622372012-11-06 21:26:13 +0000152 }
153 }
154 if (PictureRenderer::kHinting_DrawFilterFlag & fFlags[t]) {
155 paint->setHinting(SkPaint::kNo_Hinting);
156 } else if (PictureRenderer::kSlightHinting_DrawFilterFlag & fFlags[t]) {
157 paint->setHinting(SkPaint::kSlight_Hinting);
158 }
reed@google.com971aca72012-11-26 20:26:54 +0000159 return true;
caryclark@google.coma3622372012-11-06 21:26:13 +0000160 }
161
162private:
163 PictureRenderer::DrawFilterFlags* fFlags;
164};
165
scroggo@google.com82ec0b02012-12-17 19:25:54 +0000166static void setUpFilter(SkCanvas* canvas, PictureRenderer::DrawFilterFlags* drawFilters) {
caryclark@google.coma3622372012-11-06 21:26:13 +0000167 if (drawFilters && !canvas->getDrawFilter()) {
168 canvas->setDrawFilter(SkNEW_ARGS(FlagsDrawFilter, (drawFilters)))->unref();
caryclark@google.come3e940c2012-11-07 16:42:17 +0000169 if (drawFilters[0] & PictureRenderer::kAAClip_DrawFilterFlag) {
170 canvas->setAllowSoftClip(false);
171 }
caryclark@google.coma3622372012-11-06 21:26:13 +0000172 }
caryclark@google.coma3622372012-11-06 21:26:13 +0000173}
174
keyar@chromium.orga474ce32012-08-20 15:03:57 +0000175SkCanvas* PictureRenderer::setupCanvas() {
scroggo@google.comc0d5e542012-12-13 21:40:48 +0000176 const int width = this->getViewWidth();
177 const int height = this->getViewHeight();
178 return this->setupCanvas(width, height);
keyar@chromium.orga474ce32012-08-20 15:03:57 +0000179}
180
181SkCanvas* PictureRenderer::setupCanvas(int width, int height) {
caryclark@google.coma3622372012-11-06 21:26:13 +0000182 SkCanvas* canvas;
keyar@chromium.org4ea96c52012-08-20 15:03:29 +0000183 switch(fDeviceType) {
184 case kBitmap_DeviceType: {
185 SkBitmap bitmap;
keyar@chromium.orga474ce32012-08-20 15:03:57 +0000186 sk_tools::setup_bitmap(&bitmap, width, height);
caryclark@google.coma3622372012-11-06 21:26:13 +0000187 canvas = SkNEW_ARGS(SkCanvas, (bitmap));
keyar@chromium.org4ea96c52012-08-20 15:03:29 +0000188 }
scroggo@google.com82ec0b02012-12-17 19:25:54 +0000189 break;
keyar@chromium.org4ea96c52012-08-20 15:03:29 +0000190#if SK_SUPPORT_GPU
scroggo@google.com0556ea02013-02-08 19:38:21 +0000191#if SK_ANGLE
192 case kAngle_DeviceType:
193 // fall through
194#endif
rmistry@google.com6ab96732014-01-06 18:37:24 +0000195#if SK_MESA
196 case kMesa_DeviceType:
197 // fall through
198#endif
commit-bot@chromium.org0fd52702014-03-07 18:41:14 +0000199 case kGPU_DeviceType:
200 case kNVPR_DeviceType: {
commit-bot@chromium.orgae403b92013-04-10 17:27:30 +0000201 SkAutoTUnref<GrSurface> target;
scroggo@google.com0556ea02013-02-08 19:38:21 +0000202 if (fGrContext) {
203 // create a render target to back the device
204 GrTextureDesc desc;
205 desc.fConfig = kSkia8888_GrPixelConfig;
206 desc.fFlags = kRenderTarget_GrTextureFlagBit;
207 desc.fWidth = width;
208 desc.fHeight = height;
jvanverth@google.comf6a90332013-05-02 12:39:37 +0000209 desc.fSampleCnt = fSampleCount;
commit-bot@chromium.orgae403b92013-04-10 17:27:30 +0000210 target.reset(fGrContext->createUncachedTexture(desc, NULL, 0));
scroggo@google.com0556ea02013-02-08 19:38:21 +0000211 }
commit-bot@chromium.orgae403b92013-04-10 17:27:30 +0000212 if (NULL == target.get()) {
scroggo@google.com0556ea02013-02-08 19:38:21 +0000213 SkASSERT(0);
214 return NULL;
215 }
216
commit-bot@chromium.orgae403b92013-04-10 17:27:30 +0000217 SkAutoTUnref<SkGpuDevice> device(SkGpuDevice::Create(target));
caryclark@google.coma3622372012-11-06 21:26:13 +0000218 canvas = SkNEW_ARGS(SkCanvas, (device.get()));
scroggo@google.com0556ea02013-02-08 19:38:21 +0000219 break;
keyar@chromium.org4ea96c52012-08-20 15:03:29 +0000220 }
221#endif
222 default:
223 SkASSERT(0);
scroggo@google.com82ec0b02012-12-17 19:25:54 +0000224 return NULL;
keyar@chromium.org4ea96c52012-08-20 15:03:29 +0000225 }
scroggo@google.com82ec0b02012-12-17 19:25:54 +0000226 setUpFilter(canvas, fDrawFilters);
227 this->scaleToScaleFactor(canvas);
commit-bot@chromium.org17cc3ea2014-01-15 14:51:25 +0000228
229 // Pictures often lie about their extent (i.e., claim to be 100x100 but
230 // only ever draw to 90x100). Clear here so the undrawn portion will have
231 // a consistent color
232 canvas->clear(SK_ColorTRANSPARENT);
scroggo@google.com82ec0b02012-12-17 19:25:54 +0000233 return canvas;
234}
keyar@chromium.orga474ce32012-08-20 15:03:57 +0000235
scroggo@google.com82ec0b02012-12-17 19:25:54 +0000236void PictureRenderer::scaleToScaleFactor(SkCanvas* canvas) {
237 SkASSERT(canvas != NULL);
238 if (fScaleFactor != SK_Scalar1) {
239 canvas->scale(fScaleFactor, fScaleFactor);
240 }
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000241}
242
243void PictureRenderer::end() {
scroggo@google.com08085f82013-01-28 20:40:24 +0000244 this->resetState(true);
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000245 fPicture.reset(NULL);
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000246 fCanvas.reset(NULL);
247}
248
scroggo@google.comc0d5e542012-12-13 21:40:48 +0000249int PictureRenderer::getViewWidth() {
250 SkASSERT(fPicture != NULL);
robertphillips@google.com8ac811e2013-02-07 00:13:34 +0000251 int width = SkScalarCeilToInt(fPicture->width() * fScaleFactor);
scroggo@google.comc0d5e542012-12-13 21:40:48 +0000252 if (fViewport.width() > 0) {
253 width = SkMin32(width, fViewport.width());
254 }
255 return width;
256}
257
258int PictureRenderer::getViewHeight() {
259 SkASSERT(fPicture != NULL);
robertphillips@google.com8ac811e2013-02-07 00:13:34 +0000260 int height = SkScalarCeilToInt(fPicture->height() * fScaleFactor);
scroggo@google.comc0d5e542012-12-13 21:40:48 +0000261 if (fViewport.height() > 0) {
262 height = SkMin32(height, fViewport.height());
263 }
264 return height;
265}
266
junov@chromium.org9313ca42012-11-02 18:11:49 +0000267/** Converts fPicture to a picture that uses a BBoxHierarchy.
268 * PictureRenderer subclasses that are used to test picture playback
269 * should call this method during init.
270 */
271void PictureRenderer::buildBBoxHierarchy() {
272 SkASSERT(NULL != fPicture);
273 if (kNone_BBoxHierarchyType != fBBoxHierarchyType && NULL != fPicture) {
commit-bot@chromium.org5fb2ce32014-04-17 23:35:06 +0000274 SkAutoTDelete<SkBBHFactory> factory(this->getFactory());
275 SkPictureRecorder recorder;
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000276 SkCanvas* canvas = recorder.beginRecording(fPicture->width(), fPicture->height(),
commit-bot@chromium.org5fb2ce32014-04-17 23:35:06 +0000277 factory.get(),
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000278 this->recordFlags());
279 fPicture->draw(canvas);
280 fPicture.reset(recorder.endRecording());
junov@chromium.org9313ca42012-11-02 18:11:49 +0000281 }
282}
283
scroggo@google.com08085f82013-01-28 20:40:24 +0000284void PictureRenderer::resetState(bool callFinish) {
keyar@chromium.org28136b32012-08-20 15:04:15 +0000285#if SK_SUPPORT_GPU
robertphillips@google.com6177e692013-02-28 20:16:25 +0000286 SkGLContextHelper* glContext = this->getGLContext();
scroggo@google.com0556ea02013-02-08 19:38:21 +0000287 if (NULL == glContext) {
288 SkASSERT(kBitmap_DeviceType == fDeviceType);
289 return;
290 }
keyar@chromium.org28136b32012-08-20 15:04:15 +0000291
scroggo@google.com0556ea02013-02-08 19:38:21 +0000292 fGrContext->flush();
commit-bot@chromium.org51c040e2014-03-11 22:58:00 +0000293 glContext->swapBuffers();
scroggo@google.com0556ea02013-02-08 19:38:21 +0000294 if (callFinish) {
295 SK_GL(*glContext, Finish());
keyar@chromium.org77a55222012-08-20 15:03:47 +0000296 }
keyar@chromium.orga40c20d2012-08-20 15:04:12 +0000297#endif
keyar@chromium.org77a55222012-08-20 15:03:47 +0000298}
299
robertphillips@google.com94d8f1e2013-12-18 17:25:33 +0000300void PictureRenderer::purgeTextures() {
301 SkDiscardableMemoryPool* pool = SkGetGlobalDiscardableMemoryPool();
302
303 pool->dumpPool();
304
305#if SK_SUPPORT_GPU
306 SkGLContextHelper* glContext = this->getGLContext();
307 if (NULL == glContext) {
308 SkASSERT(kBitmap_DeviceType == fDeviceType);
309 return;
310 }
311
312 // resetState should've already done this
313 fGrContext->flush();
314
315 fGrContext->purgeAllUnlockedResources();
316#endif
317}
318
junov@chromium.org9313ca42012-11-02 18:11:49 +0000319uint32_t PictureRenderer::recordFlags() {
skia.committer@gmail.com667b98d2014-04-17 03:05:10 +0000320 return (kNone_BBoxHierarchyType == fBBoxHierarchyType)
commit-bot@chromium.orgd393b172014-04-16 16:02:10 +0000321 ? 0
322 : SkPicture::kUsePathBoundsForClip_RecordingFlag;
junov@chromium.org9313ca42012-11-02 18:11:49 +0000323}
324
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000325/**
commit-bot@chromium.org4610a462014-04-29 19:39:22 +0000326 * Write the canvas to an image file and/or JSON summary.
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000327 *
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000328 * @param canvas Must be non-null. Canvas to be written to a file.
commit-bot@chromium.org4610a462014-04-29 19:39:22 +0000329 * @param outputDir If nonempty, write the binary image to a file within this directory;
330 * if empty, don't write out the image at all.
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000331 * @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 +0000332 * @param jsonSummaryPtr If not null, add image results (checksum) to this summary.
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000333 * @param useChecksumBasedFilenames If true, use checksum-based filenames when writing to disk.
commit-bot@chromium.org24c568c2014-04-10 15:39:02 +0000334 * @param tileNumberPtr If not null, which tile number this image contains.
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +0000335 *
commit-bot@chromium.org4610a462014-04-29 19:39:22 +0000336 * @return bool True if the operation completed successfully.
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000337 */
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000338static bool write(SkCanvas* canvas, const SkString& outputDir, const SkString& inputFilename,
339 ImageResultsSummary *jsonSummaryPtr, bool useChecksumBasedFilenames,
commit-bot@chromium.org24c568c2014-04-10 15:39:02 +0000340 const int* tileNumberPtr=NULL) {
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000341 SkASSERT(canvas != NULL);
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000342 if (NULL == canvas) {
keyar@chromium.org9299ede2012-08-21 19:05:08 +0000343 return false;
344 }
345
346 SkBitmap bitmap;
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000347 SkISize size = canvas->getDeviceSize();
348 sk_tools::setup_bitmap(&bitmap, size.width(), size.height());
keyar@chromium.org9299ede2012-08-21 19:05:08 +0000349
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000350 // Make sure we only compute the bitmap hash once (at most).
351 uint64_t hash;
352 bool generatedHash = false;
353
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000354 canvas->readPixels(&bitmap, 0, 0);
keyar@chromium.org9299ede2012-08-21 19:05:08 +0000355 sk_tools::force_all_opaque(bitmap);
356
commit-bot@chromium.org24c568c2014-04-10 15:39:02 +0000357 SkString escapedInputFilename(inputFilename);
358 replace_char(&escapedInputFilename, '.', '_');
359
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000360 // TODO(epoger): what about including the config type within outputFilename? That way,
361 // we could combine results of different config types without conflicting filenames.
commit-bot@chromium.org24c568c2014-04-10 15:39:02 +0000362 SkString outputFilename;
363 const char *outputSubdirPtr = NULL;
364 if (useChecksumBasedFilenames) {
commit-bot@chromium.orgb470c212014-03-19 22:58:52 +0000365 SkASSERT(!generatedHash);
366 SkAssertResult(SkBitmapHasher::ComputeDigest(bitmap, &hash));
367 generatedHash = true;
368
commit-bot@chromium.org24c568c2014-04-10 15:39:02 +0000369 outputSubdirPtr = escapedInputFilename.c_str();
370 outputFilename.set(kJsonValue_Image_ChecksumAlgorithm_Bitmap64bitMD5);
371 outputFilename.append("_");
372 outputFilename.appendU64(hash);
373 } else {
374 outputFilename.set(escapedInputFilename);
375 if (NULL != tileNumberPtr) {
376 outputFilename.append("-tile");
377 outputFilename.appendS32(*tileNumberPtr);
378 }
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +0000379 }
commit-bot@chromium.org24c568c2014-04-10 15:39:02 +0000380 outputFilename.append(".png");
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +0000381
commit-bot@chromium.org24c568c2014-04-10 15:39:02 +0000382 if (NULL != jsonSummaryPtr) {
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000383 if (!generatedHash) {
384 SkAssertResult(SkBitmapHasher::ComputeDigest(bitmap, &hash));
385 generatedHash = true;
386 }
commit-bot@chromium.org24c568c2014-04-10 15:39:02 +0000387
388 SkString outputRelativePath;
389 if (outputSubdirPtr) {
390 outputRelativePath.set(outputSubdirPtr);
391 outputRelativePath.append("/"); // always use "/", even on Windows
392 outputRelativePath.append(outputFilename);
393 } else {
394 outputRelativePath.set(outputFilename);
395 }
396
397 jsonSummaryPtr->add(inputFilename.c_str(), outputRelativePath.c_str(),
398 hash, tileNumberPtr);
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000399 }
400
commit-bot@chromium.org4610a462014-04-29 19:39:22 +0000401 if (outputDir.isEmpty()) {
402 return true;
403 }
404
commit-bot@chromium.org24c568c2014-04-10 15:39:02 +0000405 SkString dirPath;
406 if (outputSubdirPtr) {
407 dirPath = SkOSPath::SkPathJoin(outputDir.c_str(), outputSubdirPtr);
408 sk_mkdir(dirPath.c_str());
409 } else {
410 dirPath.set(outputDir);
411 }
412 SkString fullPath = SkOSPath::SkPathJoin(dirPath.c_str(), outputFilename.c_str());
413 return SkImageEncoder::EncodeFile(fullPath.c_str(), bitmap, SkImageEncoder::kPNG_Type, 100);
keyar@chromium.org9299ede2012-08-21 19:05:08 +0000414}
415
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000416///////////////////////////////////////////////////////////////////////////////////////////////
417
djsollen@google.comfd9720c2012-11-06 16:54:40 +0000418SkCanvas* RecordPictureRenderer::setupCanvas(int width, int height) {
419 // defer the canvas setup until the render step
420 return NULL;
421}
422
reed@google.com672588b2014-01-08 15:42:01 +0000423// the size_t* parameter is deprecated, so we ignore it
424static SkData* encode_bitmap_to_data(size_t*, const SkBitmap& bm) {
scroggo@google.com1b1bcc32013-05-21 20:31:23 +0000425 return SkImageEncoder::EncodeData(bm, SkImageEncoder::kPNG_Type, 100);
scroggo@google.coma9e3a362012-11-07 17:52:48 +0000426}
427
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000428bool RecordPictureRenderer::render(SkBitmap** out) {
commit-bot@chromium.org5fb2ce32014-04-17 23:35:06 +0000429 SkAutoTDelete<SkBBHFactory> factory(this->getFactory());
430 SkPictureRecorder recorder;
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000431 SkCanvas* canvas = recorder.beginRecording(this->getViewWidth(), this->getViewHeight(),
commit-bot@chromium.org5fb2ce32014-04-17 23:35:06 +0000432 factory.get(),
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000433 this->recordFlags());
434 this->scaleToScaleFactor(canvas);
435 fPicture->draw(canvas);
436 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000437 if (!fOutputDir.isEmpty()) {
scroggo@google.coma9e3a362012-11-07 17:52:48 +0000438 // Record the new picture as a new SKP with PNG encoded bitmaps.
commit-bot@chromium.org24c568c2014-04-10 15:39:02 +0000439 SkString skpPath = SkOSPath::SkPathJoin(fOutputDir.c_str(), fInputFilename.c_str());
scroggo@google.coma9e3a362012-11-07 17:52:48 +0000440 SkFILEWStream stream(skpPath.c_str());
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000441 picture->serialize(&stream, &encode_bitmap_to_data);
scroggo@google.coma9e3a362012-11-07 17:52:48 +0000442 return true;
443 }
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000444 return false;
scroggo@google.com9a412522012-09-07 15:21:18 +0000445}
446
scroggo@google.com0a049b82012-11-02 22:01:26 +0000447SkString RecordPictureRenderer::getConfigNameInternal() {
448 return SkString("record");
449}
450
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000451///////////////////////////////////////////////////////////////////////////////////////////////
452
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000453bool PipePictureRenderer::render(SkBitmap** out) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000454 SkASSERT(fCanvas.get() != NULL);
455 SkASSERT(fPicture != NULL);
keyar@chromium.org78a35c52012-08-20 15:03:44 +0000456 if (NULL == fCanvas.get() || NULL == fPicture) {
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000457 return false;
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000458 }
459
460 PipeController pipeController(fCanvas.get());
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000461 SkGPipeWriter writer;
462 SkCanvas* pipeCanvas = writer.startRecording(&pipeController);
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000463 pipeCanvas->drawPicture(*fPicture);
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000464 writer.endRecording();
scroggo@google.com9a412522012-09-07 15:21:18 +0000465 fCanvas->flush();
edisonn@google.com84f548c2012-12-18 22:24:03 +0000466 if (NULL != out) {
467 *out = SkNEW(SkBitmap);
468 setup_bitmap(*out, fPicture->width(), fPicture->height());
469 fCanvas->readPixels(*out, 0, 0);
skia.committer@gmail.coma7d8e3e2012-12-19 02:01:38 +0000470 }
commit-bot@chromium.org4610a462014-04-29 19:39:22 +0000471 return write(fCanvas, fOutputDir, fInputFilename, fJsonSummaryPtr,
472 fUseChecksumBasedFilenames);
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000473}
474
scroggo@google.com0a049b82012-11-02 22:01:26 +0000475SkString PipePictureRenderer::getConfigNameInternal() {
476 return SkString("pipe");
477}
478
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000479///////////////////////////////////////////////////////////////////////////////////////////////
480
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000481void SimplePictureRenderer::init(SkPicture* picture, const SkString* outputDir,
482 const SkString* inputFilename, bool useChecksumBasedFilenames) {
483 INHERITED::init(picture, outputDir, inputFilename, useChecksumBasedFilenames);
junov@chromium.org9313ca42012-11-02 18:11:49 +0000484 this->buildBBoxHierarchy();
485}
486
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000487bool SimplePictureRenderer::render(SkBitmap** out) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000488 SkASSERT(fCanvas.get() != NULL);
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000489 SkASSERT(NULL != fPicture);
keyar@chromium.org78a35c52012-08-20 15:03:44 +0000490 if (NULL == fCanvas.get() || NULL == fPicture) {
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000491 return false;
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000492 }
493
494 fCanvas->drawPicture(*fPicture);
scroggo@google.com9a412522012-09-07 15:21:18 +0000495 fCanvas->flush();
edisonn@google.com84f548c2012-12-18 22:24:03 +0000496 if (NULL != out) {
497 *out = SkNEW(SkBitmap);
498 setup_bitmap(*out, fPicture->width(), fPicture->height());
499 fCanvas->readPixels(*out, 0, 0);
500 }
commit-bot@chromium.org4610a462014-04-29 19:39:22 +0000501 return write(fCanvas, fOutputDir, fInputFilename, fJsonSummaryPtr,
502 fUseChecksumBasedFilenames);
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000503}
504
scroggo@google.com0a049b82012-11-02 22:01:26 +0000505SkString SimplePictureRenderer::getConfigNameInternal() {
506 return SkString("simple");
507}
508
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000509///////////////////////////////////////////////////////////////////////////////////////////////
510
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000511TiledPictureRenderer::TiledPictureRenderer()
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000512 : fTileWidth(kDefaultTileWidth)
rileya@google.comb947b912012-08-29 17:35:07 +0000513 , fTileHeight(kDefaultTileHeight)
rileya@google.coma04dc022012-09-10 19:01:38 +0000514 , fTileWidthPercentage(0.0)
rileya@google.comb947b912012-08-29 17:35:07 +0000515 , fTileHeightPercentage(0.0)
scroggo@google.comcbcef702012-12-13 22:09:28 +0000516 , fTileMinPowerOf2Width(0)
517 , fCurrentTileOffset(-1)
518 , fTilesX(0)
519 , fTilesY(0) { }
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000520
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000521void TiledPictureRenderer::init(SkPicture* pict, const SkString* outputDir,
522 const SkString* inputFilename, bool useChecksumBasedFilenames) {
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000523 SkASSERT(NULL != pict);
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000524 SkASSERT(0 == fTileRects.count());
525 if (NULL == pict || fTileRects.count() != 0) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000526 return;
527 }
528
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000529 // Do not call INHERITED::init(), which would create a (potentially large) canvas which is not
530 // used by bench_pictures.
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000531 fPicture.reset(pict)->ref();
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000532 this->CopyString(&fOutputDir, outputDir);
533 this->CopyString(&fInputFilename, inputFilename);
534 fUseChecksumBasedFilenames = useChecksumBasedFilenames;
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000535 this->buildBBoxHierarchy();
keyar@chromium.orgcc6e5ef2012-07-27 20:09:26 +0000536
537 if (fTileWidthPercentage > 0) {
robertphillips@google.com5d8d1862012-08-15 14:36:41 +0000538 fTileWidth = sk_float_ceil2int(float(fTileWidthPercentage * fPicture->width() / 100));
keyar@chromium.orgcc6e5ef2012-07-27 20:09:26 +0000539 }
540 if (fTileHeightPercentage > 0) {
robertphillips@google.com5d8d1862012-08-15 14:36:41 +0000541 fTileHeight = sk_float_ceil2int(float(fTileHeightPercentage * fPicture->height() / 100));
keyar@chromium.orgcc6e5ef2012-07-27 20:09:26 +0000542 }
543
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000544 if (fTileMinPowerOf2Width > 0) {
545 this->setupPowerOf2Tiles();
546 } else {
547 this->setupTiles();
548 }
scroggo@google.comcbcef702012-12-13 22:09:28 +0000549 fCanvas.reset(this->setupCanvas(fTileWidth, fTileHeight));
550 // Initialize to -1 so that the first call to nextTile will set this up to draw tile 0 on the
551 // first call to drawCurrentTile.
552 fCurrentTileOffset = -1;
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000553}
554
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000555void TiledPictureRenderer::end() {
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000556 fTileRects.reset();
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000557 this->INHERITED::end();
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000558}
559
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000560void TiledPictureRenderer::setupTiles() {
scroggo@google.comc0d5e542012-12-13 21:40:48 +0000561 // Only use enough tiles to cover the viewport
562 const int width = this->getViewWidth();
563 const int height = this->getViewHeight();
564
scroggo@google.comcbcef702012-12-13 22:09:28 +0000565 fTilesX = fTilesY = 0;
scroggo@google.comc0d5e542012-12-13 21:40:48 +0000566 for (int tile_y_start = 0; tile_y_start < height; tile_y_start += fTileHeight) {
scroggo@google.comcbcef702012-12-13 22:09:28 +0000567 fTilesY++;
scroggo@google.comc0d5e542012-12-13 21:40:48 +0000568 for (int tile_x_start = 0; tile_x_start < width; tile_x_start += fTileWidth) {
scroggo@google.comcbcef702012-12-13 22:09:28 +0000569 if (0 == tile_y_start) {
570 // Only count tiles in the X direction on the first pass.
571 fTilesX++;
572 }
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000573 *fTileRects.append() = SkRect::MakeXYWH(SkIntToScalar(tile_x_start),
574 SkIntToScalar(tile_y_start),
575 SkIntToScalar(fTileWidth),
576 SkIntToScalar(fTileHeight));
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000577 }
578 }
579}
580
scroggo@google.comcbcef702012-12-13 22:09:28 +0000581bool TiledPictureRenderer::tileDimensions(int &x, int &y) {
582 if (fTileRects.count() == 0 || NULL == fPicture) {
583 return false;
584 }
585 x = fTilesX;
586 y = fTilesY;
587 return true;
588}
589
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000590// The goal of the powers of two tiles is to minimize the amount of wasted tile
591// space in the width-wise direction and then minimize the number of tiles. The
592// constraints are that every tile must have a pixel width that is a power of
593// two and also be of some minimal width (that is also a power of two).
594//
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000595// This is solved by first taking our picture size and rounding it up to the
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000596// multiple of the minimal width. The binary representation of this rounded
597// value gives us the tiles we need: a bit of value one means we need a tile of
598// that size.
599void TiledPictureRenderer::setupPowerOf2Tiles() {
scroggo@google.comc0d5e542012-12-13 21:40:48 +0000600 // Only use enough tiles to cover the viewport
601 const int width = this->getViewWidth();
602 const int height = this->getViewHeight();
603
604 int rounded_value = width;
605 if (width % fTileMinPowerOf2Width != 0) {
606 rounded_value = width - (width % fTileMinPowerOf2Width) + fTileMinPowerOf2Width;
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000607 }
608
reed@google.come15b2f52013-12-18 04:59:26 +0000609 int num_bits = SkScalarCeilToInt(scalar_log2(SkIntToScalar(width)));
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000610 int largest_possible_tile_size = 1 << num_bits;
611
scroggo@google.comcbcef702012-12-13 22:09:28 +0000612 fTilesX = fTilesY = 0;
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000613 // The tile height is constant for a particular picture.
scroggo@google.comc0d5e542012-12-13 21:40:48 +0000614 for (int tile_y_start = 0; tile_y_start < height; tile_y_start += fTileHeight) {
scroggo@google.comcbcef702012-12-13 22:09:28 +0000615 fTilesY++;
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000616 int tile_x_start = 0;
617 int current_width = largest_possible_tile_size;
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000618 // Set fTileWidth to be the width of the widest tile, so that each canvas is large enough
619 // to draw each tile.
620 fTileWidth = current_width;
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000621
622 while (current_width >= fTileMinPowerOf2Width) {
623 // It is very important this is a bitwise AND.
624 if (current_width & rounded_value) {
scroggo@google.comcbcef702012-12-13 22:09:28 +0000625 if (0 == tile_y_start) {
626 // Only count tiles in the X direction on the first pass.
627 fTilesX++;
628 }
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000629 *fTileRects.append() = SkRect::MakeXYWH(SkIntToScalar(tile_x_start),
630 SkIntToScalar(tile_y_start),
631 SkIntToScalar(current_width),
632 SkIntToScalar(fTileHeight));
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000633 tile_x_start += current_width;
634 }
635
636 current_width >>= 1;
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000637 }
638 }
639}
640
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000641/**
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +0000642 * Draw the specified picture to the canvas translated to rectangle provided, so that this mini
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000643 * canvas represents the rectangle's portion of the overall picture.
644 * Saves and restores so that the initial clip and matrix return to their state before this function
645 * is called.
646 */
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +0000647static void draw_tile_to_canvas(SkCanvas* canvas, const SkRect& tileRect, SkPicture* picture) {
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000648 int saveCount = canvas->save();
scroggo@google.com82ec0b02012-12-17 19:25:54 +0000649 // Translate so that we draw the correct portion of the picture.
650 // Perform a postTranslate so that the scaleFactor does not interfere with the positioning.
651 SkMatrix mat(canvas->getTotalMatrix());
652 mat.postTranslate(-tileRect.fLeft, -tileRect.fTop);
653 canvas->setMatrix(mat);
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +0000654 canvas->drawPicture(*picture);
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000655 canvas->restoreToCount(saveCount);
656 canvas->flush();
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000657}
658
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000659///////////////////////////////////////////////////////////////////////////////////////////////
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000660
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +0000661/**
662 * Copies the entirety of the src bitmap (typically a tile) into a portion of the dst bitmap.
663 * If the src bitmap is too large to fit within the dst bitmap after the x and y
664 * offsets have been applied, any excess will be ignored (so only the top-left portion of the
665 * src bitmap will be copied).
666 *
667 * @param src source bitmap
668 * @param dst destination bitmap
669 * @param xOffset x-offset within destination bitmap
670 * @param yOffset y-offset within destination bitmap
671 */
672static void bitmapCopyAtOffset(const SkBitmap& src, SkBitmap* dst,
673 int xOffset, int yOffset) {
674 for (int y = 0; y <src.height() && y + yOffset < dst->height() ; y++) {
675 for (int x = 0; x < src.width() && x + xOffset < dst->width() ; x++) {
676 *dst->getAddr32(xOffset + x, yOffset + y) = *src.getAddr32(x, y);
edisonn@google.com84f548c2012-12-18 22:24:03 +0000677 }
678 }
679}
680
scroggo@google.comcbcef702012-12-13 22:09:28 +0000681bool TiledPictureRenderer::nextTile(int &i, int &j) {
682 if (++fCurrentTileOffset < fTileRects.count()) {
683 i = fCurrentTileOffset % fTilesX;
684 j = fCurrentTileOffset / fTilesX;
685 return true;
686 }
687 return false;
688}
689
690void TiledPictureRenderer::drawCurrentTile() {
691 SkASSERT(fCurrentTileOffset >= 0 && fCurrentTileOffset < fTileRects.count());
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +0000692 draw_tile_to_canvas(fCanvas, fTileRects[fCurrentTileOffset], fPicture);
scroggo@google.comcbcef702012-12-13 22:09:28 +0000693}
694
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000695bool TiledPictureRenderer::render(SkBitmap** out) {
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000696 SkASSERT(fPicture != NULL);
697 if (NULL == fPicture) {
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000698 return false;
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000699 }
700
edisonn@google.com84f548c2012-12-18 22:24:03 +0000701 SkBitmap bitmap;
702 if (out){
703 *out = SkNEW(SkBitmap);
704 setup_bitmap(*out, fPicture->width(), fPicture->height());
705 setup_bitmap(&bitmap, fTileWidth, fTileHeight);
706 }
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000707 bool success = true;
708 for (int i = 0; i < fTileRects.count(); ++i) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +0000709 draw_tile_to_canvas(fCanvas, fTileRects[i], fPicture);
commit-bot@chromium.org4610a462014-04-29 19:39:22 +0000710 success &= write(fCanvas, fOutputDir, fInputFilename, fJsonSummaryPtr,
711 fUseChecksumBasedFilenames, &i);
edisonn@google.com84f548c2012-12-18 22:24:03 +0000712 if (NULL != out) {
713 if (fCanvas->readPixels(&bitmap, 0, 0)) {
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +0000714 // Add this tile to the entire bitmap.
715 bitmapCopyAtOffset(bitmap, *out, SkScalarFloorToInt(fTileRects[i].left()),
716 SkScalarFloorToInt(fTileRects[i].top()));
edisonn@google.com84f548c2012-12-18 22:24:03 +0000717 } else {
718 success = false;
719 }
720 }
keyar@chromium.org163b5672012-08-01 17:53:29 +0000721 }
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000722 return success;
keyar@chromium.org163b5672012-08-01 17:53:29 +0000723}
724
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000725SkCanvas* TiledPictureRenderer::setupCanvas(int width, int height) {
726 SkCanvas* canvas = this->INHERITED::setupCanvas(width, height);
commit-bot@chromium.org8ddc26b2014-03-31 17:55:12 +0000727 SkASSERT(NULL != fPicture);
scroggo@google.com82ec0b02012-12-17 19:25:54 +0000728 // Clip the tile to an area that is completely inside both the SkPicture and the viewport. This
729 // is mostly important for tiles on the right and bottom edges as they may go over this area and
730 // the picture may have some commands that draw outside of this area and so should not actually
731 // be written.
732 // Uses a clipRegion so that it will be unaffected by the scale factor, which may have been set
733 // by INHERITED::setupCanvas.
734 SkRegion clipRegion;
735 clipRegion.setRect(0, 0, this->getViewWidth(), this->getViewHeight());
736 canvas->clipRegion(clipRegion);
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000737 return canvas;
738}
scroggo@google.com0a049b82012-11-02 22:01:26 +0000739
740SkString TiledPictureRenderer::getConfigNameInternal() {
741 SkString name;
742 if (fTileMinPowerOf2Width > 0) {
743 name.append("pow2tile_");
744 name.appendf("%i", fTileMinPowerOf2Width);
745 } else {
746 name.append("tile_");
747 if (fTileWidthPercentage > 0) {
748 name.appendf("%.f%%", fTileWidthPercentage);
749 } else {
750 name.appendf("%i", fTileWidth);
751 }
752 }
753 name.append("x");
754 if (fTileHeightPercentage > 0) {
755 name.appendf("%.f%%", fTileHeightPercentage);
756 } else {
757 name.appendf("%i", fTileHeight);
758 }
759 return name;
760}
761
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000762///////////////////////////////////////////////////////////////////////////////////////////////
763
764// Holds all of the information needed to draw a set of tiles.
765class CloneData : public SkRunnable {
766
767public:
768 CloneData(SkPicture* clone, SkCanvas* canvas, SkTDArray<SkRect>& rects, int start, int end,
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000769 SkRunnable* done, ImageResultsSummary* jsonSummaryPtr, bool useChecksumBasedFilenames)
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000770 : fClone(clone)
771 , fCanvas(canvas)
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000772 , fRects(rects)
773 , fStart(start)
774 , fEnd(end)
775 , fSuccess(NULL)
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +0000776 , fDone(done)
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000777 , fJsonSummaryPtr(jsonSummaryPtr)
778 , fUseChecksumBasedFilenames(useChecksumBasedFilenames) {
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000779 SkASSERT(fDone != NULL);
780 }
781
782 virtual void run() SK_OVERRIDE {
783 SkGraphics::SetTLSFontCacheLimit(1024 * 1024);
edisonn@google.com84f548c2012-12-18 22:24:03 +0000784
785 SkBitmap bitmap;
786 if (fBitmap != NULL) {
787 // All tiles are the same size.
jvanverth@google.com9c4e5ac2013-01-07 18:41:28 +0000788 setup_bitmap(&bitmap, SkScalarFloorToInt(fRects[0].width()), SkScalarFloorToInt(fRects[0].height()));
edisonn@google.com84f548c2012-12-18 22:24:03 +0000789 }
skia.committer@gmail.coma7d8e3e2012-12-19 02:01:38 +0000790
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000791 for (int i = fStart; i < fEnd; i++) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +0000792 draw_tile_to_canvas(fCanvas, fRects[i], fClone);
commit-bot@chromium.org4610a462014-04-29 19:39:22 +0000793 if (!write(fCanvas, fOutputDir, fInputFilename, fJsonSummaryPtr,
794 fUseChecksumBasedFilenames, &i)
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000795 && fSuccess != NULL) {
796 *fSuccess = false;
797 // If one tile fails to write to a file, do not continue drawing the rest.
798 break;
799 }
edisonn@google.com84f548c2012-12-18 22:24:03 +0000800 if (fBitmap != NULL) {
801 if (fCanvas->readPixels(&bitmap, 0, 0)) {
802 SkAutoLockPixels alp(*fBitmap);
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +0000803 bitmapCopyAtOffset(bitmap, fBitmap, SkScalarFloorToInt(fRects[i].left()),
804 SkScalarFloorToInt(fRects[i].top()));
edisonn@google.com84f548c2012-12-18 22:24:03 +0000805 } else {
806 *fSuccess = false;
807 // If one tile fails to read pixels, do not continue drawing the rest.
808 break;
809 }
810 }
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000811 }
812 fDone->run();
813 }
814
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000815 void setPathsAndSuccess(const SkString& outputDir, const SkString& inputFilename,
816 bool* success) {
817 fOutputDir.set(outputDir);
818 fInputFilename.set(inputFilename);
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000819 fSuccess = success;
820 }
821
edisonn@google.com84f548c2012-12-18 22:24:03 +0000822 void setBitmap(SkBitmap* bitmap) {
823 fBitmap = bitmap;
824 }
825
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000826private:
827 // All pointers unowned.
828 SkPicture* fClone; // Picture to draw from. Each CloneData has a unique one which
829 // is threadsafe.
830 SkCanvas* fCanvas; // Canvas to draw to. Reused for each tile.
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000831 SkString fOutputDir; // If not empty, write results into this directory.
832 SkString fInputFilename; // Filename of input SkPicture file.
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000833 SkTDArray<SkRect>& fRects; // All tiles of the picture.
834 const int fStart; // Range of tiles drawn by this thread.
835 const int fEnd;
836 bool* fSuccess; // Only meaningful if path is non-null. Shared by all threads,
837 // and only set to false upon failure to write to a PNG.
838 SkRunnable* fDone;
edisonn@google.com84f548c2012-12-18 22:24:03 +0000839 SkBitmap* fBitmap;
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +0000840 ImageResultsSummary* fJsonSummaryPtr;
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000841 bool fUseChecksumBasedFilenames;
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000842};
843
844MultiCorePictureRenderer::MultiCorePictureRenderer(int threadCount)
845: fNumThreads(threadCount)
846, fThreadPool(threadCount)
847, fCountdown(threadCount) {
848 // Only need to create fNumThreads - 1 clones, since one thread will use the base
849 // picture.
850 fPictureClones = SkNEW_ARRAY(SkPicture, fNumThreads - 1);
851 fCloneData = SkNEW_ARRAY(CloneData*, fNumThreads);
852}
853
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000854void MultiCorePictureRenderer::init(SkPicture *pict, const SkString* outputDir,
855 const SkString* inputFilename, bool useChecksumBasedFilenames) {
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000856 // Set fPicture and the tiles.
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000857 this->INHERITED::init(pict, outputDir, inputFilename, useChecksumBasedFilenames);
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000858 for (int i = 0; i < fNumThreads; ++i) {
859 *fCanvasPool.append() = this->setupCanvas(this->getTileWidth(), this->getTileHeight());
860 }
861 // Only need to create fNumThreads - 1 clones, since one thread will use the base picture.
862 fPicture->clone(fPictureClones, fNumThreads - 1);
863 // Populate each thread with the appropriate data.
864 // Group the tiles into nearly equal size chunks, rounding up so we're sure to cover them all.
865 const int chunkSize = (fTileRects.count() + fNumThreads - 1) / fNumThreads;
866
867 for (int i = 0; i < fNumThreads; i++) {
868 SkPicture* pic;
869 if (i == fNumThreads-1) {
870 // The last set will use the original SkPicture.
871 pic = fPicture;
872 } else {
873 pic = &fPictureClones[i];
874 }
875 const int start = i * chunkSize;
876 const int end = SkMin32(start + chunkSize, fTileRects.count());
877 fCloneData[i] = SkNEW_ARGS(CloneData,
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +0000878 (pic, fCanvasPool[i], fTileRects, start, end, &fCountdown,
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000879 fJsonSummaryPtr, useChecksumBasedFilenames));
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000880 }
881}
882
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000883bool MultiCorePictureRenderer::render(SkBitmap** out) {
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000884 bool success = true;
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000885 if (!fOutputDir.isEmpty()) {
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000886 for (int i = 0; i < fNumThreads-1; i++) {
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000887 fCloneData[i]->setPathsAndSuccess(fOutputDir, fInputFilename, &success);
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000888 }
889 }
890
edisonn@google.com84f548c2012-12-18 22:24:03 +0000891 if (NULL != out) {
892 *out = SkNEW(SkBitmap);
893 setup_bitmap(*out, fPicture->width(), fPicture->height());
894 for (int i = 0; i < fNumThreads; i++) {
895 fCloneData[i]->setBitmap(*out);
896 }
897 } else {
898 for (int i = 0; i < fNumThreads; i++) {
899 fCloneData[i]->setBitmap(NULL);
900 }
901 }
902
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000903 fCountdown.reset(fNumThreads);
904 for (int i = 0; i < fNumThreads; i++) {
905 fThreadPool.add(fCloneData[i]);
906 }
907 fCountdown.wait();
908
909 return success;
910}
911
912void MultiCorePictureRenderer::end() {
913 for (int i = 0; i < fNumThreads - 1; i++) {
914 SkDELETE(fCloneData[i]);
915 fCloneData[i] = NULL;
916 }
917
918 fCanvasPool.unrefAll();
919
920 this->INHERITED::end();
921}
922
923MultiCorePictureRenderer::~MultiCorePictureRenderer() {
924 // Each individual CloneData was deleted in end.
925 SkDELETE_ARRAY(fCloneData);
926 SkDELETE_ARRAY(fPictureClones);
927}
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000928
scroggo@google.com0a049b82012-11-02 22:01:26 +0000929SkString MultiCorePictureRenderer::getConfigNameInternal() {
930 SkString name = this->INHERITED::getConfigNameInternal();
931 name.appendf("_multi_%i_threads", fNumThreads);
932 return name;
933}
934
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000935///////////////////////////////////////////////////////////////////////////////////////////////
scroggo@google.com9a412522012-09-07 15:21:18 +0000936
937void PlaybackCreationRenderer::setup() {
commit-bot@chromium.org5fb2ce32014-04-17 23:35:06 +0000938 SkAutoTDelete<SkBBHFactory> factory(this->getFactory());
939 fRecorder.reset(SkNEW(SkPictureRecorder));
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000940 SkCanvas* canvas = fRecorder->beginRecording(this->getViewWidth(), this->getViewHeight(),
commit-bot@chromium.org5fb2ce32014-04-17 23:35:06 +0000941 factory.get(),
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000942 this->recordFlags());
943 this->scaleToScaleFactor(canvas);
944 canvas->drawPicture(*fPicture);
scroggo@google.com9a412522012-09-07 15:21:18 +0000945}
946
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000947bool PlaybackCreationRenderer::render(SkBitmap** out) {
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000948 fPicture.reset(fRecorder->endRecording());
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000949 // Since this class does not actually render, return false.
950 return false;
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000951}
952
scroggo@google.com0a049b82012-11-02 22:01:26 +0000953SkString PlaybackCreationRenderer::getConfigNameInternal() {
954 return SkString("playback_creation");
955}
956
junov@chromium.org9313ca42012-11-02 18:11:49 +0000957///////////////////////////////////////////////////////////////////////////////////////////////
958// SkPicture variants for each BBoxHierarchy type
959
commit-bot@chromium.org5fb2ce32014-04-17 23:35:06 +0000960SkBBHFactory* PictureRenderer::getFactory() {
junov@chromium.org9313ca42012-11-02 18:11:49 +0000961 switch (fBBoxHierarchyType) {
962 case kNone_BBoxHierarchyType:
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000963 return NULL;
commit-bot@chromium.orgc22d1392014-02-03 18:08:33 +0000964 case kQuadTree_BBoxHierarchyType:
commit-bot@chromium.org5fb2ce32014-04-17 23:35:06 +0000965 return SkNEW(SkQuadTreeFactory);
junov@chromium.org9313ca42012-11-02 18:11:49 +0000966 case kRTree_BBoxHierarchyType:
commit-bot@chromium.org5fb2ce32014-04-17 23:35:06 +0000967 return SkNEW(SkRTreeFactory);
junov@chromium.org7b537062012-11-06 18:58:43 +0000968 case kTileGrid_BBoxHierarchyType:
commit-bot@chromium.org5fb2ce32014-04-17 23:35:06 +0000969 return SkNEW_ARGS(SkTileGridFactory, (fGridInfo));
junov@chromium.org9313ca42012-11-02 18:11:49 +0000970 }
971 SkASSERT(0); // invalid bbhType
972 return NULL;
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000973}
junov@chromium.org9313ca42012-11-02 18:11:49 +0000974
reed@google.comfe7b1ed2012-11-29 21:00:39 +0000975///////////////////////////////////////////////////////////////////////////////
976
977class GatherRenderer : public PictureRenderer {
978public:
commit-bot@chromium.org8ddc26b2014-03-31 17:55:12 +0000979 virtual bool render(SkBitmap** out = NULL) SK_OVERRIDE {
reed@google.comfe7b1ed2012-11-29 21:00:39 +0000980 SkRect bounds = SkRect::MakeWH(SkIntToScalar(fPicture->width()),
981 SkIntToScalar(fPicture->height()));
982 SkData* data = SkPictureUtils::GatherPixelRefs(fPicture, bounds);
983 SkSafeUnref(data);
skia.committer@gmail.comc7b4be72012-12-11 02:01:20 +0000984
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000985 return (fOutputDir.isEmpty()); // we don't have anything to write
reed@google.comfe7b1ed2012-11-29 21:00:39 +0000986 }
skia.committer@gmail.comc7b4be72012-12-11 02:01:20 +0000987
reed@google.comfe7b1ed2012-11-29 21:00:39 +0000988private:
989 virtual SkString getConfigNameInternal() SK_OVERRIDE {
990 return SkString("gather_pixelrefs");
991 }
992};
993
994PictureRenderer* CreateGatherPixelRefsRenderer() {
995 return SkNEW(GatherRenderer);
996}
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +0000997
reed@google.com5a34fd32012-12-10 16:05:09 +0000998///////////////////////////////////////////////////////////////////////////////
999
1000class PictureCloneRenderer : public PictureRenderer {
1001public:
commit-bot@chromium.org8ddc26b2014-03-31 17:55:12 +00001002 virtual bool render(SkBitmap** out = NULL) SK_OVERRIDE {
reed@google.com5a34fd32012-12-10 16:05:09 +00001003 for (int i = 0; i < 100; ++i) {
1004 SkPicture* clone = fPicture->clone();
1005 SkSafeUnref(clone);
1006 }
skia.committer@gmail.comc7b4be72012-12-11 02:01:20 +00001007
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +00001008 return (fOutputDir.isEmpty()); // we don't have anything to write
reed@google.com5a34fd32012-12-10 16:05:09 +00001009 }
skia.committer@gmail.comc7b4be72012-12-11 02:01:20 +00001010
reed@google.com5a34fd32012-12-10 16:05:09 +00001011private:
1012 virtual SkString getConfigNameInternal() SK_OVERRIDE {
1013 return SkString("picture_clone");
1014 }
1015};
1016
1017PictureRenderer* CreatePictureCloneRenderer() {
1018 return SkNEW(PictureCloneRenderer);
1019}
1020
junov@chromium.org9313ca42012-11-02 18:11:49 +00001021} // namespace sk_tools