blob: 2afd3745d18258a09ac36355d80b2634a27da93f [file] [log] [blame]
keyar@chromium.orgb3fb7c12012-08-20 21:02:49 +00001/*
2 * Copyright 2012 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
keyar@chromium.org451bb9f2012-07-26 17:27:57 +00008#include "PictureRenderer.h"
scroggo@google.com58b4ead2012-08-31 16:15:22 +00009#include "picture_utils.h"
keyar@chromium.org451bb9f2012-07-26 17:27:57 +000010#include "SamplePipeControllers.h"
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +000011#include "SkBitmapHasher.h"
keyar@chromium.org451bb9f2012-07-26 17:27:57 +000012#include "SkCanvas.h"
scroggo@google.com1b1bcc32013-05-21 20:31:23 +000013#include "SkData.h"
keyar@chromium.org451bb9f2012-07-26 17:27:57 +000014#include "SkDevice.h"
robertphillips@google.com94d8f1e2013-12-18 17:25:33 +000015#include "SkDiscardableMemoryPool.h"
keyar@chromium.org451bb9f2012-07-26 17:27:57 +000016#include "SkGPipe.h"
scroggo@google.com58b4ead2012-08-31 16:15:22 +000017#if SK_SUPPORT_GPU
robertphillips@google.comfe1b5362013-02-07 19:45:46 +000018#include "gl/GrGLDefines.h"
scroggo@google.com58b4ead2012-08-31 16:15:22 +000019#include "SkGpuDevice.h"
20#endif
21#include "SkGraphics.h"
22#include "SkImageEncoder.h"
caryclark@google.coma3622372012-11-06 21:26:13 +000023#include "SkMaskFilter.h"
keyar@chromium.orgea826952012-08-23 15:24:13 +000024#include "SkMatrix.h"
keyar@chromium.org451bb9f2012-07-26 17:27:57 +000025#include "SkPicture.h"
scroggo@google.com1b1bcc32013-05-21 20:31:23 +000026#include "SkPictureUtils.h"
27#include "SkPixelRef.h"
commit-bot@chromium.orgc22d1392014-02-03 18:08:33 +000028#include "SkQuadTree.h"
29#include "SkQuadTreePicture.h"
junov@chromium.org9313ca42012-11-02 18:11:49 +000030#include "SkRTree.h"
keyar@chromium.orgea826952012-08-23 15:24:13 +000031#include "SkScalar.h"
scroggo@google.coma9e3a362012-11-07 17:52:48 +000032#include "SkStream.h"
keyar@chromium.org9299ede2012-08-21 19:05:08 +000033#include "SkString.h"
scroggo@google.com58b4ead2012-08-31 16:15:22 +000034#include "SkTemplates.h"
junov@chromium.org3cb834b2012-12-13 16:39:53 +000035#include "SkTileGridPicture.h"
keyar@chromium.org451bb9f2012-07-26 17:27:57 +000036#include "SkTDArray.h"
scroggo@google.com58b4ead2012-08-31 16:15:22 +000037#include "SkThreadUtils.h"
keyar@chromium.org451bb9f2012-07-26 17:27:57 +000038#include "SkTypes.h"
keyar@chromium.org4ea96c52012-08-20 15:03:29 +000039
reed@google.come15b2f52013-12-18 04:59:26 +000040static inline SkScalar scalar_log2(SkScalar x) {
41 static const SkScalar log2_conversion_factor = SkScalarDiv(1, SkScalarLog(2));
skia.committer@gmail.com3b85deb2013-12-18 07:01:56 +000042
reed@google.come15b2f52013-12-18 04:59:26 +000043 return SkScalarLog(x) * log2_conversion_factor;
44}
45
keyar@chromium.org451bb9f2012-07-26 17:27:57 +000046namespace sk_tools {
47
48enum {
49 kDefaultTileWidth = 256,
50 kDefaultTileHeight = 256
51};
52
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +000053/* TODO(epoger): These constants are already maintained in 2 other places:
54 * gm/gm_json.py and gm/gm_expectations.cpp. We shouldn't add yet a third place.
55 * Figure out a way to share the definitions instead.
56 */
57const static char kJsonKey_ActualResults[] = "actual-results";
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +000058const static char kJsonKey_ActualResults_NoComparison[] = "no-comparison";
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +000059const static char kJsonKey_Hashtype_Bitmap_64bitMD5[] = "bitmap-64bitMD5";
60
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +000061void ImageResultsSummary::add(const char *testName, uint64_t hash) {
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +000062 Json::Value jsonTypeValuePair;
63 jsonTypeValuePair.append(Json::Value(kJsonKey_Hashtype_Bitmap_64bitMD5));
64 jsonTypeValuePair.append(Json::UInt64(hash));
65 fActualResultsNoComparison[testName] = jsonTypeValuePair;
66}
67
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +000068void ImageResultsSummary::add(const char *testName, const SkBitmap& bitmap) {
69 uint64_t hash;
70 SkAssertResult(SkBitmapHasher::ComputeDigest(bitmap, &hash));
71 this->add(testName, hash);
72}
73
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +000074void ImageResultsSummary::writeToFile(const char *filename) {
75 Json::Value actualResults;
76 actualResults[kJsonKey_ActualResults_NoComparison] = fActualResultsNoComparison;
77 Json::Value root;
78 root[kJsonKey_ActualResults] = actualResults;
79 std::string jsonStdString = root.toStyledString();
80 SkFILEWStream stream(filename);
81 stream.write(jsonStdString.c_str(), jsonStdString.length());
82}
83
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +000084void PictureRenderer::init(SkPicture* pict, const SkString* outputDir,
85 const SkString* inputFilename, bool useChecksumBasedFilenames) {
86 this->CopyString(&fOutputDir, outputDir);
87 this->CopyString(&fInputFilename, inputFilename);
88 fUseChecksumBasedFilenames = useChecksumBasedFilenames;
89
keyar@chromium.org78a35c52012-08-20 15:03:44 +000090 SkASSERT(NULL == fPicture);
91 SkASSERT(NULL == fCanvas.get());
92 if (fPicture != NULL || NULL != fCanvas.get()) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +000093 return;
94 }
95
96 SkASSERT(pict != NULL);
keyar@chromium.org78a35c52012-08-20 15:03:44 +000097 if (NULL == pict) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +000098 return;
99 }
100
101 fPicture = pict;
junov@chromium.org9313ca42012-11-02 18:11:49 +0000102 fPicture->ref();
keyar@chromium.orga474ce32012-08-20 15:03:57 +0000103 fCanvas.reset(this->setupCanvas());
104}
105
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000106void PictureRenderer::CopyString(SkString* dest, const SkString* src) {
107 if (NULL != src) {
108 dest->set(*src);
109 } else {
110 dest->reset();
111 }
112}
113
caryclark@google.coma3622372012-11-06 21:26:13 +0000114class FlagsDrawFilter : public SkDrawFilter {
115public:
116 FlagsDrawFilter(PictureRenderer::DrawFilterFlags* flags) :
117 fFlags(flags) {}
118
reed@google.com971aca72012-11-26 20:26:54 +0000119 virtual bool filter(SkPaint* paint, Type t) {
caryclark@google.coma3622372012-11-06 21:26:13 +0000120 paint->setFlags(paint->getFlags() & ~fFlags[t] & SkPaint::kAllFlags);
robertphillips@google.com49149312013-07-03 15:34:35 +0000121 if (PictureRenderer::kMaskFilter_DrawFilterFlag & fFlags[t]) {
caryclark@google.coma3622372012-11-06 21:26:13 +0000122 SkMaskFilter* maskFilter = paint->getMaskFilter();
robertphillips@google.com49149312013-07-03 15:34:35 +0000123 if (NULL != maskFilter) {
reed@google.com457d8a72012-12-18 18:20:44 +0000124 paint->setMaskFilter(NULL);
caryclark@google.coma3622372012-11-06 21:26:13 +0000125 }
126 }
127 if (PictureRenderer::kHinting_DrawFilterFlag & fFlags[t]) {
128 paint->setHinting(SkPaint::kNo_Hinting);
129 } else if (PictureRenderer::kSlightHinting_DrawFilterFlag & fFlags[t]) {
130 paint->setHinting(SkPaint::kSlight_Hinting);
131 }
reed@google.com971aca72012-11-26 20:26:54 +0000132 return true;
caryclark@google.coma3622372012-11-06 21:26:13 +0000133 }
134
135private:
136 PictureRenderer::DrawFilterFlags* fFlags;
137};
138
scroggo@google.com82ec0b02012-12-17 19:25:54 +0000139static void setUpFilter(SkCanvas* canvas, PictureRenderer::DrawFilterFlags* drawFilters) {
caryclark@google.coma3622372012-11-06 21:26:13 +0000140 if (drawFilters && !canvas->getDrawFilter()) {
141 canvas->setDrawFilter(SkNEW_ARGS(FlagsDrawFilter, (drawFilters)))->unref();
caryclark@google.come3e940c2012-11-07 16:42:17 +0000142 if (drawFilters[0] & PictureRenderer::kAAClip_DrawFilterFlag) {
143 canvas->setAllowSoftClip(false);
144 }
caryclark@google.coma3622372012-11-06 21:26:13 +0000145 }
caryclark@google.coma3622372012-11-06 21:26:13 +0000146}
147
keyar@chromium.orga474ce32012-08-20 15:03:57 +0000148SkCanvas* PictureRenderer::setupCanvas() {
scroggo@google.comc0d5e542012-12-13 21:40:48 +0000149 const int width = this->getViewWidth();
150 const int height = this->getViewHeight();
151 return this->setupCanvas(width, height);
keyar@chromium.orga474ce32012-08-20 15:03:57 +0000152}
153
154SkCanvas* PictureRenderer::setupCanvas(int width, int height) {
caryclark@google.coma3622372012-11-06 21:26:13 +0000155 SkCanvas* canvas;
keyar@chromium.org4ea96c52012-08-20 15:03:29 +0000156 switch(fDeviceType) {
157 case kBitmap_DeviceType: {
158 SkBitmap bitmap;
keyar@chromium.orga474ce32012-08-20 15:03:57 +0000159 sk_tools::setup_bitmap(&bitmap, width, height);
caryclark@google.coma3622372012-11-06 21:26:13 +0000160 canvas = SkNEW_ARGS(SkCanvas, (bitmap));
keyar@chromium.org4ea96c52012-08-20 15:03:29 +0000161 }
scroggo@google.com82ec0b02012-12-17 19:25:54 +0000162 break;
keyar@chromium.org4ea96c52012-08-20 15:03:29 +0000163#if SK_SUPPORT_GPU
scroggo@google.com0556ea02013-02-08 19:38:21 +0000164#if SK_ANGLE
165 case kAngle_DeviceType:
166 // fall through
167#endif
rmistry@google.com6ab96732014-01-06 18:37:24 +0000168#if SK_MESA
169 case kMesa_DeviceType:
170 // fall through
171#endif
commit-bot@chromium.org0fd52702014-03-07 18:41:14 +0000172 case kGPU_DeviceType:
173 case kNVPR_DeviceType: {
commit-bot@chromium.orgae403b92013-04-10 17:27:30 +0000174 SkAutoTUnref<GrSurface> target;
scroggo@google.com0556ea02013-02-08 19:38:21 +0000175 if (fGrContext) {
176 // create a render target to back the device
177 GrTextureDesc desc;
178 desc.fConfig = kSkia8888_GrPixelConfig;
179 desc.fFlags = kRenderTarget_GrTextureFlagBit;
180 desc.fWidth = width;
181 desc.fHeight = height;
jvanverth@google.comf6a90332013-05-02 12:39:37 +0000182 desc.fSampleCnt = fSampleCount;
commit-bot@chromium.orgae403b92013-04-10 17:27:30 +0000183 target.reset(fGrContext->createUncachedTexture(desc, NULL, 0));
scroggo@google.com0556ea02013-02-08 19:38:21 +0000184 }
commit-bot@chromium.orgae403b92013-04-10 17:27:30 +0000185 if (NULL == target.get()) {
scroggo@google.com0556ea02013-02-08 19:38:21 +0000186 SkASSERT(0);
187 return NULL;
188 }
189
commit-bot@chromium.orgae403b92013-04-10 17:27:30 +0000190 SkAutoTUnref<SkGpuDevice> device(SkGpuDevice::Create(target));
caryclark@google.coma3622372012-11-06 21:26:13 +0000191 canvas = SkNEW_ARGS(SkCanvas, (device.get()));
scroggo@google.com0556ea02013-02-08 19:38:21 +0000192 break;
keyar@chromium.org4ea96c52012-08-20 15:03:29 +0000193 }
194#endif
195 default:
196 SkASSERT(0);
scroggo@google.com82ec0b02012-12-17 19:25:54 +0000197 return NULL;
keyar@chromium.org4ea96c52012-08-20 15:03:29 +0000198 }
scroggo@google.com82ec0b02012-12-17 19:25:54 +0000199 setUpFilter(canvas, fDrawFilters);
200 this->scaleToScaleFactor(canvas);
commit-bot@chromium.org17cc3ea2014-01-15 14:51:25 +0000201
202 // Pictures often lie about their extent (i.e., claim to be 100x100 but
203 // only ever draw to 90x100). Clear here so the undrawn portion will have
204 // a consistent color
205 canvas->clear(SK_ColorTRANSPARENT);
scroggo@google.com82ec0b02012-12-17 19:25:54 +0000206 return canvas;
207}
keyar@chromium.orga474ce32012-08-20 15:03:57 +0000208
scroggo@google.com82ec0b02012-12-17 19:25:54 +0000209void PictureRenderer::scaleToScaleFactor(SkCanvas* canvas) {
210 SkASSERT(canvas != NULL);
211 if (fScaleFactor != SK_Scalar1) {
212 canvas->scale(fScaleFactor, fScaleFactor);
213 }
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000214}
215
216void PictureRenderer::end() {
scroggo@google.com08085f82013-01-28 20:40:24 +0000217 this->resetState(true);
junov@chromium.org9313ca42012-11-02 18:11:49 +0000218 SkSafeUnref(fPicture);
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000219 fPicture = NULL;
220 fCanvas.reset(NULL);
221}
222
scroggo@google.comc0d5e542012-12-13 21:40:48 +0000223int PictureRenderer::getViewWidth() {
224 SkASSERT(fPicture != NULL);
robertphillips@google.com8ac811e2013-02-07 00:13:34 +0000225 int width = SkScalarCeilToInt(fPicture->width() * fScaleFactor);
scroggo@google.comc0d5e542012-12-13 21:40:48 +0000226 if (fViewport.width() > 0) {
227 width = SkMin32(width, fViewport.width());
228 }
229 return width;
230}
231
232int PictureRenderer::getViewHeight() {
233 SkASSERT(fPicture != NULL);
robertphillips@google.com8ac811e2013-02-07 00:13:34 +0000234 int height = SkScalarCeilToInt(fPicture->height() * fScaleFactor);
scroggo@google.comc0d5e542012-12-13 21:40:48 +0000235 if (fViewport.height() > 0) {
236 height = SkMin32(height, fViewport.height());
237 }
238 return height;
239}
240
junov@chromium.org9313ca42012-11-02 18:11:49 +0000241/** Converts fPicture to a picture that uses a BBoxHierarchy.
242 * PictureRenderer subclasses that are used to test picture playback
243 * should call this method during init.
244 */
245void PictureRenderer::buildBBoxHierarchy() {
246 SkASSERT(NULL != fPicture);
247 if (kNone_BBoxHierarchyType != fBBoxHierarchyType && NULL != fPicture) {
248 SkPicture* newPicture = this->createPicture();
249 SkCanvas* recorder = newPicture->beginRecording(fPicture->width(), fPicture->height(),
250 this->recordFlags());
251 fPicture->draw(recorder);
252 newPicture->endRecording();
253 fPicture->unref();
254 fPicture = newPicture;
255 }
256}
257
scroggo@google.com08085f82013-01-28 20:40:24 +0000258void PictureRenderer::resetState(bool callFinish) {
keyar@chromium.org28136b32012-08-20 15:04:15 +0000259#if SK_SUPPORT_GPU
robertphillips@google.com6177e692013-02-28 20:16:25 +0000260 SkGLContextHelper* glContext = this->getGLContext();
scroggo@google.com0556ea02013-02-08 19:38:21 +0000261 if (NULL == glContext) {
262 SkASSERT(kBitmap_DeviceType == fDeviceType);
263 return;
264 }
keyar@chromium.org28136b32012-08-20 15:04:15 +0000265
scroggo@google.com0556ea02013-02-08 19:38:21 +0000266 fGrContext->flush();
commit-bot@chromium.org51c040e2014-03-11 22:58:00 +0000267 glContext->swapBuffers();
scroggo@google.com0556ea02013-02-08 19:38:21 +0000268 if (callFinish) {
269 SK_GL(*glContext, Finish());
keyar@chromium.org77a55222012-08-20 15:03:47 +0000270 }
keyar@chromium.orga40c20d2012-08-20 15:04:12 +0000271#endif
keyar@chromium.org77a55222012-08-20 15:03:47 +0000272}
273
robertphillips@google.com94d8f1e2013-12-18 17:25:33 +0000274void PictureRenderer::purgeTextures() {
275 SkDiscardableMemoryPool* pool = SkGetGlobalDiscardableMemoryPool();
276
277 pool->dumpPool();
278
279#if SK_SUPPORT_GPU
280 SkGLContextHelper* glContext = this->getGLContext();
281 if (NULL == glContext) {
282 SkASSERT(kBitmap_DeviceType == fDeviceType);
283 return;
284 }
285
286 // resetState should've already done this
287 fGrContext->flush();
288
289 fGrContext->purgeAllUnlockedResources();
290#endif
291}
292
junov@chromium.org9313ca42012-11-02 18:11:49 +0000293uint32_t PictureRenderer::recordFlags() {
junov@chromium.org100b1c52013-01-16 20:12:22 +0000294 return ((kNone_BBoxHierarchyType == fBBoxHierarchyType) ? 0 :
295 SkPicture::kOptimizeForClippedPlayback_RecordingFlag) |
296 SkPicture::kUsePathBoundsForClip_RecordingFlag;
junov@chromium.org9313ca42012-11-02 18:11:49 +0000297}
298
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000299/**
300 * Write the canvas to the specified path.
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000301 *
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000302 * @param canvas Must be non-null. Canvas to be written to a file.
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000303 * @param outputDir If nonempty, write the binary image to a file within this directory.
304 * @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 +0000305 * @param jsonSummaryPtr If not null, add image results to this summary.
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000306 * @param useChecksumBasedFilenames If true, use checksum-based filenames when writing to disk.
307 * @param numberToAppend If not null, append this number to the filename.
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000308 * @return bool True if the Canvas is written to a file.
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +0000309 *
310 * TODO(epoger): Right now, all canvases must pass through this function in order to be appended
311 * to the ImageResultsSummary. We need some way to add bitmaps to the ImageResultsSummary
312 * even if --writePath has not been specified (and thus this function is not called).
313 *
314 * One fix would be to pass in these path elements separately, and allow this function to be
315 * called even if --writePath was not specified...
316 * const char *outputDir // NULL if we don't want to write image files to disk
317 * const char *filename // name we use within JSON summary, and as the filename within outputDir
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000318 *
319 * UPDATE: Now that outputDir and inputFilename are passed separately, we should be able to do that.
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000320 */
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000321static bool write(SkCanvas* canvas, const SkString& outputDir, const SkString& inputFilename,
322 ImageResultsSummary *jsonSummaryPtr, bool useChecksumBasedFilenames,
323 const int* numberToAppend=NULL) {
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000324 SkASSERT(canvas != NULL);
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000325 if (NULL == canvas) {
keyar@chromium.org9299ede2012-08-21 19:05:08 +0000326 return false;
327 }
328
329 SkBitmap bitmap;
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000330 SkISize size = canvas->getDeviceSize();
331 sk_tools::setup_bitmap(&bitmap, size.width(), size.height());
keyar@chromium.org9299ede2012-08-21 19:05:08 +0000332
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000333 // Make sure we only compute the bitmap hash once (at most).
334 uint64_t hash;
335 bool generatedHash = false;
336
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000337 canvas->readPixels(&bitmap, 0, 0);
keyar@chromium.org9299ede2012-08-21 19:05:08 +0000338 sk_tools::force_all_opaque(bitmap);
339
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000340 SkString outputFilename(inputFilename);
341 outputFilename.remove(outputFilename.size() - 4, 4);
342 if (NULL != numberToAppend) {
343 outputFilename.appendf("%i", *numberToAppend);
344 }
345 outputFilename.append(".png");
346 // TODO(epoger): what about including the config type within outputFilename? That way,
347 // we could combine results of different config types without conflicting filenames.
348
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +0000349 if (NULL != jsonSummaryPtr) {
commit-bot@chromium.orgb470c212014-03-19 22:58:52 +0000350 SkASSERT(!generatedHash);
351 SkAssertResult(SkBitmapHasher::ComputeDigest(bitmap, &hash));
352 generatedHash = true;
353
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000354 jsonSummaryPtr->add(outputFilename.c_str(), hash);
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +0000355 }
356
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000357 // Update outputFilename AFTER adding to JSON summary, but BEFORE writing out the image file.
358 if (useChecksumBasedFilenames) {
359 if (!generatedHash) {
360 SkAssertResult(SkBitmapHasher::ComputeDigest(bitmap, &hash));
361 generatedHash = true;
362 }
363 outputFilename.set(kJsonKey_Hashtype_Bitmap_64bitMD5);
364 outputFilename.append("_");
365 outputFilename.appendU64(hash);
366 outputFilename.append(".png");
367 }
368
369 SkASSERT(!outputDir.isEmpty()); // TODO(epoger): we want to remove this constraint,
370 // as noted above
371 SkString fullPathname;
372 make_filepath(&fullPathname, outputDir, outputFilename);
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +0000373 return SkImageEncoder::EncodeFile(fullPathname.c_str(), bitmap, SkImageEncoder::kPNG_Type, 100);
keyar@chromium.org9299ede2012-08-21 19:05:08 +0000374}
375
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000376///////////////////////////////////////////////////////////////////////////////////////////////
377
djsollen@google.comfd9720c2012-11-06 16:54:40 +0000378SkCanvas* RecordPictureRenderer::setupCanvas(int width, int height) {
379 // defer the canvas setup until the render step
380 return NULL;
381}
382
reed@google.com672588b2014-01-08 15:42:01 +0000383// the size_t* parameter is deprecated, so we ignore it
384static SkData* encode_bitmap_to_data(size_t*, const SkBitmap& bm) {
scroggo@google.com1b1bcc32013-05-21 20:31:23 +0000385 return SkImageEncoder::EncodeData(bm, SkImageEncoder::kPNG_Type, 100);
scroggo@google.coma9e3a362012-11-07 17:52:48 +0000386}
387
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000388bool RecordPictureRenderer::render(SkBitmap** out) {
junov@chromium.org9313ca42012-11-02 18:11:49 +0000389 SkAutoTUnref<SkPicture> replayer(this->createPicture());
scroggo@google.com82ec0b02012-12-17 19:25:54 +0000390 SkCanvas* recorder = replayer->beginRecording(this->getViewWidth(), this->getViewHeight(),
junov@chromium.org9313ca42012-11-02 18:11:49 +0000391 this->recordFlags());
scroggo@google.com82ec0b02012-12-17 19:25:54 +0000392 this->scaleToScaleFactor(recorder);
scroggo@google.com9a412522012-09-07 15:21:18 +0000393 fPicture->draw(recorder);
junov@chromium.org9313ca42012-11-02 18:11:49 +0000394 replayer->endRecording();
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000395 if (!fOutputDir.isEmpty()) {
scroggo@google.coma9e3a362012-11-07 17:52:48 +0000396 // Record the new picture as a new SKP with PNG encoded bitmaps.
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000397 SkString skpPath;
398 make_filepath(&skpPath, fOutputDir, fInputFilename);
scroggo@google.coma9e3a362012-11-07 17:52:48 +0000399 SkFILEWStream stream(skpPath.c_str());
scroggo@google.com1b1bcc32013-05-21 20:31:23 +0000400 replayer->serialize(&stream, &encode_bitmap_to_data);
scroggo@google.coma9e3a362012-11-07 17:52:48 +0000401 return true;
402 }
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000403 return false;
scroggo@google.com9a412522012-09-07 15:21:18 +0000404}
405
scroggo@google.com0a049b82012-11-02 22:01:26 +0000406SkString RecordPictureRenderer::getConfigNameInternal() {
407 return SkString("record");
408}
409
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000410///////////////////////////////////////////////////////////////////////////////////////////////
411
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000412bool PipePictureRenderer::render(SkBitmap** out) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000413 SkASSERT(fCanvas.get() != NULL);
414 SkASSERT(fPicture != NULL);
keyar@chromium.org78a35c52012-08-20 15:03:44 +0000415 if (NULL == fCanvas.get() || NULL == fPicture) {
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000416 return false;
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000417 }
418
419 PipeController pipeController(fCanvas.get());
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000420 SkGPipeWriter writer;
421 SkCanvas* pipeCanvas = writer.startRecording(&pipeController);
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000422 pipeCanvas->drawPicture(*fPicture);
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000423 writer.endRecording();
scroggo@google.com9a412522012-09-07 15:21:18 +0000424 fCanvas->flush();
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000425 if (!fOutputDir.isEmpty()) {
426 return write(fCanvas, fOutputDir, fInputFilename, fJsonSummaryPtr,
427 fUseChecksumBasedFilenames);
borenet@google.com070d3542012-10-26 13:26:55 +0000428 }
edisonn@google.com84f548c2012-12-18 22:24:03 +0000429 if (NULL != out) {
430 *out = SkNEW(SkBitmap);
431 setup_bitmap(*out, fPicture->width(), fPicture->height());
432 fCanvas->readPixels(*out, 0, 0);
skia.committer@gmail.coma7d8e3e2012-12-19 02:01:38 +0000433 }
borenet@google.com070d3542012-10-26 13:26:55 +0000434 return true;
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000435}
436
scroggo@google.com0a049b82012-11-02 22:01:26 +0000437SkString PipePictureRenderer::getConfigNameInternal() {
438 return SkString("pipe");
439}
440
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000441///////////////////////////////////////////////////////////////////////////////////////////////
442
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000443void SimplePictureRenderer::init(SkPicture* picture, const SkString* outputDir,
444 const SkString* inputFilename, bool useChecksumBasedFilenames) {
445 INHERITED::init(picture, outputDir, inputFilename, useChecksumBasedFilenames);
junov@chromium.org9313ca42012-11-02 18:11:49 +0000446 this->buildBBoxHierarchy();
447}
448
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000449bool SimplePictureRenderer::render(SkBitmap** out) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000450 SkASSERT(fCanvas.get() != NULL);
451 SkASSERT(fPicture != NULL);
keyar@chromium.org78a35c52012-08-20 15:03:44 +0000452 if (NULL == fCanvas.get() || NULL == fPicture) {
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000453 return false;
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000454 }
455
456 fCanvas->drawPicture(*fPicture);
scroggo@google.com9a412522012-09-07 15:21:18 +0000457 fCanvas->flush();
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000458 if (!fOutputDir.isEmpty()) {
459 return write(fCanvas, fOutputDir, fInputFilename, fJsonSummaryPtr,
460 fUseChecksumBasedFilenames);
borenet@google.com070d3542012-10-26 13:26:55 +0000461 }
skia.committer@gmail.coma7d8e3e2012-12-19 02:01:38 +0000462
edisonn@google.com84f548c2012-12-18 22:24:03 +0000463 if (NULL != out) {
464 *out = SkNEW(SkBitmap);
465 setup_bitmap(*out, fPicture->width(), fPicture->height());
466 fCanvas->readPixels(*out, 0, 0);
467 }
468
borenet@google.com070d3542012-10-26 13:26:55 +0000469 return true;
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000470}
471
scroggo@google.com0a049b82012-11-02 22:01:26 +0000472SkString SimplePictureRenderer::getConfigNameInternal() {
473 return SkString("simple");
474}
475
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000476///////////////////////////////////////////////////////////////////////////////////////////////
477
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000478TiledPictureRenderer::TiledPictureRenderer()
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000479 : fTileWidth(kDefaultTileWidth)
rileya@google.comb947b912012-08-29 17:35:07 +0000480 , fTileHeight(kDefaultTileHeight)
rileya@google.coma04dc022012-09-10 19:01:38 +0000481 , fTileWidthPercentage(0.0)
rileya@google.comb947b912012-08-29 17:35:07 +0000482 , fTileHeightPercentage(0.0)
scroggo@google.comcbcef702012-12-13 22:09:28 +0000483 , fTileMinPowerOf2Width(0)
484 , fCurrentTileOffset(-1)
485 , fTilesX(0)
486 , fTilesY(0) { }
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000487
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000488void TiledPictureRenderer::init(SkPicture* pict, const SkString* outputDir,
489 const SkString* inputFilename, bool useChecksumBasedFilenames) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000490 SkASSERT(pict != NULL);
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000491 SkASSERT(0 == fTileRects.count());
492 if (NULL == pict || fTileRects.count() != 0) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000493 return;
494 }
495
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000496 // Do not call INHERITED::init(), which would create a (potentially large) canvas which is not
497 // used by bench_pictures.
commit-bot@chromium.org8ddc26b2014-03-31 17:55:12 +0000498 fPicture = SkRef(pict);
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000499 this->CopyString(&fOutputDir, outputDir);
500 this->CopyString(&fInputFilename, inputFilename);
501 fUseChecksumBasedFilenames = useChecksumBasedFilenames;
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000502 this->buildBBoxHierarchy();
keyar@chromium.orgcc6e5ef2012-07-27 20:09:26 +0000503
504 if (fTileWidthPercentage > 0) {
robertphillips@google.com5d8d1862012-08-15 14:36:41 +0000505 fTileWidth = sk_float_ceil2int(float(fTileWidthPercentage * fPicture->width() / 100));
keyar@chromium.orgcc6e5ef2012-07-27 20:09:26 +0000506 }
507 if (fTileHeightPercentage > 0) {
robertphillips@google.com5d8d1862012-08-15 14:36:41 +0000508 fTileHeight = sk_float_ceil2int(float(fTileHeightPercentage * fPicture->height() / 100));
keyar@chromium.orgcc6e5ef2012-07-27 20:09:26 +0000509 }
510
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000511 if (fTileMinPowerOf2Width > 0) {
512 this->setupPowerOf2Tiles();
513 } else {
514 this->setupTiles();
515 }
scroggo@google.comcbcef702012-12-13 22:09:28 +0000516 fCanvas.reset(this->setupCanvas(fTileWidth, fTileHeight));
517 // Initialize to -1 so that the first call to nextTile will set this up to draw tile 0 on the
518 // first call to drawCurrentTile.
519 fCurrentTileOffset = -1;
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000520}
521
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000522void TiledPictureRenderer::end() {
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000523 fTileRects.reset();
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000524 this->INHERITED::end();
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000525}
526
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000527void TiledPictureRenderer::setupTiles() {
scroggo@google.comc0d5e542012-12-13 21:40:48 +0000528 // Only use enough tiles to cover the viewport
529 const int width = this->getViewWidth();
530 const int height = this->getViewHeight();
531
scroggo@google.comcbcef702012-12-13 22:09:28 +0000532 fTilesX = fTilesY = 0;
scroggo@google.comc0d5e542012-12-13 21:40:48 +0000533 for (int tile_y_start = 0; tile_y_start < height; tile_y_start += fTileHeight) {
scroggo@google.comcbcef702012-12-13 22:09:28 +0000534 fTilesY++;
scroggo@google.comc0d5e542012-12-13 21:40:48 +0000535 for (int tile_x_start = 0; tile_x_start < width; tile_x_start += fTileWidth) {
scroggo@google.comcbcef702012-12-13 22:09:28 +0000536 if (0 == tile_y_start) {
537 // Only count tiles in the X direction on the first pass.
538 fTilesX++;
539 }
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000540 *fTileRects.append() = SkRect::MakeXYWH(SkIntToScalar(tile_x_start),
541 SkIntToScalar(tile_y_start),
542 SkIntToScalar(fTileWidth),
543 SkIntToScalar(fTileHeight));
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000544 }
545 }
546}
547
scroggo@google.comcbcef702012-12-13 22:09:28 +0000548bool TiledPictureRenderer::tileDimensions(int &x, int &y) {
549 if (fTileRects.count() == 0 || NULL == fPicture) {
550 return false;
551 }
552 x = fTilesX;
553 y = fTilesY;
554 return true;
555}
556
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000557// The goal of the powers of two tiles is to minimize the amount of wasted tile
558// space in the width-wise direction and then minimize the number of tiles. The
559// constraints are that every tile must have a pixel width that is a power of
560// two and also be of some minimal width (that is also a power of two).
561//
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000562// This is solved by first taking our picture size and rounding it up to the
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000563// multiple of the minimal width. The binary representation of this rounded
564// value gives us the tiles we need: a bit of value one means we need a tile of
565// that size.
566void TiledPictureRenderer::setupPowerOf2Tiles() {
scroggo@google.comc0d5e542012-12-13 21:40:48 +0000567 // Only use enough tiles to cover the viewport
568 const int width = this->getViewWidth();
569 const int height = this->getViewHeight();
570
571 int rounded_value = width;
572 if (width % fTileMinPowerOf2Width != 0) {
573 rounded_value = width - (width % fTileMinPowerOf2Width) + fTileMinPowerOf2Width;
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000574 }
575
reed@google.come15b2f52013-12-18 04:59:26 +0000576 int num_bits = SkScalarCeilToInt(scalar_log2(SkIntToScalar(width)));
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000577 int largest_possible_tile_size = 1 << num_bits;
578
scroggo@google.comcbcef702012-12-13 22:09:28 +0000579 fTilesX = fTilesY = 0;
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000580 // The tile height is constant for a particular picture.
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++;
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000583 int tile_x_start = 0;
584 int current_width = largest_possible_tile_size;
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000585 // Set fTileWidth to be the width of the widest tile, so that each canvas is large enough
586 // to draw each tile.
587 fTileWidth = current_width;
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000588
589 while (current_width >= fTileMinPowerOf2Width) {
590 // It is very important this is a bitwise AND.
591 if (current_width & rounded_value) {
scroggo@google.comcbcef702012-12-13 22:09:28 +0000592 if (0 == tile_y_start) {
593 // Only count tiles in the X direction on the first pass.
594 fTilesX++;
595 }
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000596 *fTileRects.append() = SkRect::MakeXYWH(SkIntToScalar(tile_x_start),
597 SkIntToScalar(tile_y_start),
598 SkIntToScalar(current_width),
599 SkIntToScalar(fTileHeight));
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000600 tile_x_start += current_width;
601 }
602
603 current_width >>= 1;
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000604 }
605 }
606}
607
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000608/**
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +0000609 * Draw the specified picture to the canvas translated to rectangle provided, so that this mini
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000610 * canvas represents the rectangle's portion of the overall picture.
611 * Saves and restores so that the initial clip and matrix return to their state before this function
612 * is called.
613 */
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +0000614static void draw_tile_to_canvas(SkCanvas* canvas, const SkRect& tileRect, SkPicture* picture) {
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000615 int saveCount = canvas->save();
scroggo@google.com82ec0b02012-12-17 19:25:54 +0000616 // Translate so that we draw the correct portion of the picture.
617 // Perform a postTranslate so that the scaleFactor does not interfere with the positioning.
618 SkMatrix mat(canvas->getTotalMatrix());
619 mat.postTranslate(-tileRect.fLeft, -tileRect.fTop);
620 canvas->setMatrix(mat);
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +0000621 canvas->drawPicture(*picture);
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000622 canvas->restoreToCount(saveCount);
623 canvas->flush();
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000624}
625
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000626///////////////////////////////////////////////////////////////////////////////////////////////
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000627
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +0000628/**
629 * Copies the entirety of the src bitmap (typically a tile) into a portion of the dst bitmap.
630 * If the src bitmap is too large to fit within the dst bitmap after the x and y
631 * offsets have been applied, any excess will be ignored (so only the top-left portion of the
632 * src bitmap will be copied).
633 *
634 * @param src source bitmap
635 * @param dst destination bitmap
636 * @param xOffset x-offset within destination bitmap
637 * @param yOffset y-offset within destination bitmap
638 */
639static void bitmapCopyAtOffset(const SkBitmap& src, SkBitmap* dst,
640 int xOffset, int yOffset) {
641 for (int y = 0; y <src.height() && y + yOffset < dst->height() ; y++) {
642 for (int x = 0; x < src.width() && x + xOffset < dst->width() ; x++) {
643 *dst->getAddr32(xOffset + x, yOffset + y) = *src.getAddr32(x, y);
edisonn@google.com84f548c2012-12-18 22:24:03 +0000644 }
645 }
646}
647
scroggo@google.comcbcef702012-12-13 22:09:28 +0000648bool TiledPictureRenderer::nextTile(int &i, int &j) {
649 if (++fCurrentTileOffset < fTileRects.count()) {
650 i = fCurrentTileOffset % fTilesX;
651 j = fCurrentTileOffset / fTilesX;
652 return true;
653 }
654 return false;
655}
656
657void TiledPictureRenderer::drawCurrentTile() {
658 SkASSERT(fCurrentTileOffset >= 0 && fCurrentTileOffset < fTileRects.count());
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +0000659 draw_tile_to_canvas(fCanvas, fTileRects[fCurrentTileOffset], fPicture);
scroggo@google.comcbcef702012-12-13 22:09:28 +0000660}
661
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000662bool TiledPictureRenderer::render(SkBitmap** out) {
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000663 SkASSERT(fPicture != NULL);
664 if (NULL == fPicture) {
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000665 return false;
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000666 }
667
edisonn@google.com84f548c2012-12-18 22:24:03 +0000668 SkBitmap bitmap;
669 if (out){
670 *out = SkNEW(SkBitmap);
671 setup_bitmap(*out, fPicture->width(), fPicture->height());
672 setup_bitmap(&bitmap, fTileWidth, fTileHeight);
673 }
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000674 bool success = true;
675 for (int i = 0; i < fTileRects.count(); ++i) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +0000676 draw_tile_to_canvas(fCanvas, fTileRects[i], fPicture);
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000677 if (!fOutputDir.isEmpty()) {
678 success &= write(fCanvas, fOutputDir, fInputFilename, fJsonSummaryPtr,
679 fUseChecksumBasedFilenames, &i);
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000680 }
edisonn@google.com84f548c2012-12-18 22:24:03 +0000681 if (NULL != out) {
682 if (fCanvas->readPixels(&bitmap, 0, 0)) {
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +0000683 // Add this tile to the entire bitmap.
684 bitmapCopyAtOffset(bitmap, *out, SkScalarFloorToInt(fTileRects[i].left()),
685 SkScalarFloorToInt(fTileRects[i].top()));
edisonn@google.com84f548c2012-12-18 22:24:03 +0000686 } else {
687 success = false;
688 }
689 }
keyar@chromium.org163b5672012-08-01 17:53:29 +0000690 }
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000691 return success;
keyar@chromium.org163b5672012-08-01 17:53:29 +0000692}
693
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000694SkCanvas* TiledPictureRenderer::setupCanvas(int width, int height) {
695 SkCanvas* canvas = this->INHERITED::setupCanvas(width, height);
commit-bot@chromium.org8ddc26b2014-03-31 17:55:12 +0000696 SkASSERT(NULL != fPicture);
scroggo@google.com82ec0b02012-12-17 19:25:54 +0000697 // Clip the tile to an area that is completely inside both the SkPicture and the viewport. This
698 // is mostly important for tiles on the right and bottom edges as they may go over this area and
699 // the picture may have some commands that draw outside of this area and so should not actually
700 // be written.
701 // Uses a clipRegion so that it will be unaffected by the scale factor, which may have been set
702 // by INHERITED::setupCanvas.
703 SkRegion clipRegion;
704 clipRegion.setRect(0, 0, this->getViewWidth(), this->getViewHeight());
705 canvas->clipRegion(clipRegion);
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000706 return canvas;
707}
scroggo@google.com0a049b82012-11-02 22:01:26 +0000708
709SkString TiledPictureRenderer::getConfigNameInternal() {
710 SkString name;
711 if (fTileMinPowerOf2Width > 0) {
712 name.append("pow2tile_");
713 name.appendf("%i", fTileMinPowerOf2Width);
714 } else {
715 name.append("tile_");
716 if (fTileWidthPercentage > 0) {
717 name.appendf("%.f%%", fTileWidthPercentage);
718 } else {
719 name.appendf("%i", fTileWidth);
720 }
721 }
722 name.append("x");
723 if (fTileHeightPercentage > 0) {
724 name.appendf("%.f%%", fTileHeightPercentage);
725 } else {
726 name.appendf("%i", fTileHeight);
727 }
728 return name;
729}
730
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000731///////////////////////////////////////////////////////////////////////////////////////////////
732
733// Holds all of the information needed to draw a set of tiles.
734class CloneData : public SkRunnable {
735
736public:
737 CloneData(SkPicture* clone, SkCanvas* canvas, SkTDArray<SkRect>& rects, int start, int end,
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000738 SkRunnable* done, ImageResultsSummary* jsonSummaryPtr, bool useChecksumBasedFilenames)
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000739 : fClone(clone)
740 , fCanvas(canvas)
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000741 , fRects(rects)
742 , fStart(start)
743 , fEnd(end)
744 , fSuccess(NULL)
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +0000745 , fDone(done)
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000746 , fJsonSummaryPtr(jsonSummaryPtr)
747 , fUseChecksumBasedFilenames(useChecksumBasedFilenames) {
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000748 SkASSERT(fDone != NULL);
749 }
750
751 virtual void run() SK_OVERRIDE {
752 SkGraphics::SetTLSFontCacheLimit(1024 * 1024);
edisonn@google.com84f548c2012-12-18 22:24:03 +0000753
754 SkBitmap bitmap;
755 if (fBitmap != NULL) {
756 // All tiles are the same size.
jvanverth@google.com9c4e5ac2013-01-07 18:41:28 +0000757 setup_bitmap(&bitmap, SkScalarFloorToInt(fRects[0].width()), SkScalarFloorToInt(fRects[0].height()));
edisonn@google.com84f548c2012-12-18 22:24:03 +0000758 }
skia.committer@gmail.coma7d8e3e2012-12-19 02:01:38 +0000759
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000760 for (int i = fStart; i < fEnd; i++) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +0000761 draw_tile_to_canvas(fCanvas, fRects[i], fClone);
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000762 if ((!fOutputDir.isEmpty())
763 && !write(fCanvas, fOutputDir, fInputFilename, fJsonSummaryPtr,
764 fUseChecksumBasedFilenames, &i)
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000765 && fSuccess != NULL) {
766 *fSuccess = false;
767 // If one tile fails to write to a file, do not continue drawing the rest.
768 break;
769 }
edisonn@google.com84f548c2012-12-18 22:24:03 +0000770 if (fBitmap != NULL) {
771 if (fCanvas->readPixels(&bitmap, 0, 0)) {
772 SkAutoLockPixels alp(*fBitmap);
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +0000773 bitmapCopyAtOffset(bitmap, fBitmap, SkScalarFloorToInt(fRects[i].left()),
774 SkScalarFloorToInt(fRects[i].top()));
edisonn@google.com84f548c2012-12-18 22:24:03 +0000775 } else {
776 *fSuccess = false;
777 // If one tile fails to read pixels, do not continue drawing the rest.
778 break;
779 }
780 }
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000781 }
782 fDone->run();
783 }
784
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000785 void setPathsAndSuccess(const SkString& outputDir, const SkString& inputFilename,
786 bool* success) {
787 fOutputDir.set(outputDir);
788 fInputFilename.set(inputFilename);
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000789 fSuccess = success;
790 }
791
edisonn@google.com84f548c2012-12-18 22:24:03 +0000792 void setBitmap(SkBitmap* bitmap) {
793 fBitmap = bitmap;
794 }
795
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000796private:
797 // All pointers unowned.
798 SkPicture* fClone; // Picture to draw from. Each CloneData has a unique one which
799 // is threadsafe.
800 SkCanvas* fCanvas; // Canvas to draw to. Reused for each tile.
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000801 SkString fOutputDir; // If not empty, write results into this directory.
802 SkString fInputFilename; // Filename of input SkPicture file.
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000803 SkTDArray<SkRect>& fRects; // All tiles of the picture.
804 const int fStart; // Range of tiles drawn by this thread.
805 const int fEnd;
806 bool* fSuccess; // Only meaningful if path is non-null. Shared by all threads,
807 // and only set to false upon failure to write to a PNG.
808 SkRunnable* fDone;
edisonn@google.com84f548c2012-12-18 22:24:03 +0000809 SkBitmap* fBitmap;
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +0000810 ImageResultsSummary* fJsonSummaryPtr;
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000811 bool fUseChecksumBasedFilenames;
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000812};
813
814MultiCorePictureRenderer::MultiCorePictureRenderer(int threadCount)
815: fNumThreads(threadCount)
816, fThreadPool(threadCount)
817, fCountdown(threadCount) {
818 // Only need to create fNumThreads - 1 clones, since one thread will use the base
819 // picture.
820 fPictureClones = SkNEW_ARRAY(SkPicture, fNumThreads - 1);
821 fCloneData = SkNEW_ARRAY(CloneData*, fNumThreads);
822}
823
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000824void MultiCorePictureRenderer::init(SkPicture *pict, const SkString* outputDir,
825 const SkString* inputFilename, bool useChecksumBasedFilenames) {
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000826 // Set fPicture and the tiles.
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000827 this->INHERITED::init(pict, outputDir, inputFilename, useChecksumBasedFilenames);
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000828 for (int i = 0; i < fNumThreads; ++i) {
829 *fCanvasPool.append() = this->setupCanvas(this->getTileWidth(), this->getTileHeight());
830 }
831 // Only need to create fNumThreads - 1 clones, since one thread will use the base picture.
832 fPicture->clone(fPictureClones, fNumThreads - 1);
833 // Populate each thread with the appropriate data.
834 // Group the tiles into nearly equal size chunks, rounding up so we're sure to cover them all.
835 const int chunkSize = (fTileRects.count() + fNumThreads - 1) / fNumThreads;
836
837 for (int i = 0; i < fNumThreads; i++) {
838 SkPicture* pic;
839 if (i == fNumThreads-1) {
840 // The last set will use the original SkPicture.
841 pic = fPicture;
842 } else {
843 pic = &fPictureClones[i];
844 }
845 const int start = i * chunkSize;
846 const int end = SkMin32(start + chunkSize, fTileRects.count());
847 fCloneData[i] = SkNEW_ARGS(CloneData,
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +0000848 (pic, fCanvasPool[i], fTileRects, start, end, &fCountdown,
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000849 fJsonSummaryPtr, useChecksumBasedFilenames));
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000850 }
851}
852
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000853bool MultiCorePictureRenderer::render(SkBitmap** out) {
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000854 bool success = true;
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000855 if (!fOutputDir.isEmpty()) {
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000856 for (int i = 0; i < fNumThreads-1; i++) {
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000857 fCloneData[i]->setPathsAndSuccess(fOutputDir, fInputFilename, &success);
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000858 }
859 }
860
edisonn@google.com84f548c2012-12-18 22:24:03 +0000861 if (NULL != out) {
862 *out = SkNEW(SkBitmap);
863 setup_bitmap(*out, fPicture->width(), fPicture->height());
864 for (int i = 0; i < fNumThreads; i++) {
865 fCloneData[i]->setBitmap(*out);
866 }
867 } else {
868 for (int i = 0; i < fNumThreads; i++) {
869 fCloneData[i]->setBitmap(NULL);
870 }
871 }
872
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000873 fCountdown.reset(fNumThreads);
874 for (int i = 0; i < fNumThreads; i++) {
875 fThreadPool.add(fCloneData[i]);
876 }
877 fCountdown.wait();
878
879 return success;
880}
881
882void MultiCorePictureRenderer::end() {
883 for (int i = 0; i < fNumThreads - 1; i++) {
884 SkDELETE(fCloneData[i]);
885 fCloneData[i] = NULL;
886 }
887
888 fCanvasPool.unrefAll();
889
890 this->INHERITED::end();
891}
892
893MultiCorePictureRenderer::~MultiCorePictureRenderer() {
894 // Each individual CloneData was deleted in end.
895 SkDELETE_ARRAY(fCloneData);
896 SkDELETE_ARRAY(fPictureClones);
897}
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000898
scroggo@google.com0a049b82012-11-02 22:01:26 +0000899SkString MultiCorePictureRenderer::getConfigNameInternal() {
900 SkString name = this->INHERITED::getConfigNameInternal();
901 name.appendf("_multi_%i_threads", fNumThreads);
902 return name;
903}
904
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000905///////////////////////////////////////////////////////////////////////////////////////////////
scroggo@google.com9a412522012-09-07 15:21:18 +0000906
907void PlaybackCreationRenderer::setup() {
junov@chromium.org9313ca42012-11-02 18:11:49 +0000908 fReplayer.reset(this->createPicture());
scroggo@google.com82ec0b02012-12-17 19:25:54 +0000909 SkCanvas* recorder = fReplayer->beginRecording(this->getViewWidth(), this->getViewHeight(),
junov@chromium.org9313ca42012-11-02 18:11:49 +0000910 this->recordFlags());
scroggo@google.com82ec0b02012-12-17 19:25:54 +0000911 this->scaleToScaleFactor(recorder);
commit-bot@chromium.org8ddc26b2014-03-31 17:55:12 +0000912 recorder->drawPicture(*fPicture);
scroggo@google.com9a412522012-09-07 15:21:18 +0000913}
914
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000915bool PlaybackCreationRenderer::render(SkBitmap** out) {
junov@chromium.org9313ca42012-11-02 18:11:49 +0000916 fReplayer->endRecording();
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000917 // Since this class does not actually render, return false.
918 return false;
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000919}
920
scroggo@google.com0a049b82012-11-02 22:01:26 +0000921SkString PlaybackCreationRenderer::getConfigNameInternal() {
922 return SkString("playback_creation");
923}
924
junov@chromium.org9313ca42012-11-02 18:11:49 +0000925///////////////////////////////////////////////////////////////////////////////////////////////
926// SkPicture variants for each BBoxHierarchy type
927
928class RTreePicture : public SkPicture {
929public:
930 virtual SkBBoxHierarchy* createBBoxHierarchy() const SK_OVERRIDE{
931 static const int kRTreeMinChildren = 6;
932 static const int kRTreeMaxChildren = 11;
933 SkScalar aspectRatio = SkScalarDiv(SkIntToScalar(fWidth),
934 SkIntToScalar(fHeight));
sglez@google.com8c902122013-08-30 17:27:47 +0000935 bool sortDraws = false;
junov@chromium.org9313ca42012-11-02 18:11:49 +0000936 return SkRTree::Create(kRTreeMinChildren, kRTreeMaxChildren,
sglez@google.com8c902122013-08-30 17:27:47 +0000937 aspectRatio, sortDraws);
junov@chromium.org9313ca42012-11-02 18:11:49 +0000938 }
939};
940
941SkPicture* PictureRenderer::createPicture() {
942 switch (fBBoxHierarchyType) {
943 case kNone_BBoxHierarchyType:
944 return SkNEW(SkPicture);
commit-bot@chromium.orgc22d1392014-02-03 18:08:33 +0000945 case kQuadTree_BBoxHierarchyType:
946 return SkNEW_ARGS(SkQuadTreePicture, (SkIRect::MakeWH(fPicture->width(),
commit-bot@chromium.org8ddc26b2014-03-31 17:55:12 +0000947 fPicture->height())));
junov@chromium.org9313ca42012-11-02 18:11:49 +0000948 case kRTree_BBoxHierarchyType:
949 return SkNEW(RTreePicture);
junov@chromium.org7b537062012-11-06 18:58:43 +0000950 case kTileGrid_BBoxHierarchyType:
junov@chromium.org29b19e52013-02-27 18:35:16 +0000951 return SkNEW_ARGS(SkTileGridPicture, (fPicture->width(),
commit-bot@chromium.org8ddc26b2014-03-31 17:55:12 +0000952 fPicture->height(), fGridInfo));
junov@chromium.org9313ca42012-11-02 18:11:49 +0000953 }
954 SkASSERT(0); // invalid bbhType
955 return NULL;
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000956}
junov@chromium.org9313ca42012-11-02 18:11:49 +0000957
reed@google.comfe7b1ed2012-11-29 21:00:39 +0000958///////////////////////////////////////////////////////////////////////////////
959
960class GatherRenderer : public PictureRenderer {
961public:
commit-bot@chromium.org8ddc26b2014-03-31 17:55:12 +0000962 virtual bool render(SkBitmap** out = NULL) SK_OVERRIDE {
reed@google.comfe7b1ed2012-11-29 21:00:39 +0000963 SkRect bounds = SkRect::MakeWH(SkIntToScalar(fPicture->width()),
964 SkIntToScalar(fPicture->height()));
965 SkData* data = SkPictureUtils::GatherPixelRefs(fPicture, bounds);
966 SkSafeUnref(data);
skia.committer@gmail.comc7b4be72012-12-11 02:01:20 +0000967
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000968 return (fOutputDir.isEmpty()); // we don't have anything to write
reed@google.comfe7b1ed2012-11-29 21:00:39 +0000969 }
skia.committer@gmail.comc7b4be72012-12-11 02:01:20 +0000970
reed@google.comfe7b1ed2012-11-29 21:00:39 +0000971private:
972 virtual SkString getConfigNameInternal() SK_OVERRIDE {
973 return SkString("gather_pixelrefs");
974 }
975};
976
977PictureRenderer* CreateGatherPixelRefsRenderer() {
978 return SkNEW(GatherRenderer);
979}
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +0000980
reed@google.com5a34fd32012-12-10 16:05:09 +0000981///////////////////////////////////////////////////////////////////////////////
982
983class PictureCloneRenderer : public PictureRenderer {
984public:
commit-bot@chromium.org8ddc26b2014-03-31 17:55:12 +0000985 virtual bool render(SkBitmap** out = NULL) SK_OVERRIDE {
reed@google.com5a34fd32012-12-10 16:05:09 +0000986 for (int i = 0; i < 100; ++i) {
987 SkPicture* clone = fPicture->clone();
988 SkSafeUnref(clone);
989 }
skia.committer@gmail.comc7b4be72012-12-11 02:01:20 +0000990
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000991 return (fOutputDir.isEmpty()); // we don't have anything to write
reed@google.com5a34fd32012-12-10 16:05:09 +0000992 }
skia.committer@gmail.comc7b4be72012-12-11 02:01:20 +0000993
reed@google.com5a34fd32012-12-10 16:05:09 +0000994private:
995 virtual SkString getConfigNameInternal() SK_OVERRIDE {
996 return SkString("picture_clone");
997 }
998};
999
1000PictureRenderer* CreatePictureCloneRenderer() {
1001 return SkNEW(PictureCloneRenderer);
1002}
1003
junov@chromium.org9313ca42012-11-02 18:11:49 +00001004} // namespace sk_tools