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