blob: e531a30dc065a7fa12fd2b6a74501743bfad52e2 [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/**
326 * Write the canvas to the specified path.
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.orgf5e315c2014-03-19 17:26:07 +0000329 * @param outputDir If nonempty, write the binary image to a file within this directory.
330 * @param inputFilename If we are writing out a binary image, use this to build its filename.
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +0000331 * @param jsonSummaryPtr If not null, add image results to this summary.
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000332 * @param useChecksumBasedFilenames If true, use checksum-based filenames when writing to disk.
commit-bot@chromium.org24c568c2014-04-10 15:39:02 +0000333 * @param tileNumberPtr If not null, which tile number this image contains.
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000334 * @return bool True if the Canvas is written to a file.
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +0000335 *
336 * TODO(epoger): Right now, all canvases must pass through this function in order to be appended
337 * to the ImageResultsSummary. We need some way to add bitmaps to the ImageResultsSummary
338 * even if --writePath has not been specified (and thus this function is not called).
339 *
340 * One fix would be to pass in these path elements separately, and allow this function to be
341 * called even if --writePath was not specified...
342 * const char *outputDir // NULL if we don't want to write image files to disk
343 * const char *filename // name we use within JSON summary, and as the filename within outputDir
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000344 *
345 * UPDATE: Now that outputDir and inputFilename are passed separately, we should be able to do that.
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000346 */
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000347static bool write(SkCanvas* canvas, const SkString& outputDir, const SkString& inputFilename,
348 ImageResultsSummary *jsonSummaryPtr, bool useChecksumBasedFilenames,
commit-bot@chromium.org24c568c2014-04-10 15:39:02 +0000349 const int* tileNumberPtr=NULL) {
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000350 SkASSERT(canvas != NULL);
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000351 if (NULL == canvas) {
keyar@chromium.org9299ede2012-08-21 19:05:08 +0000352 return false;
353 }
354
355 SkBitmap bitmap;
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000356 SkISize size = canvas->getDeviceSize();
357 sk_tools::setup_bitmap(&bitmap, size.width(), size.height());
keyar@chromium.org9299ede2012-08-21 19:05:08 +0000358
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000359 // Make sure we only compute the bitmap hash once (at most).
360 uint64_t hash;
361 bool generatedHash = false;
362
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000363 canvas->readPixels(&bitmap, 0, 0);
keyar@chromium.org9299ede2012-08-21 19:05:08 +0000364 sk_tools::force_all_opaque(bitmap);
365
commit-bot@chromium.org24c568c2014-04-10 15:39:02 +0000366 SkString escapedInputFilename(inputFilename);
367 replace_char(&escapedInputFilename, '.', '_');
368
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000369 // TODO(epoger): what about including the config type within outputFilename? That way,
370 // we could combine results of different config types without conflicting filenames.
commit-bot@chromium.org24c568c2014-04-10 15:39:02 +0000371 SkString outputFilename;
372 const char *outputSubdirPtr = NULL;
373 if (useChecksumBasedFilenames) {
commit-bot@chromium.orgb470c212014-03-19 22:58:52 +0000374 SkASSERT(!generatedHash);
375 SkAssertResult(SkBitmapHasher::ComputeDigest(bitmap, &hash));
376 generatedHash = true;
377
commit-bot@chromium.org24c568c2014-04-10 15:39:02 +0000378 outputSubdirPtr = escapedInputFilename.c_str();
379 outputFilename.set(kJsonValue_Image_ChecksumAlgorithm_Bitmap64bitMD5);
380 outputFilename.append("_");
381 outputFilename.appendU64(hash);
382 } else {
383 outputFilename.set(escapedInputFilename);
384 if (NULL != tileNumberPtr) {
385 outputFilename.append("-tile");
386 outputFilename.appendS32(*tileNumberPtr);
387 }
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +0000388 }
commit-bot@chromium.org24c568c2014-04-10 15:39:02 +0000389 outputFilename.append(".png");
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +0000390
commit-bot@chromium.org24c568c2014-04-10 15:39:02 +0000391 if (NULL != jsonSummaryPtr) {
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000392 if (!generatedHash) {
393 SkAssertResult(SkBitmapHasher::ComputeDigest(bitmap, &hash));
394 generatedHash = true;
395 }
commit-bot@chromium.org24c568c2014-04-10 15:39:02 +0000396
397 SkString outputRelativePath;
398 if (outputSubdirPtr) {
399 outputRelativePath.set(outputSubdirPtr);
400 outputRelativePath.append("/"); // always use "/", even on Windows
401 outputRelativePath.append(outputFilename);
402 } else {
403 outputRelativePath.set(outputFilename);
404 }
405
406 jsonSummaryPtr->add(inputFilename.c_str(), outputRelativePath.c_str(),
407 hash, tileNumberPtr);
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000408 }
409
410 SkASSERT(!outputDir.isEmpty()); // TODO(epoger): we want to remove this constraint,
411 // as noted above
commit-bot@chromium.org24c568c2014-04-10 15:39:02 +0000412 SkString dirPath;
413 if (outputSubdirPtr) {
414 dirPath = SkOSPath::SkPathJoin(outputDir.c_str(), outputSubdirPtr);
415 sk_mkdir(dirPath.c_str());
416 } else {
417 dirPath.set(outputDir);
418 }
419 SkString fullPath = SkOSPath::SkPathJoin(dirPath.c_str(), outputFilename.c_str());
420 return SkImageEncoder::EncodeFile(fullPath.c_str(), bitmap, SkImageEncoder::kPNG_Type, 100);
keyar@chromium.org9299ede2012-08-21 19:05:08 +0000421}
422
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000423///////////////////////////////////////////////////////////////////////////////////////////////
424
djsollen@google.comfd9720c2012-11-06 16:54:40 +0000425SkCanvas* RecordPictureRenderer::setupCanvas(int width, int height) {
426 // defer the canvas setup until the render step
427 return NULL;
428}
429
reed@google.com672588b2014-01-08 15:42:01 +0000430// the size_t* parameter is deprecated, so we ignore it
431static SkData* encode_bitmap_to_data(size_t*, const SkBitmap& bm) {
scroggo@google.com1b1bcc32013-05-21 20:31:23 +0000432 return SkImageEncoder::EncodeData(bm, SkImageEncoder::kPNG_Type, 100);
scroggo@google.coma9e3a362012-11-07 17:52:48 +0000433}
434
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000435bool RecordPictureRenderer::render(SkBitmap** out) {
commit-bot@chromium.org5fb2ce32014-04-17 23:35:06 +0000436 SkAutoTDelete<SkBBHFactory> factory(this->getFactory());
437 SkPictureRecorder recorder;
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000438 SkCanvas* canvas = recorder.beginRecording(this->getViewWidth(), this->getViewHeight(),
commit-bot@chromium.org5fb2ce32014-04-17 23:35:06 +0000439 factory.get(),
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000440 this->recordFlags());
441 this->scaleToScaleFactor(canvas);
442 fPicture->draw(canvas);
443 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000444 if (!fOutputDir.isEmpty()) {
scroggo@google.coma9e3a362012-11-07 17:52:48 +0000445 // Record the new picture as a new SKP with PNG encoded bitmaps.
commit-bot@chromium.org24c568c2014-04-10 15:39:02 +0000446 SkString skpPath = SkOSPath::SkPathJoin(fOutputDir.c_str(), fInputFilename.c_str());
scroggo@google.coma9e3a362012-11-07 17:52:48 +0000447 SkFILEWStream stream(skpPath.c_str());
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000448 picture->serialize(&stream, &encode_bitmap_to_data);
scroggo@google.coma9e3a362012-11-07 17:52:48 +0000449 return true;
450 }
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000451 return false;
scroggo@google.com9a412522012-09-07 15:21:18 +0000452}
453
scroggo@google.com0a049b82012-11-02 22:01:26 +0000454SkString RecordPictureRenderer::getConfigNameInternal() {
455 return SkString("record");
456}
457
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000458///////////////////////////////////////////////////////////////////////////////////////////////
459
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000460bool PipePictureRenderer::render(SkBitmap** out) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000461 SkASSERT(fCanvas.get() != NULL);
462 SkASSERT(fPicture != NULL);
keyar@chromium.org78a35c52012-08-20 15:03:44 +0000463 if (NULL == fCanvas.get() || NULL == fPicture) {
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000464 return false;
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000465 }
466
467 PipeController pipeController(fCanvas.get());
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000468 SkGPipeWriter writer;
469 SkCanvas* pipeCanvas = writer.startRecording(&pipeController);
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000470 pipeCanvas->drawPicture(*fPicture);
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000471 writer.endRecording();
scroggo@google.com9a412522012-09-07 15:21:18 +0000472 fCanvas->flush();
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000473 if (!fOutputDir.isEmpty()) {
474 return write(fCanvas, fOutputDir, fInputFilename, fJsonSummaryPtr,
475 fUseChecksumBasedFilenames);
borenet@google.com070d3542012-10-26 13:26:55 +0000476 }
edisonn@google.com84f548c2012-12-18 22:24:03 +0000477 if (NULL != out) {
478 *out = SkNEW(SkBitmap);
479 setup_bitmap(*out, fPicture->width(), fPicture->height());
480 fCanvas->readPixels(*out, 0, 0);
skia.committer@gmail.coma7d8e3e2012-12-19 02:01:38 +0000481 }
borenet@google.com070d3542012-10-26 13:26:55 +0000482 return true;
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000483}
484
scroggo@google.com0a049b82012-11-02 22:01:26 +0000485SkString PipePictureRenderer::getConfigNameInternal() {
486 return SkString("pipe");
487}
488
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000489///////////////////////////////////////////////////////////////////////////////////////////////
490
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000491void SimplePictureRenderer::init(SkPicture* picture, const SkString* outputDir,
492 const SkString* inputFilename, bool useChecksumBasedFilenames) {
493 INHERITED::init(picture, outputDir, inputFilename, useChecksumBasedFilenames);
junov@chromium.org9313ca42012-11-02 18:11:49 +0000494 this->buildBBoxHierarchy();
495}
496
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000497bool SimplePictureRenderer::render(SkBitmap** out) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000498 SkASSERT(fCanvas.get() != NULL);
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000499 SkASSERT(NULL != fPicture);
keyar@chromium.org78a35c52012-08-20 15:03:44 +0000500 if (NULL == fCanvas.get() || NULL == fPicture) {
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000501 return false;
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000502 }
503
504 fCanvas->drawPicture(*fPicture);
scroggo@google.com9a412522012-09-07 15:21:18 +0000505 fCanvas->flush();
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000506 if (!fOutputDir.isEmpty()) {
507 return write(fCanvas, fOutputDir, fInputFilename, fJsonSummaryPtr,
508 fUseChecksumBasedFilenames);
borenet@google.com070d3542012-10-26 13:26:55 +0000509 }
skia.committer@gmail.coma7d8e3e2012-12-19 02:01:38 +0000510
edisonn@google.com84f548c2012-12-18 22:24:03 +0000511 if (NULL != out) {
512 *out = SkNEW(SkBitmap);
513 setup_bitmap(*out, fPicture->width(), fPicture->height());
514 fCanvas->readPixels(*out, 0, 0);
515 }
516
borenet@google.com070d3542012-10-26 13:26:55 +0000517 return true;
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000518}
519
scroggo@google.com0a049b82012-11-02 22:01:26 +0000520SkString SimplePictureRenderer::getConfigNameInternal() {
521 return SkString("simple");
522}
523
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000524///////////////////////////////////////////////////////////////////////////////////////////////
525
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000526TiledPictureRenderer::TiledPictureRenderer()
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000527 : fTileWidth(kDefaultTileWidth)
rileya@google.comb947b912012-08-29 17:35:07 +0000528 , fTileHeight(kDefaultTileHeight)
rileya@google.coma04dc022012-09-10 19:01:38 +0000529 , fTileWidthPercentage(0.0)
rileya@google.comb947b912012-08-29 17:35:07 +0000530 , fTileHeightPercentage(0.0)
scroggo@google.comcbcef702012-12-13 22:09:28 +0000531 , fTileMinPowerOf2Width(0)
532 , fCurrentTileOffset(-1)
533 , fTilesX(0)
534 , fTilesY(0) { }
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000535
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000536void TiledPictureRenderer::init(SkPicture* pict, const SkString* outputDir,
537 const SkString* inputFilename, bool useChecksumBasedFilenames) {
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000538 SkASSERT(NULL != pict);
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000539 SkASSERT(0 == fTileRects.count());
540 if (NULL == pict || fTileRects.count() != 0) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000541 return;
542 }
543
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000544 // Do not call INHERITED::init(), which would create a (potentially large) canvas which is not
545 // used by bench_pictures.
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000546 fPicture.reset(pict)->ref();
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000547 this->CopyString(&fOutputDir, outputDir);
548 this->CopyString(&fInputFilename, inputFilename);
549 fUseChecksumBasedFilenames = useChecksumBasedFilenames;
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000550 this->buildBBoxHierarchy();
keyar@chromium.orgcc6e5ef2012-07-27 20:09:26 +0000551
552 if (fTileWidthPercentage > 0) {
robertphillips@google.com5d8d1862012-08-15 14:36:41 +0000553 fTileWidth = sk_float_ceil2int(float(fTileWidthPercentage * fPicture->width() / 100));
keyar@chromium.orgcc6e5ef2012-07-27 20:09:26 +0000554 }
555 if (fTileHeightPercentage > 0) {
robertphillips@google.com5d8d1862012-08-15 14:36:41 +0000556 fTileHeight = sk_float_ceil2int(float(fTileHeightPercentage * fPicture->height() / 100));
keyar@chromium.orgcc6e5ef2012-07-27 20:09:26 +0000557 }
558
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000559 if (fTileMinPowerOf2Width > 0) {
560 this->setupPowerOf2Tiles();
561 } else {
562 this->setupTiles();
563 }
scroggo@google.comcbcef702012-12-13 22:09:28 +0000564 fCanvas.reset(this->setupCanvas(fTileWidth, fTileHeight));
565 // Initialize to -1 so that the first call to nextTile will set this up to draw tile 0 on the
566 // first call to drawCurrentTile.
567 fCurrentTileOffset = -1;
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000568}
569
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000570void TiledPictureRenderer::end() {
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000571 fTileRects.reset();
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000572 this->INHERITED::end();
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000573}
574
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000575void TiledPictureRenderer::setupTiles() {
scroggo@google.comc0d5e542012-12-13 21:40:48 +0000576 // Only use enough tiles to cover the viewport
577 const int width = this->getViewWidth();
578 const int height = this->getViewHeight();
579
scroggo@google.comcbcef702012-12-13 22:09:28 +0000580 fTilesX = fTilesY = 0;
scroggo@google.comc0d5e542012-12-13 21:40:48 +0000581 for (int tile_y_start = 0; tile_y_start < height; tile_y_start += fTileHeight) {
scroggo@google.comcbcef702012-12-13 22:09:28 +0000582 fTilesY++;
scroggo@google.comc0d5e542012-12-13 21:40:48 +0000583 for (int tile_x_start = 0; tile_x_start < width; tile_x_start += fTileWidth) {
scroggo@google.comcbcef702012-12-13 22:09:28 +0000584 if (0 == tile_y_start) {
585 // Only count tiles in the X direction on the first pass.
586 fTilesX++;
587 }
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000588 *fTileRects.append() = SkRect::MakeXYWH(SkIntToScalar(tile_x_start),
589 SkIntToScalar(tile_y_start),
590 SkIntToScalar(fTileWidth),
591 SkIntToScalar(fTileHeight));
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000592 }
593 }
594}
595
scroggo@google.comcbcef702012-12-13 22:09:28 +0000596bool TiledPictureRenderer::tileDimensions(int &x, int &y) {
597 if (fTileRects.count() == 0 || NULL == fPicture) {
598 return false;
599 }
600 x = fTilesX;
601 y = fTilesY;
602 return true;
603}
604
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000605// The goal of the powers of two tiles is to minimize the amount of wasted tile
606// space in the width-wise direction and then minimize the number of tiles. The
607// constraints are that every tile must have a pixel width that is a power of
608// two and also be of some minimal width (that is also a power of two).
609//
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000610// This is solved by first taking our picture size and rounding it up to the
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000611// multiple of the minimal width. The binary representation of this rounded
612// value gives us the tiles we need: a bit of value one means we need a tile of
613// that size.
614void TiledPictureRenderer::setupPowerOf2Tiles() {
scroggo@google.comc0d5e542012-12-13 21:40:48 +0000615 // Only use enough tiles to cover the viewport
616 const int width = this->getViewWidth();
617 const int height = this->getViewHeight();
618
619 int rounded_value = width;
620 if (width % fTileMinPowerOf2Width != 0) {
621 rounded_value = width - (width % fTileMinPowerOf2Width) + fTileMinPowerOf2Width;
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000622 }
623
reed@google.come15b2f52013-12-18 04:59:26 +0000624 int num_bits = SkScalarCeilToInt(scalar_log2(SkIntToScalar(width)));
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000625 int largest_possible_tile_size = 1 << num_bits;
626
scroggo@google.comcbcef702012-12-13 22:09:28 +0000627 fTilesX = fTilesY = 0;
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000628 // The tile height is constant for a particular picture.
scroggo@google.comc0d5e542012-12-13 21:40:48 +0000629 for (int tile_y_start = 0; tile_y_start < height; tile_y_start += fTileHeight) {
scroggo@google.comcbcef702012-12-13 22:09:28 +0000630 fTilesY++;
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000631 int tile_x_start = 0;
632 int current_width = largest_possible_tile_size;
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000633 // Set fTileWidth to be the width of the widest tile, so that each canvas is large enough
634 // to draw each tile.
635 fTileWidth = current_width;
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000636
637 while (current_width >= fTileMinPowerOf2Width) {
638 // It is very important this is a bitwise AND.
639 if (current_width & rounded_value) {
scroggo@google.comcbcef702012-12-13 22:09:28 +0000640 if (0 == tile_y_start) {
641 // Only count tiles in the X direction on the first pass.
642 fTilesX++;
643 }
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000644 *fTileRects.append() = SkRect::MakeXYWH(SkIntToScalar(tile_x_start),
645 SkIntToScalar(tile_y_start),
646 SkIntToScalar(current_width),
647 SkIntToScalar(fTileHeight));
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000648 tile_x_start += current_width;
649 }
650
651 current_width >>= 1;
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000652 }
653 }
654}
655
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000656/**
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +0000657 * Draw the specified picture to the canvas translated to rectangle provided, so that this mini
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000658 * canvas represents the rectangle's portion of the overall picture.
659 * Saves and restores so that the initial clip and matrix return to their state before this function
660 * is called.
661 */
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +0000662static void draw_tile_to_canvas(SkCanvas* canvas, const SkRect& tileRect, SkPicture* picture) {
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000663 int saveCount = canvas->save();
scroggo@google.com82ec0b02012-12-17 19:25:54 +0000664 // Translate so that we draw the correct portion of the picture.
665 // Perform a postTranslate so that the scaleFactor does not interfere with the positioning.
666 SkMatrix mat(canvas->getTotalMatrix());
667 mat.postTranslate(-tileRect.fLeft, -tileRect.fTop);
668 canvas->setMatrix(mat);
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +0000669 canvas->drawPicture(*picture);
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000670 canvas->restoreToCount(saveCount);
671 canvas->flush();
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000672}
673
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000674///////////////////////////////////////////////////////////////////////////////////////////////
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000675
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +0000676/**
677 * Copies the entirety of the src bitmap (typically a tile) into a portion of the dst bitmap.
678 * If the src bitmap is too large to fit within the dst bitmap after the x and y
679 * offsets have been applied, any excess will be ignored (so only the top-left portion of the
680 * src bitmap will be copied).
681 *
682 * @param src source bitmap
683 * @param dst destination bitmap
684 * @param xOffset x-offset within destination bitmap
685 * @param yOffset y-offset within destination bitmap
686 */
687static void bitmapCopyAtOffset(const SkBitmap& src, SkBitmap* dst,
688 int xOffset, int yOffset) {
689 for (int y = 0; y <src.height() && y + yOffset < dst->height() ; y++) {
690 for (int x = 0; x < src.width() && x + xOffset < dst->width() ; x++) {
691 *dst->getAddr32(xOffset + x, yOffset + y) = *src.getAddr32(x, y);
edisonn@google.com84f548c2012-12-18 22:24:03 +0000692 }
693 }
694}
695
scroggo@google.comcbcef702012-12-13 22:09:28 +0000696bool TiledPictureRenderer::nextTile(int &i, int &j) {
697 if (++fCurrentTileOffset < fTileRects.count()) {
698 i = fCurrentTileOffset % fTilesX;
699 j = fCurrentTileOffset / fTilesX;
700 return true;
701 }
702 return false;
703}
704
705void TiledPictureRenderer::drawCurrentTile() {
706 SkASSERT(fCurrentTileOffset >= 0 && fCurrentTileOffset < fTileRects.count());
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +0000707 draw_tile_to_canvas(fCanvas, fTileRects[fCurrentTileOffset], fPicture);
scroggo@google.comcbcef702012-12-13 22:09:28 +0000708}
709
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000710bool TiledPictureRenderer::render(SkBitmap** out) {
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000711 SkASSERT(fPicture != NULL);
712 if (NULL == fPicture) {
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000713 return false;
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000714 }
715
edisonn@google.com84f548c2012-12-18 22:24:03 +0000716 SkBitmap bitmap;
717 if (out){
718 *out = SkNEW(SkBitmap);
719 setup_bitmap(*out, fPicture->width(), fPicture->height());
720 setup_bitmap(&bitmap, fTileWidth, fTileHeight);
721 }
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000722 bool success = true;
723 for (int i = 0; i < fTileRects.count(); ++i) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +0000724 draw_tile_to_canvas(fCanvas, fTileRects[i], fPicture);
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000725 if (!fOutputDir.isEmpty()) {
726 success &= write(fCanvas, fOutputDir, fInputFilename, fJsonSummaryPtr,
727 fUseChecksumBasedFilenames, &i);
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000728 }
edisonn@google.com84f548c2012-12-18 22:24:03 +0000729 if (NULL != out) {
730 if (fCanvas->readPixels(&bitmap, 0, 0)) {
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +0000731 // Add this tile to the entire bitmap.
732 bitmapCopyAtOffset(bitmap, *out, SkScalarFloorToInt(fTileRects[i].left()),
733 SkScalarFloorToInt(fTileRects[i].top()));
edisonn@google.com84f548c2012-12-18 22:24:03 +0000734 } else {
735 success = false;
736 }
737 }
keyar@chromium.org163b5672012-08-01 17:53:29 +0000738 }
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000739 return success;
keyar@chromium.org163b5672012-08-01 17:53:29 +0000740}
741
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000742SkCanvas* TiledPictureRenderer::setupCanvas(int width, int height) {
743 SkCanvas* canvas = this->INHERITED::setupCanvas(width, height);
commit-bot@chromium.org8ddc26b2014-03-31 17:55:12 +0000744 SkASSERT(NULL != fPicture);
scroggo@google.com82ec0b02012-12-17 19:25:54 +0000745 // Clip the tile to an area that is completely inside both the SkPicture and the viewport. This
746 // is mostly important for tiles on the right and bottom edges as they may go over this area and
747 // the picture may have some commands that draw outside of this area and so should not actually
748 // be written.
749 // Uses a clipRegion so that it will be unaffected by the scale factor, which may have been set
750 // by INHERITED::setupCanvas.
751 SkRegion clipRegion;
752 clipRegion.setRect(0, 0, this->getViewWidth(), this->getViewHeight());
753 canvas->clipRegion(clipRegion);
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000754 return canvas;
755}
scroggo@google.com0a049b82012-11-02 22:01:26 +0000756
757SkString TiledPictureRenderer::getConfigNameInternal() {
758 SkString name;
759 if (fTileMinPowerOf2Width > 0) {
760 name.append("pow2tile_");
761 name.appendf("%i", fTileMinPowerOf2Width);
762 } else {
763 name.append("tile_");
764 if (fTileWidthPercentage > 0) {
765 name.appendf("%.f%%", fTileWidthPercentage);
766 } else {
767 name.appendf("%i", fTileWidth);
768 }
769 }
770 name.append("x");
771 if (fTileHeightPercentage > 0) {
772 name.appendf("%.f%%", fTileHeightPercentage);
773 } else {
774 name.appendf("%i", fTileHeight);
775 }
776 return name;
777}
778
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000779///////////////////////////////////////////////////////////////////////////////////////////////
780
781// Holds all of the information needed to draw a set of tiles.
782class CloneData : public SkRunnable {
783
784public:
785 CloneData(SkPicture* clone, SkCanvas* canvas, SkTDArray<SkRect>& rects, int start, int end,
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000786 SkRunnable* done, ImageResultsSummary* jsonSummaryPtr, bool useChecksumBasedFilenames)
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000787 : fClone(clone)
788 , fCanvas(canvas)
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000789 , fRects(rects)
790 , fStart(start)
791 , fEnd(end)
792 , fSuccess(NULL)
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +0000793 , fDone(done)
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000794 , fJsonSummaryPtr(jsonSummaryPtr)
795 , fUseChecksumBasedFilenames(useChecksumBasedFilenames) {
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000796 SkASSERT(fDone != NULL);
797 }
798
799 virtual void run() SK_OVERRIDE {
800 SkGraphics::SetTLSFontCacheLimit(1024 * 1024);
edisonn@google.com84f548c2012-12-18 22:24:03 +0000801
802 SkBitmap bitmap;
803 if (fBitmap != NULL) {
804 // All tiles are the same size.
jvanverth@google.com9c4e5ac2013-01-07 18:41:28 +0000805 setup_bitmap(&bitmap, SkScalarFloorToInt(fRects[0].width()), SkScalarFloorToInt(fRects[0].height()));
edisonn@google.com84f548c2012-12-18 22:24:03 +0000806 }
skia.committer@gmail.coma7d8e3e2012-12-19 02:01:38 +0000807
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000808 for (int i = fStart; i < fEnd; i++) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +0000809 draw_tile_to_canvas(fCanvas, fRects[i], fClone);
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000810 if ((!fOutputDir.isEmpty())
811 && !write(fCanvas, fOutputDir, fInputFilename, fJsonSummaryPtr,
812 fUseChecksumBasedFilenames, &i)
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000813 && fSuccess != NULL) {
814 *fSuccess = false;
815 // If one tile fails to write to a file, do not continue drawing the rest.
816 break;
817 }
edisonn@google.com84f548c2012-12-18 22:24:03 +0000818 if (fBitmap != NULL) {
819 if (fCanvas->readPixels(&bitmap, 0, 0)) {
820 SkAutoLockPixels alp(*fBitmap);
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +0000821 bitmapCopyAtOffset(bitmap, fBitmap, SkScalarFloorToInt(fRects[i].left()),
822 SkScalarFloorToInt(fRects[i].top()));
edisonn@google.com84f548c2012-12-18 22:24:03 +0000823 } else {
824 *fSuccess = false;
825 // If one tile fails to read pixels, do not continue drawing the rest.
826 break;
827 }
828 }
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000829 }
830 fDone->run();
831 }
832
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000833 void setPathsAndSuccess(const SkString& outputDir, const SkString& inputFilename,
834 bool* success) {
835 fOutputDir.set(outputDir);
836 fInputFilename.set(inputFilename);
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000837 fSuccess = success;
838 }
839
edisonn@google.com84f548c2012-12-18 22:24:03 +0000840 void setBitmap(SkBitmap* bitmap) {
841 fBitmap = bitmap;
842 }
843
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000844private:
845 // All pointers unowned.
846 SkPicture* fClone; // Picture to draw from. Each CloneData has a unique one which
847 // is threadsafe.
848 SkCanvas* fCanvas; // Canvas to draw to. Reused for each tile.
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000849 SkString fOutputDir; // If not empty, write results into this directory.
850 SkString fInputFilename; // Filename of input SkPicture file.
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000851 SkTDArray<SkRect>& fRects; // All tiles of the picture.
852 const int fStart; // Range of tiles drawn by this thread.
853 const int fEnd;
854 bool* fSuccess; // Only meaningful if path is non-null. Shared by all threads,
855 // and only set to false upon failure to write to a PNG.
856 SkRunnable* fDone;
edisonn@google.com84f548c2012-12-18 22:24:03 +0000857 SkBitmap* fBitmap;
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +0000858 ImageResultsSummary* fJsonSummaryPtr;
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000859 bool fUseChecksumBasedFilenames;
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000860};
861
862MultiCorePictureRenderer::MultiCorePictureRenderer(int threadCount)
863: fNumThreads(threadCount)
864, fThreadPool(threadCount)
865, fCountdown(threadCount) {
866 // Only need to create fNumThreads - 1 clones, since one thread will use the base
867 // picture.
868 fPictureClones = SkNEW_ARRAY(SkPicture, fNumThreads - 1);
869 fCloneData = SkNEW_ARRAY(CloneData*, fNumThreads);
870}
871
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000872void MultiCorePictureRenderer::init(SkPicture *pict, const SkString* outputDir,
873 const SkString* inputFilename, bool useChecksumBasedFilenames) {
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000874 // Set fPicture and the tiles.
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000875 this->INHERITED::init(pict, outputDir, inputFilename, useChecksumBasedFilenames);
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000876 for (int i = 0; i < fNumThreads; ++i) {
877 *fCanvasPool.append() = this->setupCanvas(this->getTileWidth(), this->getTileHeight());
878 }
879 // Only need to create fNumThreads - 1 clones, since one thread will use the base picture.
880 fPicture->clone(fPictureClones, fNumThreads - 1);
881 // Populate each thread with the appropriate data.
882 // Group the tiles into nearly equal size chunks, rounding up so we're sure to cover them all.
883 const int chunkSize = (fTileRects.count() + fNumThreads - 1) / fNumThreads;
884
885 for (int i = 0; i < fNumThreads; i++) {
886 SkPicture* pic;
887 if (i == fNumThreads-1) {
888 // The last set will use the original SkPicture.
889 pic = fPicture;
890 } else {
891 pic = &fPictureClones[i];
892 }
893 const int start = i * chunkSize;
894 const int end = SkMin32(start + chunkSize, fTileRects.count());
895 fCloneData[i] = SkNEW_ARGS(CloneData,
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +0000896 (pic, fCanvasPool[i], fTileRects, start, end, &fCountdown,
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000897 fJsonSummaryPtr, useChecksumBasedFilenames));
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000898 }
899}
900
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000901bool MultiCorePictureRenderer::render(SkBitmap** out) {
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000902 bool success = true;
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000903 if (!fOutputDir.isEmpty()) {
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000904 for (int i = 0; i < fNumThreads-1; i++) {
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000905 fCloneData[i]->setPathsAndSuccess(fOutputDir, fInputFilename, &success);
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000906 }
907 }
908
edisonn@google.com84f548c2012-12-18 22:24:03 +0000909 if (NULL != out) {
910 *out = SkNEW(SkBitmap);
911 setup_bitmap(*out, fPicture->width(), fPicture->height());
912 for (int i = 0; i < fNumThreads; i++) {
913 fCloneData[i]->setBitmap(*out);
914 }
915 } else {
916 for (int i = 0; i < fNumThreads; i++) {
917 fCloneData[i]->setBitmap(NULL);
918 }
919 }
920
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000921 fCountdown.reset(fNumThreads);
922 for (int i = 0; i < fNumThreads; i++) {
923 fThreadPool.add(fCloneData[i]);
924 }
925 fCountdown.wait();
926
927 return success;
928}
929
930void MultiCorePictureRenderer::end() {
931 for (int i = 0; i < fNumThreads - 1; i++) {
932 SkDELETE(fCloneData[i]);
933 fCloneData[i] = NULL;
934 }
935
936 fCanvasPool.unrefAll();
937
938 this->INHERITED::end();
939}
940
941MultiCorePictureRenderer::~MultiCorePictureRenderer() {
942 // Each individual CloneData was deleted in end.
943 SkDELETE_ARRAY(fCloneData);
944 SkDELETE_ARRAY(fPictureClones);
945}
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000946
scroggo@google.com0a049b82012-11-02 22:01:26 +0000947SkString MultiCorePictureRenderer::getConfigNameInternal() {
948 SkString name = this->INHERITED::getConfigNameInternal();
949 name.appendf("_multi_%i_threads", fNumThreads);
950 return name;
951}
952
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000953///////////////////////////////////////////////////////////////////////////////////////////////
scroggo@google.com9a412522012-09-07 15:21:18 +0000954
955void PlaybackCreationRenderer::setup() {
commit-bot@chromium.org5fb2ce32014-04-17 23:35:06 +0000956 SkAutoTDelete<SkBBHFactory> factory(this->getFactory());
957 fRecorder.reset(SkNEW(SkPictureRecorder));
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000958 SkCanvas* canvas = fRecorder->beginRecording(this->getViewWidth(), this->getViewHeight(),
commit-bot@chromium.org5fb2ce32014-04-17 23:35:06 +0000959 factory.get(),
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000960 this->recordFlags());
961 this->scaleToScaleFactor(canvas);
962 canvas->drawPicture(*fPicture);
scroggo@google.com9a412522012-09-07 15:21:18 +0000963}
964
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000965bool PlaybackCreationRenderer::render(SkBitmap** out) {
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000966 fPicture.reset(fRecorder->endRecording());
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000967 // Since this class does not actually render, return false.
968 return false;
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000969}
970
scroggo@google.com0a049b82012-11-02 22:01:26 +0000971SkString PlaybackCreationRenderer::getConfigNameInternal() {
972 return SkString("playback_creation");
973}
974
junov@chromium.org9313ca42012-11-02 18:11:49 +0000975///////////////////////////////////////////////////////////////////////////////////////////////
976// SkPicture variants for each BBoxHierarchy type
977
commit-bot@chromium.org5fb2ce32014-04-17 23:35:06 +0000978SkBBHFactory* PictureRenderer::getFactory() {
junov@chromium.org9313ca42012-11-02 18:11:49 +0000979 switch (fBBoxHierarchyType) {
980 case kNone_BBoxHierarchyType:
robertphillips@google.com84b18c72014-04-13 19:09:42 +0000981 return NULL;
commit-bot@chromium.orgc22d1392014-02-03 18:08:33 +0000982 case kQuadTree_BBoxHierarchyType:
commit-bot@chromium.org5fb2ce32014-04-17 23:35:06 +0000983 return SkNEW(SkQuadTreeFactory);
junov@chromium.org9313ca42012-11-02 18:11:49 +0000984 case kRTree_BBoxHierarchyType:
commit-bot@chromium.org5fb2ce32014-04-17 23:35:06 +0000985 return SkNEW(SkRTreeFactory);
junov@chromium.org7b537062012-11-06 18:58:43 +0000986 case kTileGrid_BBoxHierarchyType:
commit-bot@chromium.org5fb2ce32014-04-17 23:35:06 +0000987 return SkNEW_ARGS(SkTileGridFactory, (fGridInfo));
junov@chromium.org9313ca42012-11-02 18:11:49 +0000988 }
989 SkASSERT(0); // invalid bbhType
990 return NULL;
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000991}
junov@chromium.org9313ca42012-11-02 18:11:49 +0000992
reed@google.comfe7b1ed2012-11-29 21:00:39 +0000993///////////////////////////////////////////////////////////////////////////////
994
995class GatherRenderer : public PictureRenderer {
996public:
commit-bot@chromium.org8ddc26b2014-03-31 17:55:12 +0000997 virtual bool render(SkBitmap** out = NULL) SK_OVERRIDE {
reed@google.comfe7b1ed2012-11-29 21:00:39 +0000998 SkRect bounds = SkRect::MakeWH(SkIntToScalar(fPicture->width()),
999 SkIntToScalar(fPicture->height()));
1000 SkData* data = SkPictureUtils::GatherPixelRefs(fPicture, bounds);
1001 SkSafeUnref(data);
skia.committer@gmail.comc7b4be72012-12-11 02:01:20 +00001002
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +00001003 return (fOutputDir.isEmpty()); // we don't have anything to write
reed@google.comfe7b1ed2012-11-29 21:00:39 +00001004 }
skia.committer@gmail.comc7b4be72012-12-11 02:01:20 +00001005
reed@google.comfe7b1ed2012-11-29 21:00:39 +00001006private:
1007 virtual SkString getConfigNameInternal() SK_OVERRIDE {
1008 return SkString("gather_pixelrefs");
1009 }
1010};
1011
1012PictureRenderer* CreateGatherPixelRefsRenderer() {
1013 return SkNEW(GatherRenderer);
1014}
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +00001015
reed@google.com5a34fd32012-12-10 16:05:09 +00001016///////////////////////////////////////////////////////////////////////////////
1017
1018class PictureCloneRenderer : public PictureRenderer {
1019public:
commit-bot@chromium.org8ddc26b2014-03-31 17:55:12 +00001020 virtual bool render(SkBitmap** out = NULL) SK_OVERRIDE {
reed@google.com5a34fd32012-12-10 16:05:09 +00001021 for (int i = 0; i < 100; ++i) {
1022 SkPicture* clone = fPicture->clone();
1023 SkSafeUnref(clone);
1024 }
skia.committer@gmail.comc7b4be72012-12-11 02:01:20 +00001025
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +00001026 return (fOutputDir.isEmpty()); // we don't have anything to write
reed@google.com5a34fd32012-12-10 16:05:09 +00001027 }
skia.committer@gmail.comc7b4be72012-12-11 02:01:20 +00001028
reed@google.com5a34fd32012-12-10 16:05:09 +00001029private:
1030 virtual SkString getConfigNameInternal() SK_OVERRIDE {
1031 return SkString("picture_clone");
1032 }
1033};
1034
1035PictureRenderer* CreatePictureCloneRenderer() {
1036 return SkNEW(PictureCloneRenderer);
1037}
1038
junov@chromium.org9313ca42012-11-02 18:11:49 +00001039} // namespace sk_tools