1 /*
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
8 #include "PictureRenderer.h"
9 #include "picture_utils.h"
10 #include "SamplePipeControllers.h"
11 #include "SkBitmapHasher.h"
12 #include "SkCanvas.h"
13 #include "SkData.h"
14 #include "SkDevice.h"
15 #include "SkDiscardableMemoryPool.h"
16 #include "SkGPipe.h"
17 #if SK_SUPPORT_GPU
18 #include "gl/GrGLDefines.h"
19 #include "SkGpuDevice.h"
20 #endif
21 #include "SkGraphics.h"
22 #include "SkImageEncoder.h"
23 #include "SkMaskFilter.h"
24 #include "SkMatrix.h"
25 #include "SkMultiPictureDraw.h"
26 #include "SkOSFile.h"
27 #include "SkPicture.h"
28 #include "SkPictureRecorder.h"
29 #include "SkPictureUtils.h"
30 #include "SkPixelRef.h"
31 #include "SkPixelSerializer.h"
32 #include "SkScalar.h"
33 #include "SkStream.h"
34 #include "SkString.h"
35 #include "SkSurface.h"
36 #include "SkTemplates.h"
37 #include "SkTDArray.h"
38 #include "SkThreadUtils.h"
39 #include "SkTypes.h"
40 #include "sk_tool_utils.h"
41
scalar_log2(SkScalar x)42 static inline SkScalar scalar_log2(SkScalar x) {
43 static const SkScalar log2_conversion_factor = SkScalarInvert(SkScalarLog(2));
44
45 return SkScalarLog(x) * log2_conversion_factor;
46 }
47
48 namespace sk_tools {
49
50 enum {
51 kDefaultTileWidth = 256,
52 kDefaultTileHeight = 256
53 };
54
init(const SkPicture * pict,const SkString * writePath,const SkString * mismatchPath,const SkString * inputFilename,bool useChecksumBasedFilenames,bool useMultiPictureDraw)55 void PictureRenderer::init(const SkPicture* pict,
56 const SkString* writePath,
57 const SkString* mismatchPath,
58 const SkString* inputFilename,
59 bool useChecksumBasedFilenames,
60 bool useMultiPictureDraw) {
61 this->CopyString(&fWritePath, writePath);
62 this->CopyString(&fMismatchPath, mismatchPath);
63 this->CopyString(&fInputFilename, inputFilename);
64 fUseChecksumBasedFilenames = useChecksumBasedFilenames;
65 fUseMultiPictureDraw = useMultiPictureDraw;
66
67 SkASSERT(NULL == fPicture);
68 SkASSERT(NULL == fCanvas.get());
69 if (fPicture || fCanvas.get()) {
70 return;
71 }
72
73 SkASSERT(pict != NULL);
74 if (NULL == pict) {
75 return;
76 }
77
78 fPicture.reset(pict)->ref();
79 fCanvas.reset(this->setupCanvas());
80 }
81
CopyString(SkString * dest,const SkString * src)82 void PictureRenderer::CopyString(SkString* dest, const SkString* src) {
83 if (src) {
84 dest->set(*src);
85 } else {
86 dest->reset();
87 }
88 }
89
90 class FlagsDrawFilter : public SkDrawFilter {
91 public:
FlagsDrawFilter(PictureRenderer::DrawFilterFlags * flags)92 FlagsDrawFilter(PictureRenderer::DrawFilterFlags* flags) :
93 fFlags(flags) {}
94
filter(SkPaint * paint,Type t)95 virtual bool filter(SkPaint* paint, Type t) {
96 paint->setFlags(paint->getFlags() & ~fFlags[t] & SkPaint::kAllFlags);
97 if (PictureRenderer::kMaskFilter_DrawFilterFlag & fFlags[t]) {
98 SkMaskFilter* maskFilter = paint->getMaskFilter();
99 if (maskFilter) {
100 paint->setMaskFilter(NULL);
101 }
102 }
103 if (PictureRenderer::kHinting_DrawFilterFlag & fFlags[t]) {
104 paint->setHinting(SkPaint::kNo_Hinting);
105 } else if (PictureRenderer::kSlightHinting_DrawFilterFlag & fFlags[t]) {
106 paint->setHinting(SkPaint::kSlight_Hinting);
107 }
108 return true;
109 }
110
111 private:
112 PictureRenderer::DrawFilterFlags* fFlags;
113 };
114
setUpFilter(SkCanvas * canvas,PictureRenderer::DrawFilterFlags * drawFilters)115 static void setUpFilter(SkCanvas* canvas, PictureRenderer::DrawFilterFlags* drawFilters) {
116 if (drawFilters && !canvas->getDrawFilter()) {
117 canvas->setDrawFilter(SkNEW_ARGS(FlagsDrawFilter, (drawFilters)))->unref();
118 if (drawFilters[0] & PictureRenderer::kAAClip_DrawFilterFlag) {
119 canvas->setAllowSoftClip(false);
120 }
121 }
122 }
123
setupCanvas()124 SkCanvas* PictureRenderer::setupCanvas() {
125 const int width = this->getViewWidth();
126 const int height = this->getViewHeight();
127 return this->setupCanvas(width, height);
128 }
129
setupCanvas(int width,int height)130 SkCanvas* PictureRenderer::setupCanvas(int width, int height) {
131 SkCanvas* canvas;
132 switch(fDeviceType) {
133 case kBitmap_DeviceType: {
134 SkBitmap bitmap;
135 sk_tools::setup_bitmap(&bitmap, width, height);
136 canvas = SkNEW_ARGS(SkCanvas, (bitmap));
137 }
138 break;
139 #if SK_SUPPORT_GPU
140 #if SK_ANGLE
141 case kAngle_DeviceType:
142 // fall through
143 #endif
144 #if SK_MESA
145 case kMesa_DeviceType:
146 // fall through
147 #endif
148 case kGPU_DeviceType:
149 case kNVPR_DeviceType: {
150 SkAutoTUnref<GrSurface> target;
151 if (fGrContext) {
152 // create a render target to back the device
153 GrSurfaceDesc desc;
154 desc.fConfig = kSkia8888_GrPixelConfig;
155 desc.fFlags = kRenderTarget_GrSurfaceFlag;
156 desc.fWidth = width;
157 desc.fHeight = height;
158 desc.fSampleCnt = fSampleCount;
159 target.reset(fGrContext->textureProvider()->createTexture(desc, false, NULL, 0));
160 }
161
162 uint32_t flags = fUseDFText ? SkSurfaceProps::kUseDistanceFieldFonts_Flag : 0;
163 SkSurfaceProps props(flags, SkSurfaceProps::kLegacyFontHost_InitType);
164 SkAutoTUnref<SkGpuDevice> device(SkGpuDevice::Create(target->asRenderTarget(), &props));
165 if (!device) {
166 return NULL;
167 }
168 canvas = SkNEW_ARGS(SkCanvas, (device));
169 break;
170 }
171 #endif
172 default:
173 SkASSERT(0);
174 return NULL;
175 }
176 setUpFilter(canvas, fDrawFilters);
177 this->scaleToScaleFactor(canvas);
178
179 // Pictures often lie about their extent (i.e., claim to be 100x100 but
180 // only ever draw to 90x100). Clear here so the undrawn portion will have
181 // a consistent color
182 canvas->clear(SK_ColorTRANSPARENT);
183 return canvas;
184 }
185
scaleToScaleFactor(SkCanvas * canvas)186 void PictureRenderer::scaleToScaleFactor(SkCanvas* canvas) {
187 SkASSERT(canvas != NULL);
188 if (fScaleFactor != SK_Scalar1) {
189 canvas->scale(fScaleFactor, fScaleFactor);
190 }
191 }
192
end()193 void PictureRenderer::end() {
194 this->resetState(true);
195 fPicture.reset(NULL);
196 fCanvas.reset(NULL);
197 }
198
getViewWidth()199 int PictureRenderer::getViewWidth() {
200 SkASSERT(fPicture != NULL);
201 int width = SkScalarCeilToInt(fPicture->cullRect().width() * fScaleFactor);
202 if (fViewport.width() > 0) {
203 width = SkMin32(width, fViewport.width());
204 }
205 return width;
206 }
207
getViewHeight()208 int PictureRenderer::getViewHeight() {
209 SkASSERT(fPicture != NULL);
210 int height = SkScalarCeilToInt(fPicture->cullRect().height() * fScaleFactor);
211 if (fViewport.height() > 0) {
212 height = SkMin32(height, fViewport.height());
213 }
214 return height;
215 }
216
217 /** Converts fPicture to a picture that uses a BBoxHierarchy.
218 * PictureRenderer subclasses that are used to test picture playback
219 * should call this method during init.
220 */
buildBBoxHierarchy()221 void PictureRenderer::buildBBoxHierarchy() {
222 SkASSERT(fPicture);
223 if (kNone_BBoxHierarchyType != fBBoxHierarchyType && fPicture) {
224 SkAutoTDelete<SkBBHFactory> factory(this->getFactory());
225 SkPictureRecorder recorder;
226 uint32_t flags = this->recordFlags();
227 if (fUseMultiPictureDraw) {
228 flags |= SkPictureRecorder::kComputeSaveLayerInfo_RecordFlag;
229 }
230 SkCanvas* canvas = recorder.beginRecording(fPicture->cullRect().width(),
231 fPicture->cullRect().height(),
232 factory.get(),
233 flags);
234 fPicture->playback(canvas);
235 fPicture.reset(recorder.endRecording());
236 }
237 }
238
resetState(bool callFinish)239 void PictureRenderer::resetState(bool callFinish) {
240 #if SK_SUPPORT_GPU
241 SkGLContext* glContext = this->getGLContext();
242 if (NULL == glContext) {
243 SkASSERT(kBitmap_DeviceType == fDeviceType);
244 return;
245 }
246
247 fGrContext->flush();
248 glContext->swapBuffers();
249 if (callFinish) {
250 SK_GL(*glContext, Finish());
251 }
252 #endif
253 }
254
purgeTextures()255 void PictureRenderer::purgeTextures() {
256 SkDiscardableMemoryPool* pool = SkGetGlobalDiscardableMemoryPool();
257
258 pool->dumpPool();
259
260 #if SK_SUPPORT_GPU
261 SkGLContext* glContext = this->getGLContext();
262 if (NULL == glContext) {
263 SkASSERT(kBitmap_DeviceType == fDeviceType);
264 return;
265 }
266
267 // resetState should've already done this
268 fGrContext->flush();
269
270 fGrContext->purgeAllUnlockedResources();
271 #endif
272 }
273
274 /**
275 * Write the canvas to an image file and/or JSON summary.
276 *
277 * @param canvas Must be non-null. Canvas to be written to a file.
278 * @param writePath If nonempty, write the binary image to a file within this directory.
279 * @param mismatchPath If nonempty, write the binary image to a file within this directory,
280 * but only if the image does not match expectations.
281 * @param inputFilename If we are writing out a binary image, use this to build its filename.
282 * @param jsonSummaryPtr If not null, add image results (checksum) to this summary.
283 * @param useChecksumBasedFilenames If true, use checksum-based filenames when writing to disk.
284 * @param tileNumberPtr If not null, which tile number this image contains.
285 *
286 * @return bool True if the operation completed successfully.
287 */
write(SkCanvas * canvas,const SkString & writePath,const SkString & mismatchPath,const SkString & inputFilename,ImageResultsAndExpectations * jsonSummaryPtr,bool useChecksumBasedFilenames,const int * tileNumberPtr=NULL)288 static bool write(SkCanvas* canvas, const SkString& writePath, const SkString& mismatchPath,
289 const SkString& inputFilename, ImageResultsAndExpectations *jsonSummaryPtr,
290 bool useChecksumBasedFilenames, const int* tileNumberPtr=NULL) {
291 SkASSERT(canvas != NULL);
292 if (NULL == canvas) {
293 return false;
294 }
295
296 SkBitmap bitmap;
297 SkISize size = canvas->getDeviceSize();
298 setup_bitmap(&bitmap, size.width(), size.height());
299
300 canvas->readPixels(&bitmap, 0, 0);
301 force_all_opaque(bitmap);
302 BitmapAndDigest bitmapAndDigest(bitmap);
303
304 SkString escapedInputFilename(inputFilename);
305 replace_char(&escapedInputFilename, '.', '_');
306
307 // TODO(epoger): what about including the config type within outputFilename? That way,
308 // we could combine results of different config types without conflicting filenames.
309 SkString outputFilename;
310 const char *outputSubdirPtr = NULL;
311 if (useChecksumBasedFilenames) {
312 ImageDigest *imageDigestPtr = bitmapAndDigest.getImageDigestPtr();
313 outputSubdirPtr = escapedInputFilename.c_str();
314 outputFilename.set(imageDigestPtr->getHashType());
315 outputFilename.append("_");
316 outputFilename.appendU64(imageDigestPtr->getHashValue());
317 } else {
318 outputFilename.set(escapedInputFilename);
319 if (tileNumberPtr) {
320 outputFilename.append("-tile");
321 outputFilename.appendS32(*tileNumberPtr);
322 }
323 }
324 outputFilename.append(".png");
325
326 if (jsonSummaryPtr) {
327 ImageDigest *imageDigestPtr = bitmapAndDigest.getImageDigestPtr();
328 SkString outputRelativePath;
329 if (outputSubdirPtr) {
330 outputRelativePath.set(outputSubdirPtr);
331 outputRelativePath.append("/"); // always use "/", even on Windows
332 outputRelativePath.append(outputFilename);
333 } else {
334 outputRelativePath.set(outputFilename);
335 }
336
337 jsonSummaryPtr->add(inputFilename.c_str(), outputRelativePath.c_str(),
338 *imageDigestPtr, tileNumberPtr);
339 if (!mismatchPath.isEmpty() &&
340 !jsonSummaryPtr->getExpectation(inputFilename.c_str(),
341 tileNumberPtr).matches(*imageDigestPtr)) {
342 if (!write_bitmap_to_disk(bitmap, mismatchPath, outputSubdirPtr, outputFilename)) {
343 return false;
344 }
345 }
346 }
347
348 if (writePath.isEmpty()) {
349 return true;
350 } else {
351 return write_bitmap_to_disk(bitmap, writePath, outputSubdirPtr, outputFilename);
352 }
353 }
354
355 ///////////////////////////////////////////////////////////////////////////////////////////////
356
setupCanvas(int width,int height)357 SkCanvas* RecordPictureRenderer::setupCanvas(int width, int height) {
358 // defer the canvas setup until the render step
359 return NULL;
360 }
361
render(SkBitmap ** out)362 bool RecordPictureRenderer::render(SkBitmap** out) {
363 SkAutoTDelete<SkBBHFactory> factory(this->getFactory());
364 SkPictureRecorder recorder;
365 SkCanvas* canvas = recorder.beginRecording(SkIntToScalar(this->getViewWidth()),
366 SkIntToScalar(this->getViewHeight()),
367 factory.get(),
368 this->recordFlags());
369 this->scaleToScaleFactor(canvas);
370 fPicture->playback(canvas);
371 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
372 if (!fWritePath.isEmpty()) {
373 // Record the new picture as a new SKP with PNG encoded bitmaps.
374 SkString skpPath = SkOSPath::Join(fWritePath.c_str(), fInputFilename.c_str());
375 SkFILEWStream stream(skpPath.c_str());
376 sk_tool_utils::PngPixelSerializer serializer;
377 picture->serialize(&stream, &serializer);
378 return true;
379 }
380 return false;
381 }
382
getConfigNameInternal()383 SkString RecordPictureRenderer::getConfigNameInternal() {
384 return SkString("record");
385 }
386
387 ///////////////////////////////////////////////////////////////////////////////////////////////
388
render(SkBitmap ** out)389 bool PipePictureRenderer::render(SkBitmap** out) {
390 SkASSERT(fCanvas.get() != NULL);
391 SkASSERT(fPicture != NULL);
392 if (NULL == fCanvas.get() || NULL == fPicture) {
393 return false;
394 }
395
396 PipeController pipeController(fCanvas.get());
397 SkGPipeWriter writer;
398 SkCanvas* pipeCanvas = writer.startRecording(&pipeController);
399 pipeCanvas->drawPicture(fPicture);
400 writer.endRecording();
401 fCanvas->flush();
402 if (out) {
403 *out = SkNEW(SkBitmap);
404 setup_bitmap(*out, SkScalarCeilToInt(fPicture->cullRect().width()),
405 SkScalarCeilToInt(fPicture->cullRect().height()));
406 fCanvas->readPixels(*out, 0, 0);
407 }
408 if (fEnableWrites) {
409 return write(fCanvas, fWritePath, fMismatchPath, fInputFilename, fJsonSummaryPtr,
410 fUseChecksumBasedFilenames);
411 } else {
412 return true;
413 }
414 }
415
getConfigNameInternal()416 SkString PipePictureRenderer::getConfigNameInternal() {
417 return SkString("pipe");
418 }
419
420 ///////////////////////////////////////////////////////////////////////////////////////////////
421
init(const SkPicture * picture,const SkString * writePath,const SkString * mismatchPath,const SkString * inputFilename,bool useChecksumBasedFilenames,bool useMultiPictureDraw)422 void SimplePictureRenderer::init(const SkPicture* picture, const SkString* writePath,
423 const SkString* mismatchPath, const SkString* inputFilename,
424 bool useChecksumBasedFilenames, bool useMultiPictureDraw) {
425 INHERITED::init(picture, writePath, mismatchPath, inputFilename,
426 useChecksumBasedFilenames, useMultiPictureDraw);
427 this->buildBBoxHierarchy();
428 }
429
render(SkBitmap ** out)430 bool SimplePictureRenderer::render(SkBitmap** out) {
431 SkASSERT(fCanvas.get() != NULL);
432 SkASSERT(fPicture);
433 if (NULL == fCanvas.get() || NULL == fPicture) {
434 return false;
435 }
436
437 if (fUseMultiPictureDraw) {
438 SkMultiPictureDraw mpd;
439
440 mpd.add(fCanvas, fPicture);
441
442 mpd.draw();
443 } else {
444 fCanvas->drawPicture(fPicture);
445 }
446 fCanvas->flush();
447 if (out) {
448 *out = SkNEW(SkBitmap);
449 setup_bitmap(*out, SkScalarCeilToInt(fPicture->cullRect().width()),
450 SkScalarCeilToInt(fPicture->cullRect().height()));
451 fCanvas->readPixels(*out, 0, 0);
452 }
453 if (fEnableWrites) {
454 return write(fCanvas, fWritePath, fMismatchPath, fInputFilename, fJsonSummaryPtr,
455 fUseChecksumBasedFilenames);
456 } else {
457 return true;
458 }
459 }
460
getConfigNameInternal()461 SkString SimplePictureRenderer::getConfigNameInternal() {
462 return SkString("simple");
463 }
464
465 ///////////////////////////////////////////////////////////////////////////////////////////////
466
467 #if SK_SUPPORT_GPU
TiledPictureRenderer(const GrContext::Options & opts)468 TiledPictureRenderer::TiledPictureRenderer(const GrContext::Options& opts)
469 : INHERITED(opts)
470 , fTileWidth(kDefaultTileWidth)
471 #else
472 TiledPictureRenderer::TiledPictureRenderer()
473 : fTileWidth(kDefaultTileWidth)
474 #endif
475 , fTileHeight(kDefaultTileHeight)
476 , fTileWidthPercentage(0.0)
477 , fTileHeightPercentage(0.0)
478 , fTileMinPowerOf2Width(0)
479 , fCurrentTileOffset(-1)
480 , fTilesX(0)
481 , fTilesY(0) { }
482
init(const SkPicture * pict,const SkString * writePath,const SkString * mismatchPath,const SkString * inputFilename,bool useChecksumBasedFilenames,bool useMultiPictureDraw)483 void TiledPictureRenderer::init(const SkPicture* pict, const SkString* writePath,
484 const SkString* mismatchPath, const SkString* inputFilename,
485 bool useChecksumBasedFilenames, bool useMultiPictureDraw) {
486 SkASSERT(pict);
487 SkASSERT(0 == fTileRects.count());
488 if (NULL == pict || fTileRects.count() != 0) {
489 return;
490 }
491
492 // Do not call INHERITED::init(), which would create a (potentially large) canvas which is not
493 // used by bench_pictures.
494 fPicture.reset(pict)->ref();
495 this->CopyString(&fWritePath, writePath);
496 this->CopyString(&fMismatchPath, mismatchPath);
497 this->CopyString(&fInputFilename, inputFilename);
498 fUseChecksumBasedFilenames = useChecksumBasedFilenames;
499 fUseMultiPictureDraw = useMultiPictureDraw;
500 this->buildBBoxHierarchy();
501
502 if (fTileWidthPercentage > 0) {
503 fTileWidth = SkScalarCeilToInt(float(fTileWidthPercentage * fPicture->cullRect().width() / 100));
504 }
505 if (fTileHeightPercentage > 0) {
506 fTileHeight = SkScalarCeilToInt(float(fTileHeightPercentage * fPicture->cullRect().height() / 100));
507 }
508
509 if (fTileMinPowerOf2Width > 0) {
510 this->setupPowerOf2Tiles();
511 } else {
512 this->setupTiles();
513 }
514 fCanvas.reset(this->setupCanvas(fTileWidth, fTileHeight));
515 // Initialize to -1 so that the first call to nextTile will set this up to draw tile 0 on the
516 // first call to drawCurrentTile.
517 fCurrentTileOffset = -1;
518 }
519
end()520 void TiledPictureRenderer::end() {
521 fTileRects.reset();
522 this->INHERITED::end();
523 }
524
setupTiles()525 void TiledPictureRenderer::setupTiles() {
526 // Only use enough tiles to cover the viewport
527 const int width = this->getViewWidth();
528 const int height = this->getViewHeight();
529
530 fTilesX = fTilesY = 0;
531 for (int tile_y_start = 0; tile_y_start < height; tile_y_start += fTileHeight) {
532 fTilesY++;
533 for (int tile_x_start = 0; tile_x_start < width; tile_x_start += fTileWidth) {
534 if (0 == tile_y_start) {
535 // Only count tiles in the X direction on the first pass.
536 fTilesX++;
537 }
538 *fTileRects.append() = SkIRect::MakeXYWH(tile_x_start, tile_y_start,
539 fTileWidth, fTileHeight);
540 }
541 }
542 }
543
tileDimensions(int & x,int & y)544 bool TiledPictureRenderer::tileDimensions(int &x, int &y) {
545 if (fTileRects.count() == 0 || NULL == fPicture) {
546 return false;
547 }
548 x = fTilesX;
549 y = fTilesY;
550 return true;
551 }
552
553 // The goal of the powers of two tiles is to minimize the amount of wasted tile
554 // space in the width-wise direction and then minimize the number of tiles. The
555 // constraints are that every tile must have a pixel width that is a power of
556 // two and also be of some minimal width (that is also a power of two).
557 //
558 // This is solved by first taking our picture size and rounding it up to the
559 // multiple of the minimal width. The binary representation of this rounded
560 // value gives us the tiles we need: a bit of value one means we need a tile of
561 // that size.
setupPowerOf2Tiles()562 void TiledPictureRenderer::setupPowerOf2Tiles() {
563 // Only use enough tiles to cover the viewport
564 const int width = this->getViewWidth();
565 const int height = this->getViewHeight();
566
567 int rounded_value = width;
568 if (width % fTileMinPowerOf2Width != 0) {
569 rounded_value = width - (width % fTileMinPowerOf2Width) + fTileMinPowerOf2Width;
570 }
571
572 int num_bits = SkScalarCeilToInt(scalar_log2(SkIntToScalar(width)));
573 int largest_possible_tile_size = 1 << num_bits;
574
575 fTilesX = fTilesY = 0;
576 // The tile height is constant for a particular picture.
577 for (int tile_y_start = 0; tile_y_start < height; tile_y_start += fTileHeight) {
578 fTilesY++;
579 int tile_x_start = 0;
580 int current_width = largest_possible_tile_size;
581 // Set fTileWidth to be the width of the widest tile, so that each canvas is large enough
582 // to draw each tile.
583 fTileWidth = current_width;
584
585 while (current_width >= fTileMinPowerOf2Width) {
586 // It is very important this is a bitwise AND.
587 if (current_width & rounded_value) {
588 if (0 == tile_y_start) {
589 // Only count tiles in the X direction on the first pass.
590 fTilesX++;
591 }
592 *fTileRects.append() = SkIRect::MakeXYWH(tile_x_start, tile_y_start,
593 current_width, fTileHeight);
594 tile_x_start += current_width;
595 }
596
597 current_width >>= 1;
598 }
599 }
600 }
601
602 /**
603 * Draw the specified picture to the canvas translated to rectangle provided, so that this mini
604 * canvas represents the rectangle's portion of the overall picture.
605 * Saves and restores so that the initial clip and matrix return to their state before this function
606 * is called.
607 */
draw_tile_to_canvas(SkCanvas * canvas,const SkIRect & tileRect,const SkPicture * picture)608 static void draw_tile_to_canvas(SkCanvas* canvas,
609 const SkIRect& tileRect,
610 const SkPicture* picture) {
611 int saveCount = canvas->save();
612 // Translate so that we draw the correct portion of the picture.
613 // Perform a postTranslate so that the scaleFactor does not interfere with the positioning.
614 SkMatrix mat(canvas->getTotalMatrix());
615 mat.postTranslate(-SkIntToScalar(tileRect.fLeft), -SkIntToScalar(tileRect.fTop));
616 canvas->setMatrix(mat);
617 canvas->clipRect(SkRect::Make(tileRect));
618 canvas->clear(SK_ColorTRANSPARENT); // Not every picture covers the entirety of every tile
619 canvas->drawPicture(picture);
620 canvas->restoreToCount(saveCount);
621 canvas->flush();
622 }
623
624 ///////////////////////////////////////////////////////////////////////////////////////////////
625
626 /**
627 * Copies the entirety of the src bitmap (typically a tile) into a portion of the dst bitmap.
628 * If the src bitmap is too large to fit within the dst bitmap after the x and y
629 * offsets have been applied, any excess will be ignored (so only the top-left portion of the
630 * src bitmap will be copied).
631 *
632 * @param src source bitmap
633 * @param dst destination bitmap
634 * @param xOffset x-offset within destination bitmap
635 * @param yOffset y-offset within destination bitmap
636 */
bitmapCopyAtOffset(const SkBitmap & src,SkBitmap * dst,int xOffset,int yOffset)637 static void bitmapCopyAtOffset(const SkBitmap& src, SkBitmap* dst,
638 int xOffset, int yOffset) {
639 for (int y = 0; y <src.height() && y + yOffset < dst->height() ; y++) {
640 for (int x = 0; x < src.width() && x + xOffset < dst->width() ; x++) {
641 *dst->getAddr32(xOffset + x, yOffset + y) = *src.getAddr32(x, y);
642 }
643 }
644 }
645
nextTile(int & i,int & j)646 bool TiledPictureRenderer::nextTile(int &i, int &j) {
647 if (++fCurrentTileOffset < fTileRects.count()) {
648 i = fCurrentTileOffset % fTilesX;
649 j = fCurrentTileOffset / fTilesX;
650 return true;
651 }
652 return false;
653 }
654
drawCurrentTile()655 void TiledPictureRenderer::drawCurrentTile() {
656 SkASSERT(fCurrentTileOffset >= 0 && fCurrentTileOffset < fTileRects.count());
657 draw_tile_to_canvas(fCanvas, fTileRects[fCurrentTileOffset], fPicture);
658 }
659
postRender(SkCanvas * canvas,const SkIRect & tileRect,SkBitmap * tempBM,SkBitmap ** out,int tileNumber)660 bool TiledPictureRenderer::postRender(SkCanvas* canvas, const SkIRect& tileRect,
661 SkBitmap* tempBM, SkBitmap** out,
662 int tileNumber) {
663 bool success = true;
664
665 if (fEnableWrites) {
666 success &= write(canvas, fWritePath, fMismatchPath, fInputFilename, fJsonSummaryPtr,
667 fUseChecksumBasedFilenames, &tileNumber);
668 }
669 if (out) {
670 if (canvas->readPixels(tempBM, 0, 0)) {
671 // Add this tile to the entire bitmap.
672 bitmapCopyAtOffset(*tempBM, *out, tileRect.left(), tileRect.top());
673 } else {
674 success = false;
675 }
676 }
677
678 return success;
679 }
680
render(SkBitmap ** out)681 bool TiledPictureRenderer::render(SkBitmap** out) {
682 SkASSERT(fPicture != NULL);
683 if (NULL == fPicture) {
684 return false;
685 }
686
687 SkBitmap bitmap;
688 if (out) {
689 *out = SkNEW(SkBitmap);
690 setup_bitmap(*out, SkScalarCeilToInt(fPicture->cullRect().width()),
691 SkScalarCeilToInt(fPicture->cullRect().height()));
692 setup_bitmap(&bitmap, fTileWidth, fTileHeight);
693 }
694 bool success = true;
695
696 if (fUseMultiPictureDraw) {
697 SkMultiPictureDraw mpd;
698 SkTDArray<SkSurface*> surfaces;
699 surfaces.setReserve(fTileRects.count());
700
701 // Create a separate SkSurface/SkCanvas for each tile along with a
702 // translated version of the skp (to mimic Chrome's behavior) and
703 // feed all such pairs to the MultiPictureDraw.
704 for (int i = 0; i < fTileRects.count(); ++i) {
705 SkImageInfo ii = fCanvas->imageInfo().makeWH(fTileRects[i].width(),
706 fTileRects[i].height());
707 *surfaces.append() = fCanvas->newSurface(ii);
708 surfaces[i]->getCanvas()->setMatrix(fCanvas->getTotalMatrix());
709
710 SkPictureRecorder recorder;
711 SkRTreeFactory bbhFactory;
712
713 SkCanvas* c = recorder.beginRecording(SkIntToScalar(fTileRects[i].width()),
714 SkIntToScalar(fTileRects[i].height()),
715 &bbhFactory,
716 SkPictureRecorder::kComputeSaveLayerInfo_RecordFlag);
717 c->save();
718 SkMatrix mat;
719 mat.setTranslate(-SkIntToScalar(fTileRects[i].fLeft),
720 -SkIntToScalar(fTileRects[i].fTop));
721 c->setMatrix(mat);
722 c->drawPicture(fPicture);
723 c->restore();
724
725 SkAutoTUnref<SkPicture> xlatedPicture(recorder.endRecording());
726
727 mpd.add(surfaces[i]->getCanvas(), xlatedPicture);
728 }
729
730 // Render all the buffered SkCanvases/SkPictures
731 mpd.draw();
732
733 // Sort out the results and cleanup the allocated surfaces
734 for (int i = 0; i < fTileRects.count(); ++i) {
735 success &= this->postRender(surfaces[i]->getCanvas(), fTileRects[i], &bitmap, out, i);
736 surfaces[i]->unref();
737 }
738 } else {
739 for (int i = 0; i < fTileRects.count(); ++i) {
740 draw_tile_to_canvas(fCanvas, fTileRects[i], fPicture);
741 success &= this->postRender(fCanvas, fTileRects[i], &bitmap, out, i);
742 }
743 }
744
745 return success;
746 }
747
setupCanvas(int width,int height)748 SkCanvas* TiledPictureRenderer::setupCanvas(int width, int height) {
749 SkCanvas* canvas = this->INHERITED::setupCanvas(width, height);
750 SkASSERT(fPicture);
751 // Clip the tile to an area that is completely inside both the SkPicture and the viewport. This
752 // is mostly important for tiles on the right and bottom edges as they may go over this area and
753 // the picture may have some commands that draw outside of this area and so should not actually
754 // be written.
755 // Uses a clipRegion so that it will be unaffected by the scale factor, which may have been set
756 // by INHERITED::setupCanvas.
757 SkRegion clipRegion;
758 clipRegion.setRect(0, 0, this->getViewWidth(), this->getViewHeight());
759 canvas->clipRegion(clipRegion);
760 return canvas;
761 }
762
getConfigNameInternal()763 SkString TiledPictureRenderer::getConfigNameInternal() {
764 SkString name;
765 if (fTileMinPowerOf2Width > 0) {
766 name.append("pow2tile_");
767 name.appendf("%i", fTileMinPowerOf2Width);
768 } else {
769 name.append("tile_");
770 if (fTileWidthPercentage > 0) {
771 name.appendf("%.f%%", fTileWidthPercentage);
772 } else {
773 name.appendf("%i", fTileWidth);
774 }
775 }
776 name.append("x");
777 if (fTileHeightPercentage > 0) {
778 name.appendf("%.f%%", fTileHeightPercentage);
779 } else {
780 name.appendf("%i", fTileHeight);
781 }
782 return name;
783 }
784
785 ///////////////////////////////////////////////////////////////////////////////////////////////
786
setup()787 void PlaybackCreationRenderer::setup() {
788 SkAutoTDelete<SkBBHFactory> factory(this->getFactory());
789 fRecorder.reset(SkNEW(SkPictureRecorder));
790 SkCanvas* canvas = fRecorder->beginRecording(SkIntToScalar(this->getViewWidth()),
791 SkIntToScalar(this->getViewHeight()),
792 factory.get(),
793 this->recordFlags());
794 this->scaleToScaleFactor(canvas);
795 canvas->drawPicture(fPicture);
796 }
797
render(SkBitmap ** out)798 bool PlaybackCreationRenderer::render(SkBitmap** out) {
799 fPicture.reset(fRecorder->endRecording());
800 // Since this class does not actually render, return false.
801 return false;
802 }
803
getConfigNameInternal()804 SkString PlaybackCreationRenderer::getConfigNameInternal() {
805 return SkString("playback_creation");
806 }
807
808 ///////////////////////////////////////////////////////////////////////////////////////////////
809 // SkPicture variants for each BBoxHierarchy type
810
getFactory()811 SkBBHFactory* PictureRenderer::getFactory() {
812 switch (fBBoxHierarchyType) {
813 case kNone_BBoxHierarchyType:
814 return NULL;
815 case kRTree_BBoxHierarchyType:
816 return SkNEW(SkRTreeFactory);
817 }
818 SkASSERT(0); // invalid bbhType
819 return NULL;
820 }
821
822 } // namespace sk_tools
823