blob: f71b9544e025781394eef60acf0ca519a1aa17fc [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"
scroggo@google.com1b1bcc32013-05-21 20:31:23 +000027#include "SkPictureUtils.h"
28#include "SkPixelRef.h"
commit-bot@chromium.orgc22d1392014-02-03 18:08:33 +000029#include "SkQuadTree.h"
30#include "SkQuadTreePicture.h"
junov@chromium.org9313ca42012-11-02 18:11:49 +000031#include "SkRTree.h"
keyar@chromium.orgea826952012-08-23 15:24:13 +000032#include "SkScalar.h"
scroggo@google.coma9e3a362012-11-07 17:52:48 +000033#include "SkStream.h"
keyar@chromium.org9299ede2012-08-21 19:05:08 +000034#include "SkString.h"
scroggo@google.com58b4ead2012-08-31 16:15:22 +000035#include "SkTemplates.h"
junov@chromium.org3cb834b2012-12-13 16:39:53 +000036#include "SkTileGridPicture.h"
keyar@chromium.org451bb9f2012-07-26 17:27:57 +000037#include "SkTDArray.h"
scroggo@google.com58b4ead2012-08-31 16:15:22 +000038#include "SkThreadUtils.h"
keyar@chromium.org451bb9f2012-07-26 17:27:57 +000039#include "SkTypes.h"
keyar@chromium.org4ea96c52012-08-20 15:03:29 +000040
reed@google.come15b2f52013-12-18 04:59:26 +000041static inline SkScalar scalar_log2(SkScalar x) {
42 static const SkScalar log2_conversion_factor = SkScalarDiv(1, SkScalarLog(2));
skia.committer@gmail.com3b85deb2013-12-18 07:01:56 +000043
reed@google.come15b2f52013-12-18 04:59:26 +000044 return SkScalarLog(x) * log2_conversion_factor;
45}
46
keyar@chromium.org451bb9f2012-07-26 17:27:57 +000047namespace sk_tools {
48
49enum {
50 kDefaultTileWidth = 256,
51 kDefaultTileHeight = 256
52};
53
commit-bot@chromium.org24c568c2014-04-10 15:39:02 +000054/*
55 * TODO(epoger): Make constant strings consistent instead of mixing hypenated and camel-caps.
56 *
57 * TODO(epoger): Similar constants are already maintained in 2 other places:
58 * 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 +000059 * Figure out a way to share the definitions instead.
commit-bot@chromium.org24c568c2014-04-10 15:39:02 +000060 *
61 * Note that, as of https://codereview.chromium.org/226293002 , the JSON
62 * schema used here has started to differ from the one in gm_expectations.cpp .
63 * TODO(epoger): Consider getting GM and render_pictures to use the same JSON
64 * output module.
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +000065 */
commit-bot@chromium.org24c568c2014-04-10 15:39:02 +000066const static char kJsonKey_ActualResults[] = "actual-results";
67const static char kJsonKey_Header[] = "header";
68const static char kJsonKey_Header_Type[] = "type";
69const static char kJsonKey_Header_Revision[] = "revision"; // unique within Type
70const static char kJsonKey_Image_ChecksumAlgorithm[] = "checksumAlgorithm";
71const static char kJsonKey_Image_ChecksumValue[] = "checksumValue";
72const static char kJsonKey_Image_ComparisonResult[] = "comparisonResult";
73const static char kJsonKey_Image_Filepath[] = "filepath";
74const static char kJsonKey_Source_TiledImages[] = "tiled-images";
75const static char kJsonKey_Source_WholeImage[] = "whole-image";
76// Values (not keys) that are written out by this JSON generator
77const static char kJsonValue_Header_Type[] = "ChecksummedImages";
78const static int kJsonValue_Header_Revision = 1;
79const static char kJsonValue_Image_ChecksumAlgorithm_Bitmap64bitMD5[] = "bitmap-64bitMD5";
80const static char kJsonValue_Image_ComparisonResult_NoComparison[] = "no-comparison";
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +000081
commit-bot@chromium.org24c568c2014-04-10 15:39:02 +000082void ImageResultsSummary::add(const char *sourceName, const char *fileName, uint64_t hash,
83 const int *tileNumber) {
84 Json::Value image;
85 image[kJsonKey_Image_ChecksumAlgorithm] = kJsonValue_Image_ChecksumAlgorithm_Bitmap64bitMD5;
86 image[kJsonKey_Image_ChecksumValue] = Json::UInt64(hash);
87 image[kJsonKey_Image_ComparisonResult] = kJsonValue_Image_ComparisonResult_NoComparison;
88 image[kJsonKey_Image_Filepath] = fileName;
89 if (NULL == tileNumber) {
90 fActualResults[sourceName][kJsonKey_Source_WholeImage] = image;
91 } else {
92 fActualResults[sourceName][kJsonKey_Source_TiledImages][*tileNumber] = image;
93 }
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +000094}
95
commit-bot@chromium.org24c568c2014-04-10 15:39:02 +000096void ImageResultsSummary::add(const char *sourceName, const char *fileName, const SkBitmap& bitmap,
97 const int *tileNumber) {
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +000098 uint64_t hash;
99 SkAssertResult(SkBitmapHasher::ComputeDigest(bitmap, &hash));
commit-bot@chromium.org24c568c2014-04-10 15:39:02 +0000100 this->add(sourceName, fileName, hash, tileNumber);
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000101}
102
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +0000103void ImageResultsSummary::writeToFile(const char *filename) {
commit-bot@chromium.org24c568c2014-04-10 15:39:02 +0000104 Json::Value header;
105 header[kJsonKey_Header_Type] = kJsonValue_Header_Type;
106 header[kJsonKey_Header_Revision] = kJsonValue_Header_Revision;
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +0000107 Json::Value root;
commit-bot@chromium.org24c568c2014-04-10 15:39:02 +0000108 root[kJsonKey_Header] = header;
109 root[kJsonKey_ActualResults] = fActualResults;
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +0000110 std::string jsonStdString = root.toStyledString();
111 SkFILEWStream stream(filename);
112 stream.write(jsonStdString.c_str(), jsonStdString.length());
113}
114
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000115void PictureRenderer::init(SkPicture* pict, const SkString* outputDir,
116 const SkString* inputFilename, bool useChecksumBasedFilenames) {
117 this->CopyString(&fOutputDir, outputDir);
118 this->CopyString(&fInputFilename, inputFilename);
119 fUseChecksumBasedFilenames = useChecksumBasedFilenames;
120
keyar@chromium.org78a35c52012-08-20 15:03:44 +0000121 SkASSERT(NULL == fPicture);
122 SkASSERT(NULL == fCanvas.get());
123 if (fPicture != NULL || NULL != fCanvas.get()) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000124 return;
125 }
126
127 SkASSERT(pict != NULL);
keyar@chromium.org78a35c52012-08-20 15:03:44 +0000128 if (NULL == pict) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000129 return;
130 }
131
132 fPicture = pict;
junov@chromium.org9313ca42012-11-02 18:11:49 +0000133 fPicture->ref();
keyar@chromium.orga474ce32012-08-20 15:03:57 +0000134 fCanvas.reset(this->setupCanvas());
135}
136
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000137void PictureRenderer::CopyString(SkString* dest, const SkString* src) {
138 if (NULL != src) {
139 dest->set(*src);
140 } else {
141 dest->reset();
142 }
143}
144
caryclark@google.coma3622372012-11-06 21:26:13 +0000145class FlagsDrawFilter : public SkDrawFilter {
146public:
147 FlagsDrawFilter(PictureRenderer::DrawFilterFlags* flags) :
148 fFlags(flags) {}
149
reed@google.com971aca72012-11-26 20:26:54 +0000150 virtual bool filter(SkPaint* paint, Type t) {
caryclark@google.coma3622372012-11-06 21:26:13 +0000151 paint->setFlags(paint->getFlags() & ~fFlags[t] & SkPaint::kAllFlags);
robertphillips@google.com49149312013-07-03 15:34:35 +0000152 if (PictureRenderer::kMaskFilter_DrawFilterFlag & fFlags[t]) {
caryclark@google.coma3622372012-11-06 21:26:13 +0000153 SkMaskFilter* maskFilter = paint->getMaskFilter();
robertphillips@google.com49149312013-07-03 15:34:35 +0000154 if (NULL != maskFilter) {
reed@google.com457d8a72012-12-18 18:20:44 +0000155 paint->setMaskFilter(NULL);
caryclark@google.coma3622372012-11-06 21:26:13 +0000156 }
157 }
158 if (PictureRenderer::kHinting_DrawFilterFlag & fFlags[t]) {
159 paint->setHinting(SkPaint::kNo_Hinting);
160 } else if (PictureRenderer::kSlightHinting_DrawFilterFlag & fFlags[t]) {
161 paint->setHinting(SkPaint::kSlight_Hinting);
162 }
reed@google.com971aca72012-11-26 20:26:54 +0000163 return true;
caryclark@google.coma3622372012-11-06 21:26:13 +0000164 }
165
166private:
167 PictureRenderer::DrawFilterFlags* fFlags;
168};
169
scroggo@google.com82ec0b02012-12-17 19:25:54 +0000170static void setUpFilter(SkCanvas* canvas, PictureRenderer::DrawFilterFlags* drawFilters) {
caryclark@google.coma3622372012-11-06 21:26:13 +0000171 if (drawFilters && !canvas->getDrawFilter()) {
172 canvas->setDrawFilter(SkNEW_ARGS(FlagsDrawFilter, (drawFilters)))->unref();
caryclark@google.come3e940c2012-11-07 16:42:17 +0000173 if (drawFilters[0] & PictureRenderer::kAAClip_DrawFilterFlag) {
174 canvas->setAllowSoftClip(false);
175 }
caryclark@google.coma3622372012-11-06 21:26:13 +0000176 }
caryclark@google.coma3622372012-11-06 21:26:13 +0000177}
178
keyar@chromium.orga474ce32012-08-20 15:03:57 +0000179SkCanvas* PictureRenderer::setupCanvas() {
scroggo@google.comc0d5e542012-12-13 21:40:48 +0000180 const int width = this->getViewWidth();
181 const int height = this->getViewHeight();
182 return this->setupCanvas(width, height);
keyar@chromium.orga474ce32012-08-20 15:03:57 +0000183}
184
185SkCanvas* PictureRenderer::setupCanvas(int width, int height) {
caryclark@google.coma3622372012-11-06 21:26:13 +0000186 SkCanvas* canvas;
keyar@chromium.org4ea96c52012-08-20 15:03:29 +0000187 switch(fDeviceType) {
188 case kBitmap_DeviceType: {
189 SkBitmap bitmap;
keyar@chromium.orga474ce32012-08-20 15:03:57 +0000190 sk_tools::setup_bitmap(&bitmap, width, height);
caryclark@google.coma3622372012-11-06 21:26:13 +0000191 canvas = SkNEW_ARGS(SkCanvas, (bitmap));
keyar@chromium.org4ea96c52012-08-20 15:03:29 +0000192 }
scroggo@google.com82ec0b02012-12-17 19:25:54 +0000193 break;
keyar@chromium.org4ea96c52012-08-20 15:03:29 +0000194#if SK_SUPPORT_GPU
scroggo@google.com0556ea02013-02-08 19:38:21 +0000195#if SK_ANGLE
196 case kAngle_DeviceType:
197 // fall through
198#endif
rmistry@google.com6ab96732014-01-06 18:37:24 +0000199#if SK_MESA
200 case kMesa_DeviceType:
201 // fall through
202#endif
commit-bot@chromium.org0fd52702014-03-07 18:41:14 +0000203 case kGPU_DeviceType:
204 case kNVPR_DeviceType: {
commit-bot@chromium.orgae403b92013-04-10 17:27:30 +0000205 SkAutoTUnref<GrSurface> target;
scroggo@google.com0556ea02013-02-08 19:38:21 +0000206 if (fGrContext) {
207 // create a render target to back the device
208 GrTextureDesc desc;
209 desc.fConfig = kSkia8888_GrPixelConfig;
210 desc.fFlags = kRenderTarget_GrTextureFlagBit;
211 desc.fWidth = width;
212 desc.fHeight = height;
jvanverth@google.comf6a90332013-05-02 12:39:37 +0000213 desc.fSampleCnt = fSampleCount;
commit-bot@chromium.orgae403b92013-04-10 17:27:30 +0000214 target.reset(fGrContext->createUncachedTexture(desc, NULL, 0));
scroggo@google.com0556ea02013-02-08 19:38:21 +0000215 }
commit-bot@chromium.orgae403b92013-04-10 17:27:30 +0000216 if (NULL == target.get()) {
scroggo@google.com0556ea02013-02-08 19:38:21 +0000217 SkASSERT(0);
218 return NULL;
219 }
220
commit-bot@chromium.orgae403b92013-04-10 17:27:30 +0000221 SkAutoTUnref<SkGpuDevice> device(SkGpuDevice::Create(target));
caryclark@google.coma3622372012-11-06 21:26:13 +0000222 canvas = SkNEW_ARGS(SkCanvas, (device.get()));
scroggo@google.com0556ea02013-02-08 19:38:21 +0000223 break;
keyar@chromium.org4ea96c52012-08-20 15:03:29 +0000224 }
225#endif
226 default:
227 SkASSERT(0);
scroggo@google.com82ec0b02012-12-17 19:25:54 +0000228 return NULL;
keyar@chromium.org4ea96c52012-08-20 15:03:29 +0000229 }
scroggo@google.com82ec0b02012-12-17 19:25:54 +0000230 setUpFilter(canvas, fDrawFilters);
231 this->scaleToScaleFactor(canvas);
commit-bot@chromium.org17cc3ea2014-01-15 14:51:25 +0000232
233 // Pictures often lie about their extent (i.e., claim to be 100x100 but
234 // only ever draw to 90x100). Clear here so the undrawn portion will have
235 // a consistent color
236 canvas->clear(SK_ColorTRANSPARENT);
scroggo@google.com82ec0b02012-12-17 19:25:54 +0000237 return canvas;
238}
keyar@chromium.orga474ce32012-08-20 15:03:57 +0000239
scroggo@google.com82ec0b02012-12-17 19:25:54 +0000240void PictureRenderer::scaleToScaleFactor(SkCanvas* canvas) {
241 SkASSERT(canvas != NULL);
242 if (fScaleFactor != SK_Scalar1) {
243 canvas->scale(fScaleFactor, fScaleFactor);
244 }
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000245}
246
247void PictureRenderer::end() {
scroggo@google.com08085f82013-01-28 20:40:24 +0000248 this->resetState(true);
junov@chromium.org9313ca42012-11-02 18:11:49 +0000249 SkSafeUnref(fPicture);
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000250 fPicture = NULL;
251 fCanvas.reset(NULL);
252}
253
scroggo@google.comc0d5e542012-12-13 21:40:48 +0000254int PictureRenderer::getViewWidth() {
255 SkASSERT(fPicture != NULL);
robertphillips@google.com8ac811e2013-02-07 00:13:34 +0000256 int width = SkScalarCeilToInt(fPicture->width() * fScaleFactor);
scroggo@google.comc0d5e542012-12-13 21:40:48 +0000257 if (fViewport.width() > 0) {
258 width = SkMin32(width, fViewport.width());
259 }
260 return width;
261}
262
263int PictureRenderer::getViewHeight() {
264 SkASSERT(fPicture != NULL);
robertphillips@google.com8ac811e2013-02-07 00:13:34 +0000265 int height = SkScalarCeilToInt(fPicture->height() * fScaleFactor);
scroggo@google.comc0d5e542012-12-13 21:40:48 +0000266 if (fViewport.height() > 0) {
267 height = SkMin32(height, fViewport.height());
268 }
269 return height;
270}
271
junov@chromium.org9313ca42012-11-02 18:11:49 +0000272/** Converts fPicture to a picture that uses a BBoxHierarchy.
273 * PictureRenderer subclasses that are used to test picture playback
274 * should call this method during init.
275 */
276void PictureRenderer::buildBBoxHierarchy() {
277 SkASSERT(NULL != fPicture);
278 if (kNone_BBoxHierarchyType != fBBoxHierarchyType && NULL != fPicture) {
279 SkPicture* newPicture = this->createPicture();
280 SkCanvas* recorder = newPicture->beginRecording(fPicture->width(), fPicture->height(),
281 this->recordFlags());
282 fPicture->draw(recorder);
283 newPicture->endRecording();
284 fPicture->unref();
285 fPicture = newPicture;
286 }
287}
288
scroggo@google.com08085f82013-01-28 20:40:24 +0000289void PictureRenderer::resetState(bool callFinish) {
keyar@chromium.org28136b32012-08-20 15:04:15 +0000290#if SK_SUPPORT_GPU
robertphillips@google.com6177e692013-02-28 20:16:25 +0000291 SkGLContextHelper* glContext = this->getGLContext();
scroggo@google.com0556ea02013-02-08 19:38:21 +0000292 if (NULL == glContext) {
293 SkASSERT(kBitmap_DeviceType == fDeviceType);
294 return;
295 }
keyar@chromium.org28136b32012-08-20 15:04:15 +0000296
scroggo@google.com0556ea02013-02-08 19:38:21 +0000297 fGrContext->flush();
commit-bot@chromium.org51c040e2014-03-11 22:58:00 +0000298 glContext->swapBuffers();
scroggo@google.com0556ea02013-02-08 19:38:21 +0000299 if (callFinish) {
300 SK_GL(*glContext, Finish());
keyar@chromium.org77a55222012-08-20 15:03:47 +0000301 }
keyar@chromium.orga40c20d2012-08-20 15:04:12 +0000302#endif
keyar@chromium.org77a55222012-08-20 15:03:47 +0000303}
304
robertphillips@google.com94d8f1e2013-12-18 17:25:33 +0000305void PictureRenderer::purgeTextures() {
306 SkDiscardableMemoryPool* pool = SkGetGlobalDiscardableMemoryPool();
307
308 pool->dumpPool();
309
310#if SK_SUPPORT_GPU
311 SkGLContextHelper* glContext = this->getGLContext();
312 if (NULL == glContext) {
313 SkASSERT(kBitmap_DeviceType == fDeviceType);
314 return;
315 }
316
317 // resetState should've already done this
318 fGrContext->flush();
319
320 fGrContext->purgeAllUnlockedResources();
321#endif
322}
323
junov@chromium.org9313ca42012-11-02 18:11:49 +0000324uint32_t PictureRenderer::recordFlags() {
junov@chromium.org100b1c52013-01-16 20:12:22 +0000325 return ((kNone_BBoxHierarchyType == fBBoxHierarchyType) ? 0 :
326 SkPicture::kOptimizeForClippedPlayback_RecordingFlag) |
327 SkPicture::kUsePathBoundsForClip_RecordingFlag;
junov@chromium.org9313ca42012-11-02 18:11:49 +0000328}
329
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000330/**
331 * Write the canvas to the specified path.
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000332 *
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000333 * @param canvas Must be non-null. Canvas to be written to a file.
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000334 * @param outputDir If nonempty, write the binary image to a file within this directory.
335 * @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 +0000336 * @param jsonSummaryPtr If not null, add image results to this summary.
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000337 * @param useChecksumBasedFilenames If true, use checksum-based filenames when writing to disk.
commit-bot@chromium.org24c568c2014-04-10 15:39:02 +0000338 * @param tileNumberPtr If not null, which tile number this image contains.
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000339 * @return bool True if the Canvas is written to a file.
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +0000340 *
341 * TODO(epoger): Right now, all canvases must pass through this function in order to be appended
342 * to the ImageResultsSummary. We need some way to add bitmaps to the ImageResultsSummary
343 * even if --writePath has not been specified (and thus this function is not called).
344 *
345 * One fix would be to pass in these path elements separately, and allow this function to be
346 * called even if --writePath was not specified...
347 * const char *outputDir // NULL if we don't want to write image files to disk
348 * const char *filename // name we use within JSON summary, and as the filename within outputDir
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000349 *
350 * UPDATE: Now that outputDir and inputFilename are passed separately, we should be able to do that.
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000351 */
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000352static bool write(SkCanvas* canvas, const SkString& outputDir, const SkString& inputFilename,
353 ImageResultsSummary *jsonSummaryPtr, bool useChecksumBasedFilenames,
commit-bot@chromium.org24c568c2014-04-10 15:39:02 +0000354 const int* tileNumberPtr=NULL) {
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000355 SkASSERT(canvas != NULL);
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000356 if (NULL == canvas) {
keyar@chromium.org9299ede2012-08-21 19:05:08 +0000357 return false;
358 }
359
360 SkBitmap bitmap;
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000361 SkISize size = canvas->getDeviceSize();
362 sk_tools::setup_bitmap(&bitmap, size.width(), size.height());
keyar@chromium.org9299ede2012-08-21 19:05:08 +0000363
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000364 // Make sure we only compute the bitmap hash once (at most).
365 uint64_t hash;
366 bool generatedHash = false;
367
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000368 canvas->readPixels(&bitmap, 0, 0);
keyar@chromium.org9299ede2012-08-21 19:05:08 +0000369 sk_tools::force_all_opaque(bitmap);
370
commit-bot@chromium.org24c568c2014-04-10 15:39:02 +0000371 SkString escapedInputFilename(inputFilename);
372 replace_char(&escapedInputFilename, '.', '_');
373
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000374 // TODO(epoger): what about including the config type within outputFilename? That way,
375 // we could combine results of different config types without conflicting filenames.
commit-bot@chromium.org24c568c2014-04-10 15:39:02 +0000376 SkString outputFilename;
377 const char *outputSubdirPtr = NULL;
378 if (useChecksumBasedFilenames) {
commit-bot@chromium.orgb470c212014-03-19 22:58:52 +0000379 SkASSERT(!generatedHash);
380 SkAssertResult(SkBitmapHasher::ComputeDigest(bitmap, &hash));
381 generatedHash = true;
382
commit-bot@chromium.org24c568c2014-04-10 15:39:02 +0000383 outputSubdirPtr = escapedInputFilename.c_str();
384 outputFilename.set(kJsonValue_Image_ChecksumAlgorithm_Bitmap64bitMD5);
385 outputFilename.append("_");
386 outputFilename.appendU64(hash);
387 } else {
388 outputFilename.set(escapedInputFilename);
389 if (NULL != tileNumberPtr) {
390 outputFilename.append("-tile");
391 outputFilename.appendS32(*tileNumberPtr);
392 }
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +0000393 }
commit-bot@chromium.org24c568c2014-04-10 15:39:02 +0000394 outputFilename.append(".png");
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +0000395
commit-bot@chromium.org24c568c2014-04-10 15:39:02 +0000396 if (NULL != jsonSummaryPtr) {
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000397 if (!generatedHash) {
398 SkAssertResult(SkBitmapHasher::ComputeDigest(bitmap, &hash));
399 generatedHash = true;
400 }
commit-bot@chromium.org24c568c2014-04-10 15:39:02 +0000401
402 SkString outputRelativePath;
403 if (outputSubdirPtr) {
404 outputRelativePath.set(outputSubdirPtr);
405 outputRelativePath.append("/"); // always use "/", even on Windows
406 outputRelativePath.append(outputFilename);
407 } else {
408 outputRelativePath.set(outputFilename);
409 }
410
411 jsonSummaryPtr->add(inputFilename.c_str(), outputRelativePath.c_str(),
412 hash, tileNumberPtr);
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000413 }
414
415 SkASSERT(!outputDir.isEmpty()); // TODO(epoger): we want to remove this constraint,
416 // as noted above
commit-bot@chromium.org24c568c2014-04-10 15:39:02 +0000417 SkString dirPath;
418 if (outputSubdirPtr) {
419 dirPath = SkOSPath::SkPathJoin(outputDir.c_str(), outputSubdirPtr);
420 sk_mkdir(dirPath.c_str());
421 } else {
422 dirPath.set(outputDir);
423 }
424 SkString fullPath = SkOSPath::SkPathJoin(dirPath.c_str(), outputFilename.c_str());
425 return SkImageEncoder::EncodeFile(fullPath.c_str(), bitmap, SkImageEncoder::kPNG_Type, 100);
keyar@chromium.org9299ede2012-08-21 19:05:08 +0000426}
427
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000428///////////////////////////////////////////////////////////////////////////////////////////////
429
djsollen@google.comfd9720c2012-11-06 16:54:40 +0000430SkCanvas* RecordPictureRenderer::setupCanvas(int width, int height) {
431 // defer the canvas setup until the render step
432 return NULL;
433}
434
reed@google.com672588b2014-01-08 15:42:01 +0000435// the size_t* parameter is deprecated, so we ignore it
436static SkData* encode_bitmap_to_data(size_t*, const SkBitmap& bm) {
scroggo@google.com1b1bcc32013-05-21 20:31:23 +0000437 return SkImageEncoder::EncodeData(bm, SkImageEncoder::kPNG_Type, 100);
scroggo@google.coma9e3a362012-11-07 17:52:48 +0000438}
439
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000440bool RecordPictureRenderer::render(SkBitmap** out) {
junov@chromium.org9313ca42012-11-02 18:11:49 +0000441 SkAutoTUnref<SkPicture> replayer(this->createPicture());
scroggo@google.com82ec0b02012-12-17 19:25:54 +0000442 SkCanvas* recorder = replayer->beginRecording(this->getViewWidth(), this->getViewHeight(),
junov@chromium.org9313ca42012-11-02 18:11:49 +0000443 this->recordFlags());
scroggo@google.com82ec0b02012-12-17 19:25:54 +0000444 this->scaleToScaleFactor(recorder);
scroggo@google.com9a412522012-09-07 15:21:18 +0000445 fPicture->draw(recorder);
junov@chromium.org9313ca42012-11-02 18:11:49 +0000446 replayer->endRecording();
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000447 if (!fOutputDir.isEmpty()) {
scroggo@google.coma9e3a362012-11-07 17:52:48 +0000448 // Record the new picture as a new SKP with PNG encoded bitmaps.
commit-bot@chromium.org24c568c2014-04-10 15:39:02 +0000449 SkString skpPath = SkOSPath::SkPathJoin(fOutputDir.c_str(), fInputFilename.c_str());
scroggo@google.coma9e3a362012-11-07 17:52:48 +0000450 SkFILEWStream stream(skpPath.c_str());
scroggo@google.com1b1bcc32013-05-21 20:31:23 +0000451 replayer->serialize(&stream, &encode_bitmap_to_data);
scroggo@google.coma9e3a362012-11-07 17:52:48 +0000452 return true;
453 }
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000454 return false;
scroggo@google.com9a412522012-09-07 15:21:18 +0000455}
456
scroggo@google.com0a049b82012-11-02 22:01:26 +0000457SkString RecordPictureRenderer::getConfigNameInternal() {
458 return SkString("record");
459}
460
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000461///////////////////////////////////////////////////////////////////////////////////////////////
462
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000463bool PipePictureRenderer::render(SkBitmap** out) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000464 SkASSERT(fCanvas.get() != NULL);
465 SkASSERT(fPicture != NULL);
keyar@chromium.org78a35c52012-08-20 15:03:44 +0000466 if (NULL == fCanvas.get() || NULL == fPicture) {
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000467 return false;
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000468 }
469
470 PipeController pipeController(fCanvas.get());
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000471 SkGPipeWriter writer;
472 SkCanvas* pipeCanvas = writer.startRecording(&pipeController);
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000473 pipeCanvas->drawPicture(*fPicture);
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000474 writer.endRecording();
scroggo@google.com9a412522012-09-07 15:21:18 +0000475 fCanvas->flush();
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000476 if (!fOutputDir.isEmpty()) {
477 return write(fCanvas, fOutputDir, fInputFilename, fJsonSummaryPtr,
478 fUseChecksumBasedFilenames);
borenet@google.com070d3542012-10-26 13:26:55 +0000479 }
edisonn@google.com84f548c2012-12-18 22:24:03 +0000480 if (NULL != out) {
481 *out = SkNEW(SkBitmap);
482 setup_bitmap(*out, fPicture->width(), fPicture->height());
483 fCanvas->readPixels(*out, 0, 0);
skia.committer@gmail.coma7d8e3e2012-12-19 02:01:38 +0000484 }
borenet@google.com070d3542012-10-26 13:26:55 +0000485 return true;
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000486}
487
scroggo@google.com0a049b82012-11-02 22:01:26 +0000488SkString PipePictureRenderer::getConfigNameInternal() {
489 return SkString("pipe");
490}
491
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000492///////////////////////////////////////////////////////////////////////////////////////////////
493
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000494void SimplePictureRenderer::init(SkPicture* picture, const SkString* outputDir,
495 const SkString* inputFilename, bool useChecksumBasedFilenames) {
496 INHERITED::init(picture, outputDir, inputFilename, useChecksumBasedFilenames);
junov@chromium.org9313ca42012-11-02 18:11:49 +0000497 this->buildBBoxHierarchy();
498}
499
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000500bool SimplePictureRenderer::render(SkBitmap** out) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000501 SkASSERT(fCanvas.get() != NULL);
502 SkASSERT(fPicture != NULL);
keyar@chromium.org78a35c52012-08-20 15:03:44 +0000503 if (NULL == fCanvas.get() || NULL == fPicture) {
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000504 return false;
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000505 }
506
507 fCanvas->drawPicture(*fPicture);
scroggo@google.com9a412522012-09-07 15:21:18 +0000508 fCanvas->flush();
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000509 if (!fOutputDir.isEmpty()) {
510 return write(fCanvas, fOutputDir, fInputFilename, fJsonSummaryPtr,
511 fUseChecksumBasedFilenames);
borenet@google.com070d3542012-10-26 13:26:55 +0000512 }
skia.committer@gmail.coma7d8e3e2012-12-19 02:01:38 +0000513
edisonn@google.com84f548c2012-12-18 22:24:03 +0000514 if (NULL != out) {
515 *out = SkNEW(SkBitmap);
516 setup_bitmap(*out, fPicture->width(), fPicture->height());
517 fCanvas->readPixels(*out, 0, 0);
518 }
519
borenet@google.com070d3542012-10-26 13:26:55 +0000520 return true;
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000521}
522
scroggo@google.com0a049b82012-11-02 22:01:26 +0000523SkString SimplePictureRenderer::getConfigNameInternal() {
524 return SkString("simple");
525}
526
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000527///////////////////////////////////////////////////////////////////////////////////////////////
528
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000529TiledPictureRenderer::TiledPictureRenderer()
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000530 : fTileWidth(kDefaultTileWidth)
rileya@google.comb947b912012-08-29 17:35:07 +0000531 , fTileHeight(kDefaultTileHeight)
rileya@google.coma04dc022012-09-10 19:01:38 +0000532 , fTileWidthPercentage(0.0)
rileya@google.comb947b912012-08-29 17:35:07 +0000533 , fTileHeightPercentage(0.0)
scroggo@google.comcbcef702012-12-13 22:09:28 +0000534 , fTileMinPowerOf2Width(0)
535 , fCurrentTileOffset(-1)
536 , fTilesX(0)
537 , fTilesY(0) { }
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000538
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000539void TiledPictureRenderer::init(SkPicture* pict, const SkString* outputDir,
540 const SkString* inputFilename, bool useChecksumBasedFilenames) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000541 SkASSERT(pict != NULL);
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000542 SkASSERT(0 == fTileRects.count());
543 if (NULL == pict || fTileRects.count() != 0) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000544 return;
545 }
546
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000547 // Do not call INHERITED::init(), which would create a (potentially large) canvas which is not
548 // used by bench_pictures.
commit-bot@chromium.org8ddc26b2014-03-31 17:55:12 +0000549 fPicture = SkRef(pict);
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000550 this->CopyString(&fOutputDir, outputDir);
551 this->CopyString(&fInputFilename, inputFilename);
552 fUseChecksumBasedFilenames = useChecksumBasedFilenames;
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000553 this->buildBBoxHierarchy();
keyar@chromium.orgcc6e5ef2012-07-27 20:09:26 +0000554
555 if (fTileWidthPercentage > 0) {
robertphillips@google.com5d8d1862012-08-15 14:36:41 +0000556 fTileWidth = sk_float_ceil2int(float(fTileWidthPercentage * fPicture->width() / 100));
keyar@chromium.orgcc6e5ef2012-07-27 20:09:26 +0000557 }
558 if (fTileHeightPercentage > 0) {
robertphillips@google.com5d8d1862012-08-15 14:36:41 +0000559 fTileHeight = sk_float_ceil2int(float(fTileHeightPercentage * fPicture->height() / 100));
keyar@chromium.orgcc6e5ef2012-07-27 20:09:26 +0000560 }
561
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000562 if (fTileMinPowerOf2Width > 0) {
563 this->setupPowerOf2Tiles();
564 } else {
565 this->setupTiles();
566 }
scroggo@google.comcbcef702012-12-13 22:09:28 +0000567 fCanvas.reset(this->setupCanvas(fTileWidth, fTileHeight));
568 // Initialize to -1 so that the first call to nextTile will set this up to draw tile 0 on the
569 // first call to drawCurrentTile.
570 fCurrentTileOffset = -1;
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000571}
572
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000573void TiledPictureRenderer::end() {
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000574 fTileRects.reset();
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000575 this->INHERITED::end();
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000576}
577
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000578void TiledPictureRenderer::setupTiles() {
scroggo@google.comc0d5e542012-12-13 21:40:48 +0000579 // Only use enough tiles to cover the viewport
580 const int width = this->getViewWidth();
581 const int height = this->getViewHeight();
582
scroggo@google.comcbcef702012-12-13 22:09:28 +0000583 fTilesX = fTilesY = 0;
scroggo@google.comc0d5e542012-12-13 21:40:48 +0000584 for (int tile_y_start = 0; tile_y_start < height; tile_y_start += fTileHeight) {
scroggo@google.comcbcef702012-12-13 22:09:28 +0000585 fTilesY++;
scroggo@google.comc0d5e542012-12-13 21:40:48 +0000586 for (int tile_x_start = 0; tile_x_start < width; tile_x_start += fTileWidth) {
scroggo@google.comcbcef702012-12-13 22:09:28 +0000587 if (0 == tile_y_start) {
588 // Only count tiles in the X direction on the first pass.
589 fTilesX++;
590 }
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000591 *fTileRects.append() = SkRect::MakeXYWH(SkIntToScalar(tile_x_start),
592 SkIntToScalar(tile_y_start),
593 SkIntToScalar(fTileWidth),
594 SkIntToScalar(fTileHeight));
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000595 }
596 }
597}
598
scroggo@google.comcbcef702012-12-13 22:09:28 +0000599bool TiledPictureRenderer::tileDimensions(int &x, int &y) {
600 if (fTileRects.count() == 0 || NULL == fPicture) {
601 return false;
602 }
603 x = fTilesX;
604 y = fTilesY;
605 return true;
606}
607
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000608// The goal of the powers of two tiles is to minimize the amount of wasted tile
609// space in the width-wise direction and then minimize the number of tiles. The
610// constraints are that every tile must have a pixel width that is a power of
611// two and also be of some minimal width (that is also a power of two).
612//
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000613// This is solved by first taking our picture size and rounding it up to the
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000614// multiple of the minimal width. The binary representation of this rounded
615// value gives us the tiles we need: a bit of value one means we need a tile of
616// that size.
617void TiledPictureRenderer::setupPowerOf2Tiles() {
scroggo@google.comc0d5e542012-12-13 21:40:48 +0000618 // Only use enough tiles to cover the viewport
619 const int width = this->getViewWidth();
620 const int height = this->getViewHeight();
621
622 int rounded_value = width;
623 if (width % fTileMinPowerOf2Width != 0) {
624 rounded_value = width - (width % fTileMinPowerOf2Width) + fTileMinPowerOf2Width;
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000625 }
626
reed@google.come15b2f52013-12-18 04:59:26 +0000627 int num_bits = SkScalarCeilToInt(scalar_log2(SkIntToScalar(width)));
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000628 int largest_possible_tile_size = 1 << num_bits;
629
scroggo@google.comcbcef702012-12-13 22:09:28 +0000630 fTilesX = fTilesY = 0;
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000631 // The tile height is constant for a particular picture.
scroggo@google.comc0d5e542012-12-13 21:40:48 +0000632 for (int tile_y_start = 0; tile_y_start < height; tile_y_start += fTileHeight) {
scroggo@google.comcbcef702012-12-13 22:09:28 +0000633 fTilesY++;
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000634 int tile_x_start = 0;
635 int current_width = largest_possible_tile_size;
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000636 // Set fTileWidth to be the width of the widest tile, so that each canvas is large enough
637 // to draw each tile.
638 fTileWidth = current_width;
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000639
640 while (current_width >= fTileMinPowerOf2Width) {
641 // It is very important this is a bitwise AND.
642 if (current_width & rounded_value) {
scroggo@google.comcbcef702012-12-13 22:09:28 +0000643 if (0 == tile_y_start) {
644 // Only count tiles in the X direction on the first pass.
645 fTilesX++;
646 }
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000647 *fTileRects.append() = SkRect::MakeXYWH(SkIntToScalar(tile_x_start),
648 SkIntToScalar(tile_y_start),
649 SkIntToScalar(current_width),
650 SkIntToScalar(fTileHeight));
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000651 tile_x_start += current_width;
652 }
653
654 current_width >>= 1;
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000655 }
656 }
657}
658
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000659/**
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +0000660 * Draw the specified picture to the canvas translated to rectangle provided, so that this mini
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000661 * canvas represents the rectangle's portion of the overall picture.
662 * Saves and restores so that the initial clip and matrix return to their state before this function
663 * is called.
664 */
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +0000665static void draw_tile_to_canvas(SkCanvas* canvas, const SkRect& tileRect, SkPicture* picture) {
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000666 int saveCount = canvas->save();
scroggo@google.com82ec0b02012-12-17 19:25:54 +0000667 // Translate so that we draw the correct portion of the picture.
668 // Perform a postTranslate so that the scaleFactor does not interfere with the positioning.
669 SkMatrix mat(canvas->getTotalMatrix());
670 mat.postTranslate(-tileRect.fLeft, -tileRect.fTop);
671 canvas->setMatrix(mat);
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +0000672 canvas->drawPicture(*picture);
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000673 canvas->restoreToCount(saveCount);
674 canvas->flush();
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000675}
676
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000677///////////////////////////////////////////////////////////////////////////////////////////////
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000678
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +0000679/**
680 * Copies the entirety of the src bitmap (typically a tile) into a portion of the dst bitmap.
681 * If the src bitmap is too large to fit within the dst bitmap after the x and y
682 * offsets have been applied, any excess will be ignored (so only the top-left portion of the
683 * src bitmap will be copied).
684 *
685 * @param src source bitmap
686 * @param dst destination bitmap
687 * @param xOffset x-offset within destination bitmap
688 * @param yOffset y-offset within destination bitmap
689 */
690static void bitmapCopyAtOffset(const SkBitmap& src, SkBitmap* dst,
691 int xOffset, int yOffset) {
692 for (int y = 0; y <src.height() && y + yOffset < dst->height() ; y++) {
693 for (int x = 0; x < src.width() && x + xOffset < dst->width() ; x++) {
694 *dst->getAddr32(xOffset + x, yOffset + y) = *src.getAddr32(x, y);
edisonn@google.com84f548c2012-12-18 22:24:03 +0000695 }
696 }
697}
698
scroggo@google.comcbcef702012-12-13 22:09:28 +0000699bool TiledPictureRenderer::nextTile(int &i, int &j) {
700 if (++fCurrentTileOffset < fTileRects.count()) {
701 i = fCurrentTileOffset % fTilesX;
702 j = fCurrentTileOffset / fTilesX;
703 return true;
704 }
705 return false;
706}
707
708void TiledPictureRenderer::drawCurrentTile() {
709 SkASSERT(fCurrentTileOffset >= 0 && fCurrentTileOffset < fTileRects.count());
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +0000710 draw_tile_to_canvas(fCanvas, fTileRects[fCurrentTileOffset], fPicture);
scroggo@google.comcbcef702012-12-13 22:09:28 +0000711}
712
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000713bool TiledPictureRenderer::render(SkBitmap** out) {
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000714 SkASSERT(fPicture != NULL);
715 if (NULL == fPicture) {
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000716 return false;
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000717 }
718
edisonn@google.com84f548c2012-12-18 22:24:03 +0000719 SkBitmap bitmap;
720 if (out){
721 *out = SkNEW(SkBitmap);
722 setup_bitmap(*out, fPicture->width(), fPicture->height());
723 setup_bitmap(&bitmap, fTileWidth, fTileHeight);
724 }
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000725 bool success = true;
726 for (int i = 0; i < fTileRects.count(); ++i) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +0000727 draw_tile_to_canvas(fCanvas, fTileRects[i], fPicture);
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000728 if (!fOutputDir.isEmpty()) {
729 success &= write(fCanvas, fOutputDir, fInputFilename, fJsonSummaryPtr,
730 fUseChecksumBasedFilenames, &i);
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000731 }
edisonn@google.com84f548c2012-12-18 22:24:03 +0000732 if (NULL != out) {
733 if (fCanvas->readPixels(&bitmap, 0, 0)) {
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +0000734 // Add this tile to the entire bitmap.
735 bitmapCopyAtOffset(bitmap, *out, SkScalarFloorToInt(fTileRects[i].left()),
736 SkScalarFloorToInt(fTileRects[i].top()));
edisonn@google.com84f548c2012-12-18 22:24:03 +0000737 } else {
738 success = false;
739 }
740 }
keyar@chromium.org163b5672012-08-01 17:53:29 +0000741 }
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000742 return success;
keyar@chromium.org163b5672012-08-01 17:53:29 +0000743}
744
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000745SkCanvas* TiledPictureRenderer::setupCanvas(int width, int height) {
746 SkCanvas* canvas = this->INHERITED::setupCanvas(width, height);
commit-bot@chromium.org8ddc26b2014-03-31 17:55:12 +0000747 SkASSERT(NULL != fPicture);
scroggo@google.com82ec0b02012-12-17 19:25:54 +0000748 // Clip the tile to an area that is completely inside both the SkPicture and the viewport. This
749 // is mostly important for tiles on the right and bottom edges as they may go over this area and
750 // the picture may have some commands that draw outside of this area and so should not actually
751 // be written.
752 // Uses a clipRegion so that it will be unaffected by the scale factor, which may have been set
753 // by INHERITED::setupCanvas.
754 SkRegion clipRegion;
755 clipRegion.setRect(0, 0, this->getViewWidth(), this->getViewHeight());
756 canvas->clipRegion(clipRegion);
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000757 return canvas;
758}
scroggo@google.com0a049b82012-11-02 22:01:26 +0000759
760SkString TiledPictureRenderer::getConfigNameInternal() {
761 SkString name;
762 if (fTileMinPowerOf2Width > 0) {
763 name.append("pow2tile_");
764 name.appendf("%i", fTileMinPowerOf2Width);
765 } else {
766 name.append("tile_");
767 if (fTileWidthPercentage > 0) {
768 name.appendf("%.f%%", fTileWidthPercentage);
769 } else {
770 name.appendf("%i", fTileWidth);
771 }
772 }
773 name.append("x");
774 if (fTileHeightPercentage > 0) {
775 name.appendf("%.f%%", fTileHeightPercentage);
776 } else {
777 name.appendf("%i", fTileHeight);
778 }
779 return name;
780}
781
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000782///////////////////////////////////////////////////////////////////////////////////////////////
783
784// Holds all of the information needed to draw a set of tiles.
785class CloneData : public SkRunnable {
786
787public:
788 CloneData(SkPicture* clone, SkCanvas* canvas, SkTDArray<SkRect>& rects, int start, int end,
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000789 SkRunnable* done, ImageResultsSummary* jsonSummaryPtr, bool useChecksumBasedFilenames)
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000790 : fClone(clone)
791 , fCanvas(canvas)
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000792 , fRects(rects)
793 , fStart(start)
794 , fEnd(end)
795 , fSuccess(NULL)
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +0000796 , fDone(done)
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000797 , fJsonSummaryPtr(jsonSummaryPtr)
798 , fUseChecksumBasedFilenames(useChecksumBasedFilenames) {
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000799 SkASSERT(fDone != NULL);
800 }
801
802 virtual void run() SK_OVERRIDE {
803 SkGraphics::SetTLSFontCacheLimit(1024 * 1024);
edisonn@google.com84f548c2012-12-18 22:24:03 +0000804
805 SkBitmap bitmap;
806 if (fBitmap != NULL) {
807 // All tiles are the same size.
jvanverth@google.com9c4e5ac2013-01-07 18:41:28 +0000808 setup_bitmap(&bitmap, SkScalarFloorToInt(fRects[0].width()), SkScalarFloorToInt(fRects[0].height()));
edisonn@google.com84f548c2012-12-18 22:24:03 +0000809 }
skia.committer@gmail.coma7d8e3e2012-12-19 02:01:38 +0000810
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000811 for (int i = fStart; i < fEnd; i++) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +0000812 draw_tile_to_canvas(fCanvas, fRects[i], fClone);
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000813 if ((!fOutputDir.isEmpty())
814 && !write(fCanvas, fOutputDir, fInputFilename, fJsonSummaryPtr,
815 fUseChecksumBasedFilenames, &i)
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000816 && fSuccess != NULL) {
817 *fSuccess = false;
818 // If one tile fails to write to a file, do not continue drawing the rest.
819 break;
820 }
edisonn@google.com84f548c2012-12-18 22:24:03 +0000821 if (fBitmap != NULL) {
822 if (fCanvas->readPixels(&bitmap, 0, 0)) {
823 SkAutoLockPixels alp(*fBitmap);
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +0000824 bitmapCopyAtOffset(bitmap, fBitmap, SkScalarFloorToInt(fRects[i].left()),
825 SkScalarFloorToInt(fRects[i].top()));
edisonn@google.com84f548c2012-12-18 22:24:03 +0000826 } else {
827 *fSuccess = false;
828 // If one tile fails to read pixels, do not continue drawing the rest.
829 break;
830 }
831 }
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000832 }
833 fDone->run();
834 }
835
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000836 void setPathsAndSuccess(const SkString& outputDir, const SkString& inputFilename,
837 bool* success) {
838 fOutputDir.set(outputDir);
839 fInputFilename.set(inputFilename);
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000840 fSuccess = success;
841 }
842
edisonn@google.com84f548c2012-12-18 22:24:03 +0000843 void setBitmap(SkBitmap* bitmap) {
844 fBitmap = bitmap;
845 }
846
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000847private:
848 // All pointers unowned.
849 SkPicture* fClone; // Picture to draw from. Each CloneData has a unique one which
850 // is threadsafe.
851 SkCanvas* fCanvas; // Canvas to draw to. Reused for each tile.
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000852 SkString fOutputDir; // If not empty, write results into this directory.
853 SkString fInputFilename; // Filename of input SkPicture file.
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000854 SkTDArray<SkRect>& fRects; // All tiles of the picture.
855 const int fStart; // Range of tiles drawn by this thread.
856 const int fEnd;
857 bool* fSuccess; // Only meaningful if path is non-null. Shared by all threads,
858 // and only set to false upon failure to write to a PNG.
859 SkRunnable* fDone;
edisonn@google.com84f548c2012-12-18 22:24:03 +0000860 SkBitmap* fBitmap;
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +0000861 ImageResultsSummary* fJsonSummaryPtr;
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000862 bool fUseChecksumBasedFilenames;
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000863};
864
865MultiCorePictureRenderer::MultiCorePictureRenderer(int threadCount)
866: fNumThreads(threadCount)
867, fThreadPool(threadCount)
868, fCountdown(threadCount) {
869 // Only need to create fNumThreads - 1 clones, since one thread will use the base
870 // picture.
871 fPictureClones = SkNEW_ARRAY(SkPicture, fNumThreads - 1);
872 fCloneData = SkNEW_ARRAY(CloneData*, fNumThreads);
873}
874
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000875void MultiCorePictureRenderer::init(SkPicture *pict, const SkString* outputDir,
876 const SkString* inputFilename, bool useChecksumBasedFilenames) {
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000877 // Set fPicture and the tiles.
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000878 this->INHERITED::init(pict, outputDir, inputFilename, useChecksumBasedFilenames);
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000879 for (int i = 0; i < fNumThreads; ++i) {
880 *fCanvasPool.append() = this->setupCanvas(this->getTileWidth(), this->getTileHeight());
881 }
882 // Only need to create fNumThreads - 1 clones, since one thread will use the base picture.
883 fPicture->clone(fPictureClones, fNumThreads - 1);
884 // Populate each thread with the appropriate data.
885 // Group the tiles into nearly equal size chunks, rounding up so we're sure to cover them all.
886 const int chunkSize = (fTileRects.count() + fNumThreads - 1) / fNumThreads;
887
888 for (int i = 0; i < fNumThreads; i++) {
889 SkPicture* pic;
890 if (i == fNumThreads-1) {
891 // The last set will use the original SkPicture.
892 pic = fPicture;
893 } else {
894 pic = &fPictureClones[i];
895 }
896 const int start = i * chunkSize;
897 const int end = SkMin32(start + chunkSize, fTileRects.count());
898 fCloneData[i] = SkNEW_ARGS(CloneData,
commit-bot@chromium.orga3f882c2013-12-13 20:52:36 +0000899 (pic, fCanvasPool[i], fTileRects, start, end, &fCountdown,
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000900 fJsonSummaryPtr, useChecksumBasedFilenames));
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000901 }
902}
903
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000904bool MultiCorePictureRenderer::render(SkBitmap** out) {
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000905 bool success = true;
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000906 if (!fOutputDir.isEmpty()) {
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000907 for (int i = 0; i < fNumThreads-1; i++) {
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000908 fCloneData[i]->setPathsAndSuccess(fOutputDir, fInputFilename, &success);
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000909 }
910 }
911
edisonn@google.com84f548c2012-12-18 22:24:03 +0000912 if (NULL != out) {
913 *out = SkNEW(SkBitmap);
914 setup_bitmap(*out, fPicture->width(), fPicture->height());
915 for (int i = 0; i < fNumThreads; i++) {
916 fCloneData[i]->setBitmap(*out);
917 }
918 } else {
919 for (int i = 0; i < fNumThreads; i++) {
920 fCloneData[i]->setBitmap(NULL);
921 }
922 }
923
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000924 fCountdown.reset(fNumThreads);
925 for (int i = 0; i < fNumThreads; i++) {
926 fThreadPool.add(fCloneData[i]);
927 }
928 fCountdown.wait();
929
930 return success;
931}
932
933void MultiCorePictureRenderer::end() {
934 for (int i = 0; i < fNumThreads - 1; i++) {
935 SkDELETE(fCloneData[i]);
936 fCloneData[i] = NULL;
937 }
938
939 fCanvasPool.unrefAll();
940
941 this->INHERITED::end();
942}
943
944MultiCorePictureRenderer::~MultiCorePictureRenderer() {
945 // Each individual CloneData was deleted in end.
946 SkDELETE_ARRAY(fCloneData);
947 SkDELETE_ARRAY(fPictureClones);
948}
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000949
scroggo@google.com0a049b82012-11-02 22:01:26 +0000950SkString MultiCorePictureRenderer::getConfigNameInternal() {
951 SkString name = this->INHERITED::getConfigNameInternal();
952 name.appendf("_multi_%i_threads", fNumThreads);
953 return name;
954}
955
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000956///////////////////////////////////////////////////////////////////////////////////////////////
scroggo@google.com9a412522012-09-07 15:21:18 +0000957
958void PlaybackCreationRenderer::setup() {
junov@chromium.org9313ca42012-11-02 18:11:49 +0000959 fReplayer.reset(this->createPicture());
scroggo@google.com82ec0b02012-12-17 19:25:54 +0000960 SkCanvas* recorder = fReplayer->beginRecording(this->getViewWidth(), this->getViewHeight(),
junov@chromium.org9313ca42012-11-02 18:11:49 +0000961 this->recordFlags());
scroggo@google.com82ec0b02012-12-17 19:25:54 +0000962 this->scaleToScaleFactor(recorder);
commit-bot@chromium.org8ddc26b2014-03-31 17:55:12 +0000963 recorder->drawPicture(*fPicture);
scroggo@google.com9a412522012-09-07 15:21:18 +0000964}
965
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +0000966bool PlaybackCreationRenderer::render(SkBitmap** out) {
junov@chromium.org9313ca42012-11-02 18:11:49 +0000967 fReplayer->endRecording();
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000968 // Since this class does not actually render, return false.
969 return false;
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000970}
971
scroggo@google.com0a049b82012-11-02 22:01:26 +0000972SkString PlaybackCreationRenderer::getConfigNameInternal() {
973 return SkString("playback_creation");
974}
975
junov@chromium.org9313ca42012-11-02 18:11:49 +0000976///////////////////////////////////////////////////////////////////////////////////////////////
977// SkPicture variants for each BBoxHierarchy type
978
979class RTreePicture : public SkPicture {
980public:
981 virtual SkBBoxHierarchy* createBBoxHierarchy() const SK_OVERRIDE{
982 static const int kRTreeMinChildren = 6;
983 static const int kRTreeMaxChildren = 11;
984 SkScalar aspectRatio = SkScalarDiv(SkIntToScalar(fWidth),
985 SkIntToScalar(fHeight));
sglez@google.com8c902122013-08-30 17:27:47 +0000986 bool sortDraws = false;
junov@chromium.org9313ca42012-11-02 18:11:49 +0000987 return SkRTree::Create(kRTreeMinChildren, kRTreeMaxChildren,
sglez@google.com8c902122013-08-30 17:27:47 +0000988 aspectRatio, sortDraws);
junov@chromium.org9313ca42012-11-02 18:11:49 +0000989 }
990};
991
992SkPicture* PictureRenderer::createPicture() {
993 switch (fBBoxHierarchyType) {
994 case kNone_BBoxHierarchyType:
995 return SkNEW(SkPicture);
commit-bot@chromium.orgc22d1392014-02-03 18:08:33 +0000996 case kQuadTree_BBoxHierarchyType:
997 return SkNEW_ARGS(SkQuadTreePicture, (SkIRect::MakeWH(fPicture->width(),
commit-bot@chromium.org8ddc26b2014-03-31 17:55:12 +0000998 fPicture->height())));
junov@chromium.org9313ca42012-11-02 18:11:49 +0000999 case kRTree_BBoxHierarchyType:
1000 return SkNEW(RTreePicture);
junov@chromium.org7b537062012-11-06 18:58:43 +00001001 case kTileGrid_BBoxHierarchyType:
junov@chromium.org29b19e52013-02-27 18:35:16 +00001002 return SkNEW_ARGS(SkTileGridPicture, (fPicture->width(),
commit-bot@chromium.org8ddc26b2014-03-31 17:55:12 +00001003 fPicture->height(), fGridInfo));
junov@chromium.org9313ca42012-11-02 18:11:49 +00001004 }
1005 SkASSERT(0); // invalid bbhType
1006 return NULL;
keyar@chromium.org451bb9f2012-07-26 17:27:57 +00001007}
junov@chromium.org9313ca42012-11-02 18:11:49 +00001008
reed@google.comfe7b1ed2012-11-29 21:00:39 +00001009///////////////////////////////////////////////////////////////////////////////
1010
1011class GatherRenderer : public PictureRenderer {
1012public:
commit-bot@chromium.org8ddc26b2014-03-31 17:55:12 +00001013 virtual bool render(SkBitmap** out = NULL) SK_OVERRIDE {
reed@google.comfe7b1ed2012-11-29 21:00:39 +00001014 SkRect bounds = SkRect::MakeWH(SkIntToScalar(fPicture->width()),
1015 SkIntToScalar(fPicture->height()));
1016 SkData* data = SkPictureUtils::GatherPixelRefs(fPicture, bounds);
1017 SkSafeUnref(data);
skia.committer@gmail.comc7b4be72012-12-11 02:01:20 +00001018
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +00001019 return (fOutputDir.isEmpty()); // we don't have anything to write
reed@google.comfe7b1ed2012-11-29 21:00:39 +00001020 }
skia.committer@gmail.comc7b4be72012-12-11 02:01:20 +00001021
reed@google.comfe7b1ed2012-11-29 21:00:39 +00001022private:
1023 virtual SkString getConfigNameInternal() SK_OVERRIDE {
1024 return SkString("gather_pixelrefs");
1025 }
1026};
1027
1028PictureRenderer* CreateGatherPixelRefsRenderer() {
1029 return SkNEW(GatherRenderer);
1030}
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +00001031
reed@google.com5a34fd32012-12-10 16:05:09 +00001032///////////////////////////////////////////////////////////////////////////////
1033
1034class PictureCloneRenderer : public PictureRenderer {
1035public:
commit-bot@chromium.org8ddc26b2014-03-31 17:55:12 +00001036 virtual bool render(SkBitmap** out = NULL) SK_OVERRIDE {
reed@google.com5a34fd32012-12-10 16:05:09 +00001037 for (int i = 0; i < 100; ++i) {
1038 SkPicture* clone = fPicture->clone();
1039 SkSafeUnref(clone);
1040 }
skia.committer@gmail.comc7b4be72012-12-11 02:01:20 +00001041
commit-bot@chromium.orgf5e315c2014-03-19 17:26:07 +00001042 return (fOutputDir.isEmpty()); // we don't have anything to write
reed@google.com5a34fd32012-12-10 16:05:09 +00001043 }
skia.committer@gmail.comc7b4be72012-12-11 02:01:20 +00001044
reed@google.com5a34fd32012-12-10 16:05:09 +00001045private:
1046 virtual SkString getConfigNameInternal() SK_OVERRIDE {
1047 return SkString("picture_clone");
1048 }
1049};
1050
1051PictureRenderer* CreatePictureCloneRenderer() {
1052 return SkNEW(PictureCloneRenderer);
1053}
1054
junov@chromium.org9313ca42012-11-02 18:11:49 +00001055} // namespace sk_tools