blob: 132f88d8527378fd53448be0cd1df994baafbeef [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"
11#include "SkCanvas.h"
12#include "SkDevice.h"
13#include "SkGPipe.h"
scroggo@google.com58b4ead2012-08-31 16:15:22 +000014#if SK_SUPPORT_GPU
15#include "SkGpuDevice.h"
16#endif
17#include "SkGraphics.h"
18#include "SkImageEncoder.h"
caryclark@google.coma3622372012-11-06 21:26:13 +000019#include "SkMaskFilter.h"
keyar@chromium.orgea826952012-08-23 15:24:13 +000020#include "SkMatrix.h"
keyar@chromium.org451bb9f2012-07-26 17:27:57 +000021#include "SkPicture.h"
junov@chromium.org9313ca42012-11-02 18:11:49 +000022#include "SkRTree.h"
keyar@chromium.orgea826952012-08-23 15:24:13 +000023#include "SkScalar.h"
scroggo@google.coma9e3a362012-11-07 17:52:48 +000024#include "SkStream.h"
keyar@chromium.org9299ede2012-08-21 19:05:08 +000025#include "SkString.h"
scroggo@google.com58b4ead2012-08-31 16:15:22 +000026#include "SkTemplates.h"
junov@chromium.org7b537062012-11-06 18:58:43 +000027#include "SkTileGrid.h"
keyar@chromium.org451bb9f2012-07-26 17:27:57 +000028#include "SkTDArray.h"
scroggo@google.com58b4ead2012-08-31 16:15:22 +000029#include "SkThreadUtils.h"
keyar@chromium.org451bb9f2012-07-26 17:27:57 +000030#include "SkTypes.h"
keyar@chromium.org4ea96c52012-08-20 15:03:29 +000031
keyar@chromium.org451bb9f2012-07-26 17:27:57 +000032namespace sk_tools {
33
34enum {
35 kDefaultTileWidth = 256,
36 kDefaultTileHeight = 256
37};
38
keyar@chromium.org9d696c02012-08-07 17:11:33 +000039void PictureRenderer::init(SkPicture* pict) {
keyar@chromium.org78a35c52012-08-20 15:03:44 +000040 SkASSERT(NULL == fPicture);
41 SkASSERT(NULL == fCanvas.get());
42 if (fPicture != NULL || NULL != fCanvas.get()) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +000043 return;
44 }
45
46 SkASSERT(pict != NULL);
keyar@chromium.org78a35c52012-08-20 15:03:44 +000047 if (NULL == pict) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +000048 return;
49 }
50
51 fPicture = pict;
junov@chromium.org9313ca42012-11-02 18:11:49 +000052 fPicture->ref();
keyar@chromium.orga474ce32012-08-20 15:03:57 +000053 fCanvas.reset(this->setupCanvas());
54}
55
caryclark@google.coma3622372012-11-06 21:26:13 +000056class FlagsDrawFilter : public SkDrawFilter {
57public:
58 FlagsDrawFilter(PictureRenderer::DrawFilterFlags* flags) :
59 fFlags(flags) {}
60
reed@google.com971aca72012-11-26 20:26:54 +000061 virtual bool filter(SkPaint* paint, Type t) {
caryclark@google.coma3622372012-11-06 21:26:13 +000062 paint->setFlags(paint->getFlags() & ~fFlags[t] & SkPaint::kAllFlags);
caryclark@google.come3e940c2012-11-07 16:42:17 +000063 if ((PictureRenderer::kBlur_DrawFilterFlag | PictureRenderer::kLowBlur_DrawFilterFlag)
64 & fFlags[t]) {
caryclark@google.coma3622372012-11-06 21:26:13 +000065 SkMaskFilter* maskFilter = paint->getMaskFilter();
66 SkMaskFilter::BlurInfo blurInfo;
67 if (maskFilter && maskFilter->asABlur(&blurInfo)) {
caryclark@google.come3e940c2012-11-07 16:42:17 +000068 if (PictureRenderer::kBlur_DrawFilterFlag & fFlags[t]) {
69 paint->setMaskFilter(NULL);
70 } else {
71 blurInfo.fHighQuality = false;
72 maskFilter->setAsABlur(blurInfo);
73 }
caryclark@google.coma3622372012-11-06 21:26:13 +000074 }
75 }
76 if (PictureRenderer::kHinting_DrawFilterFlag & fFlags[t]) {
77 paint->setHinting(SkPaint::kNo_Hinting);
78 } else if (PictureRenderer::kSlightHinting_DrawFilterFlag & fFlags[t]) {
79 paint->setHinting(SkPaint::kSlight_Hinting);
80 }
reed@google.com971aca72012-11-26 20:26:54 +000081 return true;
caryclark@google.coma3622372012-11-06 21:26:13 +000082 }
83
84private:
85 PictureRenderer::DrawFilterFlags* fFlags;
86};
87
88static SkCanvas* setUpFilter(SkCanvas* canvas, PictureRenderer::DrawFilterFlags* drawFilters) {
89 if (drawFilters && !canvas->getDrawFilter()) {
90 canvas->setDrawFilter(SkNEW_ARGS(FlagsDrawFilter, (drawFilters)))->unref();
caryclark@google.come3e940c2012-11-07 16:42:17 +000091 if (drawFilters[0] & PictureRenderer::kAAClip_DrawFilterFlag) {
92 canvas->setAllowSoftClip(false);
93 }
caryclark@google.coma3622372012-11-06 21:26:13 +000094 }
95 return canvas;
96}
97
keyar@chromium.orga474ce32012-08-20 15:03:57 +000098SkCanvas* PictureRenderer::setupCanvas() {
99 return this->setupCanvas(fPicture->width(), fPicture->height());
100}
101
102SkCanvas* PictureRenderer::setupCanvas(int width, int height) {
caryclark@google.coma3622372012-11-06 21:26:13 +0000103 SkCanvas* canvas;
keyar@chromium.org4ea96c52012-08-20 15:03:29 +0000104 switch(fDeviceType) {
105 case kBitmap_DeviceType: {
106 SkBitmap bitmap;
keyar@chromium.orga474ce32012-08-20 15:03:57 +0000107 sk_tools::setup_bitmap(&bitmap, width, height);
caryclark@google.coma3622372012-11-06 21:26:13 +0000108 canvas = SkNEW_ARGS(SkCanvas, (bitmap));
109 return setUpFilter(canvas, fDrawFilters);
keyar@chromium.org4ea96c52012-08-20 15:03:29 +0000110 }
111#if SK_SUPPORT_GPU
112 case kGPU_DeviceType: {
keyar@chromium.org4ea96c52012-08-20 15:03:29 +0000113 SkAutoTUnref<SkGpuDevice> device(SkNEW_ARGS(SkGpuDevice,
keyar@chromium.org06125642012-08-20 15:03:33 +0000114 (fGrContext, SkBitmap::kARGB_8888_Config,
keyar@chromium.orga474ce32012-08-20 15:03:57 +0000115 width, height)));
caryclark@google.coma3622372012-11-06 21:26:13 +0000116 canvas = SkNEW_ARGS(SkCanvas, (device.get()));
117 return setUpFilter(canvas, fDrawFilters);
keyar@chromium.org4ea96c52012-08-20 15:03:29 +0000118 }
119#endif
120 default:
121 SkASSERT(0);
122 }
keyar@chromium.orga474ce32012-08-20 15:03:57 +0000123
124 return NULL;
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000125}
126
127void PictureRenderer::end() {
keyar@chromium.org77a55222012-08-20 15:03:47 +0000128 this->resetState();
junov@chromium.org9313ca42012-11-02 18:11:49 +0000129 SkSafeUnref(fPicture);
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000130 fPicture = NULL;
131 fCanvas.reset(NULL);
132}
133
junov@chromium.org9313ca42012-11-02 18:11:49 +0000134/** Converts fPicture to a picture that uses a BBoxHierarchy.
135 * PictureRenderer subclasses that are used to test picture playback
136 * should call this method during init.
137 */
138void PictureRenderer::buildBBoxHierarchy() {
139 SkASSERT(NULL != fPicture);
140 if (kNone_BBoxHierarchyType != fBBoxHierarchyType && NULL != fPicture) {
141 SkPicture* newPicture = this->createPicture();
142 SkCanvas* recorder = newPicture->beginRecording(fPicture->width(), fPicture->height(),
143 this->recordFlags());
144 fPicture->draw(recorder);
145 newPicture->endRecording();
146 fPicture->unref();
147 fPicture = newPicture;
148 }
149}
150
keyar@chromium.org77a55222012-08-20 15:03:47 +0000151void PictureRenderer::resetState() {
keyar@chromium.org28136b32012-08-20 15:04:15 +0000152#if SK_SUPPORT_GPU
153 if (this->isUsingGpuDevice()) {
154 SkGLContext* glContext = fGrContextFactory.getGLContext(
155 GrContextFactory::kNative_GLContextType);
keyar@chromium.org28136b32012-08-20 15:04:15 +0000156
157 SkASSERT(glContext != NULL);
158 if (NULL == glContext) {
159 return;
160 }
161
scroggo@google.com9a412522012-09-07 15:21:18 +0000162 fGrContext->flush();
keyar@chromium.org77a55222012-08-20 15:03:47 +0000163 SK_GL(*glContext, Finish());
keyar@chromium.org77a55222012-08-20 15:03:47 +0000164 }
keyar@chromium.orga40c20d2012-08-20 15:04:12 +0000165#endif
keyar@chromium.org77a55222012-08-20 15:03:47 +0000166}
167
junov@chromium.org9313ca42012-11-02 18:11:49 +0000168uint32_t PictureRenderer::recordFlags() {
169 return kNone_BBoxHierarchyType == fBBoxHierarchyType ? 0 :
170 SkPicture::kOptimizeForClippedPlayback_RecordingFlag;
171}
172
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000173/**
174 * Write the canvas to the specified path.
175 * @param canvas Must be non-null. Canvas to be written to a file.
176 * @param path Path for the file to be written. Should have no extension; write() will append
177 * an appropriate one. Passed in by value so it can be modified.
178 * @return bool True if the Canvas is written to a file.
179 */
180static bool write(SkCanvas* canvas, SkString path) {
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000181 SkASSERT(canvas != NULL);
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000182 if (NULL == canvas) {
keyar@chromium.org9299ede2012-08-21 19:05:08 +0000183 return false;
184 }
185
186 SkBitmap bitmap;
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000187 SkISize size = canvas->getDeviceSize();
188 sk_tools::setup_bitmap(&bitmap, size.width(), size.height());
keyar@chromium.org9299ede2012-08-21 19:05:08 +0000189
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000190 canvas->readPixels(&bitmap, 0, 0);
keyar@chromium.org9299ede2012-08-21 19:05:08 +0000191 sk_tools::force_all_opaque(bitmap);
192
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000193 // Since path is passed in by value, it is okay to modify it.
194 path.append(".png");
keyar@chromium.org9299ede2012-08-21 19:05:08 +0000195 return SkImageEncoder::EncodeFile(path.c_str(), bitmap, SkImageEncoder::kPNG_Type, 100);
196}
197
scroggo@google.comb6e806b2012-10-03 17:32:33 +0000198/**
199 * If path is non NULL, append number to it, and call write(SkCanvas*, SkString) to write the
200 * provided canvas to a file. Returns true if path is NULL or if write() succeeds.
201 */
202static bool writeAppendNumber(SkCanvas* canvas, const SkString* path, int number) {
203 if (NULL == path) {
204 return true;
205 }
206 SkString pathWithNumber(*path);
207 pathWithNumber.appendf("%i", number);
208 return write(canvas, pathWithNumber);
209}
210
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000211///////////////////////////////////////////////////////////////////////////////////////////////
212
djsollen@google.comfd9720c2012-11-06 16:54:40 +0000213SkCanvas* RecordPictureRenderer::setupCanvas(int width, int height) {
214 // defer the canvas setup until the render step
215 return NULL;
216}
217
scroggo@google.coma9e3a362012-11-07 17:52:48 +0000218static bool PNGEncodeBitmapToStream(SkWStream* wStream, const SkBitmap& bm) {
219 return SkImageEncoder::EncodeStream(wStream, bm, SkImageEncoder::kPNG_Type, 100);
220}
221
222bool RecordPictureRenderer::render(const SkString* path) {
junov@chromium.org9313ca42012-11-02 18:11:49 +0000223 SkAutoTUnref<SkPicture> replayer(this->createPicture());
224 SkCanvas* recorder = replayer->beginRecording(fPicture->width(), fPicture->height(),
225 this->recordFlags());
scroggo@google.com9a412522012-09-07 15:21:18 +0000226 fPicture->draw(recorder);
junov@chromium.org9313ca42012-11-02 18:11:49 +0000227 replayer->endRecording();
scroggo@google.coma9e3a362012-11-07 17:52:48 +0000228 if (path != NULL) {
229 // Record the new picture as a new SKP with PNG encoded bitmaps.
230 SkString skpPath(*path);
231 // ".skp" was removed from 'path' before being passed in here.
232 skpPath.append(".skp");
233 SkFILEWStream stream(skpPath.c_str());
234 replayer->serialize(&stream, &PNGEncodeBitmapToStream);
235 return true;
236 }
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000237 return false;
scroggo@google.com9a412522012-09-07 15:21:18 +0000238}
239
scroggo@google.com0a049b82012-11-02 22:01:26 +0000240SkString RecordPictureRenderer::getConfigNameInternal() {
241 return SkString("record");
242}
243
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000244///////////////////////////////////////////////////////////////////////////////////////////////
245
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000246bool PipePictureRenderer::render(const SkString* path) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000247 SkASSERT(fCanvas.get() != NULL);
248 SkASSERT(fPicture != NULL);
keyar@chromium.org78a35c52012-08-20 15:03:44 +0000249 if (NULL == fCanvas.get() || NULL == fPicture) {
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000250 return false;
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000251 }
252
253 PipeController pipeController(fCanvas.get());
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000254 SkGPipeWriter writer;
255 SkCanvas* pipeCanvas = writer.startRecording(&pipeController);
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000256 pipeCanvas->drawPicture(*fPicture);
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000257 writer.endRecording();
scroggo@google.com9a412522012-09-07 15:21:18 +0000258 fCanvas->flush();
borenet@google.com070d3542012-10-26 13:26:55 +0000259 if (NULL != path) {
260 return write(fCanvas, *path);
261 }
262 return true;
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000263}
264
scroggo@google.com0a049b82012-11-02 22:01:26 +0000265SkString PipePictureRenderer::getConfigNameInternal() {
266 return SkString("pipe");
267}
268
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000269///////////////////////////////////////////////////////////////////////////////////////////////
270
junov@chromium.org9313ca42012-11-02 18:11:49 +0000271void SimplePictureRenderer::init(SkPicture* picture) {
272 INHERITED::init(picture);
273 this->buildBBoxHierarchy();
274}
275
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000276bool SimplePictureRenderer::render(const SkString* path) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000277 SkASSERT(fCanvas.get() != NULL);
278 SkASSERT(fPicture != NULL);
keyar@chromium.org78a35c52012-08-20 15:03:44 +0000279 if (NULL == fCanvas.get() || NULL == fPicture) {
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000280 return false;
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000281 }
282
283 fCanvas->drawPicture(*fPicture);
scroggo@google.com9a412522012-09-07 15:21:18 +0000284 fCanvas->flush();
borenet@google.com070d3542012-10-26 13:26:55 +0000285 if (NULL != path) {
286 return write(fCanvas, *path);
287 }
288 return true;
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000289}
290
scroggo@google.com0a049b82012-11-02 22:01:26 +0000291SkString SimplePictureRenderer::getConfigNameInternal() {
292 return SkString("simple");
293}
294
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000295///////////////////////////////////////////////////////////////////////////////////////////////
296
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000297TiledPictureRenderer::TiledPictureRenderer()
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000298 : fTileWidth(kDefaultTileWidth)
rileya@google.comb947b912012-08-29 17:35:07 +0000299 , fTileHeight(kDefaultTileHeight)
rileya@google.coma04dc022012-09-10 19:01:38 +0000300 , fTileWidthPercentage(0.0)
rileya@google.comb947b912012-08-29 17:35:07 +0000301 , fTileHeightPercentage(0.0)
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000302 , fTileMinPowerOf2Width(0) { }
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000303
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000304void TiledPictureRenderer::init(SkPicture* pict) {
305 SkASSERT(pict != NULL);
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000306 SkASSERT(0 == fTileRects.count());
307 if (NULL == pict || fTileRects.count() != 0) {
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000308 return;
309 }
310
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000311 // Do not call INHERITED::init(), which would create a (potentially large) canvas which is not
312 // used by bench_pictures.
313 fPicture = pict;
junov@chromium.org9313ca42012-11-02 18:11:49 +0000314 fPicture->ref();
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000315 this->buildBBoxHierarchy();
keyar@chromium.orgcc6e5ef2012-07-27 20:09:26 +0000316
317 if (fTileWidthPercentage > 0) {
robertphillips@google.com5d8d1862012-08-15 14:36:41 +0000318 fTileWidth = sk_float_ceil2int(float(fTileWidthPercentage * fPicture->width() / 100));
keyar@chromium.orgcc6e5ef2012-07-27 20:09:26 +0000319 }
320 if (fTileHeightPercentage > 0) {
robertphillips@google.com5d8d1862012-08-15 14:36:41 +0000321 fTileHeight = sk_float_ceil2int(float(fTileHeightPercentage * fPicture->height() / 100));
keyar@chromium.orgcc6e5ef2012-07-27 20:09:26 +0000322 }
323
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000324 if (fTileMinPowerOf2Width > 0) {
325 this->setupPowerOf2Tiles();
326 } else {
327 this->setupTiles();
328 }
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000329}
330
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000331void TiledPictureRenderer::end() {
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000332 fTileRects.reset();
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000333 this->INHERITED::end();
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000334}
335
keyar@chromium.org9d696c02012-08-07 17:11:33 +0000336void TiledPictureRenderer::setupTiles() {
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000337 for (int tile_y_start = 0; tile_y_start < fPicture->height(); tile_y_start += fTileHeight) {
338 for (int tile_x_start = 0; tile_x_start < fPicture->width(); tile_x_start += fTileWidth) {
339 *fTileRects.append() = SkRect::MakeXYWH(SkIntToScalar(tile_x_start),
340 SkIntToScalar(tile_y_start),
341 SkIntToScalar(fTileWidth),
342 SkIntToScalar(fTileHeight));
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000343 }
344 }
345}
346
347// The goal of the powers of two tiles is to minimize the amount of wasted tile
348// space in the width-wise direction and then minimize the number of tiles. The
349// constraints are that every tile must have a pixel width that is a power of
350// two and also be of some minimal width (that is also a power of two).
351//
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000352// This is solved by first taking our picture size and rounding it up to the
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000353// multiple of the minimal width. The binary representation of this rounded
354// value gives us the tiles we need: a bit of value one means we need a tile of
355// that size.
356void TiledPictureRenderer::setupPowerOf2Tiles() {
357 int rounded_value = fPicture->width();
358 if (fPicture->width() % fTileMinPowerOf2Width != 0) {
359 rounded_value = fPicture->width() - (fPicture->width() % fTileMinPowerOf2Width)
360 + fTileMinPowerOf2Width;
361 }
362
robertphillips@google.com94acc702012-09-06 18:43:21 +0000363 int num_bits = SkScalarCeilToInt(SkScalarLog2(SkIntToScalar(fPicture->width())));
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000364 int largest_possible_tile_size = 1 << num_bits;
365
366 // The tile height is constant for a particular picture.
367 for (int tile_y_start = 0; tile_y_start < fPicture->height(); tile_y_start += fTileHeight) {
368 int tile_x_start = 0;
369 int current_width = largest_possible_tile_size;
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000370 // Set fTileWidth to be the width of the widest tile, so that each canvas is large enough
371 // to draw each tile.
372 fTileWidth = current_width;
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000373
374 while (current_width >= fTileMinPowerOf2Width) {
375 // It is very important this is a bitwise AND.
376 if (current_width & rounded_value) {
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000377 *fTileRects.append() = SkRect::MakeXYWH(SkIntToScalar(tile_x_start),
378 SkIntToScalar(tile_y_start),
379 SkIntToScalar(current_width),
380 SkIntToScalar(fTileHeight));
keyar@chromium.orgf4959ab2012-08-23 20:53:25 +0000381 tile_x_start += current_width;
382 }
383
384 current_width >>= 1;
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000385 }
386 }
387}
388
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000389/**
390 * Draw the specified playback to the canvas translated to rectangle provided, so that this mini
391 * canvas represents the rectangle's portion of the overall picture.
392 * Saves and restores so that the initial clip and matrix return to their state before this function
393 * is called.
394 */
395template<class T>
396static void DrawTileToCanvas(SkCanvas* canvas, const SkRect& tileRect, T* playback) {
397 int saveCount = canvas->save();
398 // Translate so that we draw the correct portion of the picture
399 canvas->translate(-tileRect.fLeft, -tileRect.fTop);
400 playback->draw(canvas);
401 canvas->restoreToCount(saveCount);
402 canvas->flush();
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000403}
404
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000405///////////////////////////////////////////////////////////////////////////////////////////////
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000406
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000407bool TiledPictureRenderer::render(const SkString* path) {
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000408 SkASSERT(fPicture != NULL);
409 if (NULL == fPicture) {
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000410 return false;
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000411 }
412
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000413 // Reuse one canvas for all tiles.
414 SkCanvas* canvas = this->setupCanvas(fTileWidth, fTileHeight);
415 SkAutoUnref aur(canvas);
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000416
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000417 bool success = true;
418 for (int i = 0; i < fTileRects.count(); ++i) {
419 DrawTileToCanvas(canvas, fTileRects[i], fPicture);
420 if (NULL != path) {
421 success &= writeAppendNumber(canvas, path, i);
scroggo@google.com58b4ead2012-08-31 16:15:22 +0000422 }
keyar@chromium.org163b5672012-08-01 17:53:29 +0000423 }
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000424 return success;
keyar@chromium.org163b5672012-08-01 17:53:29 +0000425}
426
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000427SkCanvas* TiledPictureRenderer::setupCanvas(int width, int height) {
428 SkCanvas* canvas = this->INHERITED::setupCanvas(width, height);
429 SkASSERT(fPicture != NULL);
430 // Clip the tile to an area that is completely in what the SkPicture says is the
431 // drawn-to area. This is mostly important for tiles on the right and bottom edges
432 // as they may go over this area and the picture may have some commands that
433 // draw outside of this area and so should not actually be written.
434 SkRect clip = SkRect::MakeWH(SkIntToScalar(fPicture->width()),
435 SkIntToScalar(fPicture->height()));
436 canvas->clipRect(clip);
437 return canvas;
438}
scroggo@google.com0a049b82012-11-02 22:01:26 +0000439
440SkString TiledPictureRenderer::getConfigNameInternal() {
441 SkString name;
442 if (fTileMinPowerOf2Width > 0) {
443 name.append("pow2tile_");
444 name.appendf("%i", fTileMinPowerOf2Width);
445 } else {
446 name.append("tile_");
447 if (fTileWidthPercentage > 0) {
448 name.appendf("%.f%%", fTileWidthPercentage);
449 } else {
450 name.appendf("%i", fTileWidth);
451 }
452 }
453 name.append("x");
454 if (fTileHeightPercentage > 0) {
455 name.appendf("%.f%%", fTileHeightPercentage);
456 } else {
457 name.appendf("%i", fTileHeight);
458 }
459 return name;
460}
461
scroggo@google.coma62da2f2012-11-02 21:28:12 +0000462///////////////////////////////////////////////////////////////////////////////////////////////
463
464// Holds all of the information needed to draw a set of tiles.
465class CloneData : public SkRunnable {
466
467public:
468 CloneData(SkPicture* clone, SkCanvas* canvas, SkTDArray<SkRect>& rects, int start, int end,
469 SkRunnable* done)
470 : fClone(clone)
471 , fCanvas(canvas)
472 , fPath(NULL)
473 , fRects(rects)
474 , fStart(start)
475 , fEnd(end)
476 , fSuccess(NULL)
477 , fDone(done) {
478 SkASSERT(fDone != NULL);
479 }
480
481 virtual void run() SK_OVERRIDE {
482 SkGraphics::SetTLSFontCacheLimit(1024 * 1024);
483 for (int i = fStart; i < fEnd; i++) {
484 DrawTileToCanvas(fCanvas, fRects[i], fClone);
485 if (fPath != NULL && !writeAppendNumber(fCanvas, fPath, i)
486 && fSuccess != NULL) {
487 *fSuccess = false;
488 // If one tile fails to write to a file, do not continue drawing the rest.
489 break;
490 }
491 }
492 fDone->run();
493 }
494
495 void setPathAndSuccess(const SkString* path, bool* success) {
496 fPath = path;
497 fSuccess = success;
498 }
499
500private:
501 // All pointers unowned.
502 SkPicture* fClone; // Picture to draw from. Each CloneData has a unique one which
503 // is threadsafe.
504 SkCanvas* fCanvas; // Canvas to draw to. Reused for each tile.
505 const SkString* fPath; // If non-null, path to write the result to as a PNG.
506 SkTDArray<SkRect>& fRects; // All tiles of the picture.
507 const int fStart; // Range of tiles drawn by this thread.
508 const int fEnd;
509 bool* fSuccess; // Only meaningful if path is non-null. Shared by all threads,
510 // and only set to false upon failure to write to a PNG.
511 SkRunnable* fDone;
512};
513
514MultiCorePictureRenderer::MultiCorePictureRenderer(int threadCount)
515: fNumThreads(threadCount)
516, fThreadPool(threadCount)
517, fCountdown(threadCount) {
518 // Only need to create fNumThreads - 1 clones, since one thread will use the base
519 // picture.
520 fPictureClones = SkNEW_ARRAY(SkPicture, fNumThreads - 1);
521 fCloneData = SkNEW_ARRAY(CloneData*, fNumThreads);
522}
523
524void MultiCorePictureRenderer::init(SkPicture *pict) {
525 // Set fPicture and the tiles.
526 this->INHERITED::init(pict);
527 for (int i = 0; i < fNumThreads; ++i) {
528 *fCanvasPool.append() = this->setupCanvas(this->getTileWidth(), this->getTileHeight());
529 }
530 // Only need to create fNumThreads - 1 clones, since one thread will use the base picture.
531 fPicture->clone(fPictureClones, fNumThreads - 1);
532 // Populate each thread with the appropriate data.
533 // Group the tiles into nearly equal size chunks, rounding up so we're sure to cover them all.
534 const int chunkSize = (fTileRects.count() + fNumThreads - 1) / fNumThreads;
535
536 for (int i = 0; i < fNumThreads; i++) {
537 SkPicture* pic;
538 if (i == fNumThreads-1) {
539 // The last set will use the original SkPicture.
540 pic = fPicture;
541 } else {
542 pic = &fPictureClones[i];
543 }
544 const int start = i * chunkSize;
545 const int end = SkMin32(start + chunkSize, fTileRects.count());
546 fCloneData[i] = SkNEW_ARGS(CloneData,
547 (pic, fCanvasPool[i], fTileRects, start, end, &fCountdown));
548 }
549}
550
551bool MultiCorePictureRenderer::render(const SkString *path) {
552 bool success = true;
553 if (path != NULL) {
554 for (int i = 0; i < fNumThreads-1; i++) {
555 fCloneData[i]->setPathAndSuccess(path, &success);
556 }
557 }
558
559 fCountdown.reset(fNumThreads);
560 for (int i = 0; i < fNumThreads; i++) {
561 fThreadPool.add(fCloneData[i]);
562 }
563 fCountdown.wait();
564
565 return success;
566}
567
568void MultiCorePictureRenderer::end() {
569 for (int i = 0; i < fNumThreads - 1; i++) {
570 SkDELETE(fCloneData[i]);
571 fCloneData[i] = NULL;
572 }
573
574 fCanvasPool.unrefAll();
575
576 this->INHERITED::end();
577}
578
579MultiCorePictureRenderer::~MultiCorePictureRenderer() {
580 // Each individual CloneData was deleted in end.
581 SkDELETE_ARRAY(fCloneData);
582 SkDELETE_ARRAY(fPictureClones);
583}
scroggo@google.combcdf2ec2012-09-20 14:42:33 +0000584
scroggo@google.com0a049b82012-11-02 22:01:26 +0000585SkString MultiCorePictureRenderer::getConfigNameInternal() {
586 SkString name = this->INHERITED::getConfigNameInternal();
587 name.appendf("_multi_%i_threads", fNumThreads);
588 return name;
589}
590
scroggo@google.comacfb30e2012-09-18 14:32:35 +0000591///////////////////////////////////////////////////////////////////////////////////////////////
scroggo@google.com9a412522012-09-07 15:21:18 +0000592
593void PlaybackCreationRenderer::setup() {
junov@chromium.org9313ca42012-11-02 18:11:49 +0000594 fReplayer.reset(this->createPicture());
595 SkCanvas* recorder = fReplayer->beginRecording(fPicture->width(), fPicture->height(),
596 this->recordFlags());
scroggo@google.com9a412522012-09-07 15:21:18 +0000597 fPicture->draw(recorder);
598}
599
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000600bool PlaybackCreationRenderer::render(const SkString*) {
junov@chromium.org9313ca42012-11-02 18:11:49 +0000601 fReplayer->endRecording();
scroggo@google.com81f9d2e2012-09-20 14:54:21 +0000602 // Since this class does not actually render, return false.
603 return false;
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000604}
605
scroggo@google.com0a049b82012-11-02 22:01:26 +0000606SkString PlaybackCreationRenderer::getConfigNameInternal() {
607 return SkString("playback_creation");
608}
609
junov@chromium.org9313ca42012-11-02 18:11:49 +0000610///////////////////////////////////////////////////////////////////////////////////////////////
611// SkPicture variants for each BBoxHierarchy type
612
613class RTreePicture : public SkPicture {
614public:
615 virtual SkBBoxHierarchy* createBBoxHierarchy() const SK_OVERRIDE{
616 static const int kRTreeMinChildren = 6;
617 static const int kRTreeMaxChildren = 11;
618 SkScalar aspectRatio = SkScalarDiv(SkIntToScalar(fWidth),
619 SkIntToScalar(fHeight));
620 return SkRTree::Create(kRTreeMinChildren, kRTreeMaxChildren,
621 aspectRatio);
622 }
623};
624
junov@chromium.org7b537062012-11-06 18:58:43 +0000625class TileGridPicture : public SkPicture {
626public:
627 TileGridPicture(int tileWidth, int tileHeight, int xTileCount, int yTileCount) {
628 fTileWidth = tileWidth;
629 fTileHeight = tileHeight;
630 fXTileCount = xTileCount;
631 fYTileCount = yTileCount;
632 }
633
634 virtual SkBBoxHierarchy* createBBoxHierarchy() const SK_OVERRIDE{
635 return SkNEW_ARGS(SkTileGrid, (fTileWidth, fTileHeight, fXTileCount, fYTileCount));
636 }
637private:
638 int fTileWidth, fTileHeight, fXTileCount, fYTileCount;
639};
640
junov@chromium.org9313ca42012-11-02 18:11:49 +0000641SkPicture* PictureRenderer::createPicture() {
642 switch (fBBoxHierarchyType) {
643 case kNone_BBoxHierarchyType:
644 return SkNEW(SkPicture);
645 case kRTree_BBoxHierarchyType:
646 return SkNEW(RTreePicture);
junov@chromium.org7b537062012-11-06 18:58:43 +0000647 case kTileGrid_BBoxHierarchyType:
648 {
649 int xTileCount = fPicture->width() / fGridWidth +
650 ((fPicture->width() % fGridWidth) ? 1 : 0);
651 int yTileCount = fPicture->height() / fGridHeight +
652 ((fPicture->height() % fGridHeight) ? 1 : 0);
653 return SkNEW_ARGS(TileGridPicture, (fGridWidth, fGridHeight, xTileCount,
654 yTileCount));
655 }
junov@chromium.org9313ca42012-11-02 18:11:49 +0000656 }
657 SkASSERT(0); // invalid bbhType
658 return NULL;
keyar@chromium.org451bb9f2012-07-26 17:27:57 +0000659}
junov@chromium.org9313ca42012-11-02 18:11:49 +0000660
661} // namespace sk_tools