PictureRenderer.cpp revision a04dc02b118363fedf3a7b11cfcdab886d368f8a
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 "SkCanvas.h" 12#include "SkDevice.h" 13#include "SkGPipe.h" 14#if SK_SUPPORT_GPU 15#include "SkGpuDevice.h" 16#endif 17#include "SkGraphics.h" 18#include "SkImageEncoder.h" 19#include "SkMatrix.h" 20#include "SkPicture.h" 21#include "SkScalar.h" 22#include "SkString.h" 23#include "SkTemplates.h" 24#include "SkTDArray.h" 25#include "SkThreadUtils.h" 26#include "SkTypes.h" 27 28namespace sk_tools { 29 30enum { 31 kDefaultTileWidth = 256, 32 kDefaultTileHeight = 256 33}; 34 35void PictureRenderer::init(SkPicture* pict) { 36 SkASSERT(NULL == fPicture); 37 SkASSERT(NULL == fCanvas.get()); 38 if (fPicture != NULL || NULL != fCanvas.get()) { 39 return; 40 } 41 42 SkASSERT(pict != NULL); 43 if (NULL == pict) { 44 return; 45 } 46 47 fPicture = pict; 48 fCanvas.reset(this->setupCanvas()); 49} 50 51SkCanvas* PictureRenderer::setupCanvas() { 52 return this->setupCanvas(fPicture->width(), fPicture->height()); 53} 54 55SkCanvas* PictureRenderer::setupCanvas(int width, int height) { 56 switch(fDeviceType) { 57 case kBitmap_DeviceType: { 58 SkBitmap bitmap; 59 sk_tools::setup_bitmap(&bitmap, width, height); 60 return SkNEW_ARGS(SkCanvas, (bitmap)); 61 break; 62 } 63#if SK_SUPPORT_GPU 64 case kGPU_DeviceType: { 65 SkAutoTUnref<SkGpuDevice> device(SkNEW_ARGS(SkGpuDevice, 66 (fGrContext, SkBitmap::kARGB_8888_Config, 67 width, height))); 68 return SkNEW_ARGS(SkCanvas, (device.get())); 69 break; 70 } 71#endif 72 default: 73 SkASSERT(0); 74 } 75 76 return NULL; 77} 78 79void PictureRenderer::end() { 80 this->resetState(); 81 fPicture = NULL; 82 fCanvas.reset(NULL); 83} 84 85void PictureRenderer::resetState() { 86#if SK_SUPPORT_GPU 87 if (this->isUsingGpuDevice()) { 88 SkGLContext* glContext = fGrContextFactory.getGLContext( 89 GrContextFactory::kNative_GLContextType); 90 91 SkASSERT(glContext != NULL); 92 if (NULL == glContext) { 93 return; 94 } 95 96 fGrContext->flush(); 97 SK_GL(*glContext, Finish()); 98 } 99#endif 100} 101 102bool PictureRenderer::write(const SkString& path) const { 103 SkASSERT(fCanvas.get() != NULL); 104 SkASSERT(fPicture != NULL); 105 if (NULL == fCanvas.get() || NULL == fPicture) { 106 return false; 107 } 108 109 SkBitmap bitmap; 110 sk_tools::setup_bitmap(&bitmap, fPicture->width(), fPicture->height()); 111 112 fCanvas->readPixels(&bitmap, 0, 0); 113 sk_tools::force_all_opaque(bitmap); 114 115 return SkImageEncoder::EncodeFile(path.c_str(), bitmap, SkImageEncoder::kPNG_Type, 100); 116} 117 118void RecordPictureRenderer::render(bool doExtraWorkToDrawToBaseCanvas) { 119 SkPicture replayer; 120 SkCanvas* recorder = replayer.beginRecording(fPicture->width(), fPicture->height()); 121 fPicture->draw(recorder); 122 replayer.endRecording(); 123} 124 125void PipePictureRenderer::render(bool doExtraWorkToDrawToBaseCanvas) { 126 SkASSERT(fCanvas.get() != NULL); 127 SkASSERT(fPicture != NULL); 128 if (NULL == fCanvas.get() || NULL == fPicture) { 129 return; 130 } 131 132 PipeController pipeController(fCanvas.get()); 133 SkGPipeWriter writer; 134 SkCanvas* pipeCanvas = writer.startRecording(&pipeController); 135 pipeCanvas->drawPicture(*fPicture); 136 writer.endRecording(); 137 fCanvas->flush(); 138} 139 140void SimplePictureRenderer::render(bool doExtraWorkToDrawToBaseCanvas) { 141 SkASSERT(fCanvas.get() != NULL); 142 SkASSERT(fPicture != NULL); 143 if (NULL == fCanvas.get() || NULL == fPicture) { 144 return; 145 } 146 147 fCanvas->drawPicture(*fPicture); 148 fCanvas->flush(); 149} 150 151TiledPictureRenderer::TiledPictureRenderer() 152 : fMultiThreaded(false) 153 , fUsePipe(false) 154 , fTileWidth(kDefaultTileWidth) 155 , fTileHeight(kDefaultTileHeight) 156 , fTileWidthPercentage(0.0) 157 , fTileHeightPercentage(0.0) 158 , fTileMinPowerOf2Width(0) { } 159 160void TiledPictureRenderer::init(SkPicture* pict) { 161 SkASSERT(pict != NULL); 162 SkASSERT(0 == fTiles.count()); 163 if (NULL == pict || fTiles.count() != 0) { 164 return; 165 } 166 167 this->INHERITED::init(pict); 168 169 if (fTileWidthPercentage > 0) { 170 fTileWidth = sk_float_ceil2int(float(fTileWidthPercentage * fPicture->width() / 100)); 171 } 172 if (fTileHeightPercentage > 0) { 173 fTileHeight = sk_float_ceil2int(float(fTileHeightPercentage * fPicture->height() / 100)); 174 } 175 176 if (fTileMinPowerOf2Width > 0) { 177 this->setupPowerOf2Tiles(); 178 } else { 179 this->setupTiles(); 180 } 181} 182 183void TiledPictureRenderer::render(bool doExtraWorkToDrawToBaseCanvas) { 184 SkASSERT(fCanvas.get() != NULL); 185 SkASSERT(fPicture != NULL); 186 if (NULL == fCanvas.get() || NULL == fPicture) { 187 return; 188 } 189 190 this->drawTiles(); 191 if (doExtraWorkToDrawToBaseCanvas) { 192 this->copyTilesToCanvas(); 193 } 194} 195 196void TiledPictureRenderer::end() { 197 this->deleteTiles(); 198 this->INHERITED::end(); 199} 200 201TiledPictureRenderer::~TiledPictureRenderer() { 202 this->deleteTiles(); 203} 204 205void TiledPictureRenderer::clipTile(SkCanvas* tile) { 206 SkRect clip = SkRect::MakeWH(SkIntToScalar(fPicture->width()), 207 SkIntToScalar(fPicture->height())); 208 tile->clipRect(clip); 209} 210 211void TiledPictureRenderer::addTile(int tile_x_start, int tile_y_start, int width, int height) { 212 SkCanvas* tile = this->setupCanvas(width, height); 213 214 tile->translate(SkIntToScalar(-tile_x_start), SkIntToScalar(-tile_y_start)); 215 this->clipTile(tile); 216 217 fTiles.push(tile); 218} 219 220void TiledPictureRenderer::setupTiles() { 221 for (int tile_y_start = 0; tile_y_start < fPicture->height(); 222 tile_y_start += fTileHeight) { 223 for (int tile_x_start = 0; tile_x_start < fPicture->width(); 224 tile_x_start += fTileWidth) { 225 this->addTile(tile_x_start, tile_y_start, fTileWidth, fTileHeight); 226 } 227 } 228} 229 230// The goal of the powers of two tiles is to minimize the amount of wasted tile 231// space in the width-wise direction and then minimize the number of tiles. The 232// constraints are that every tile must have a pixel width that is a power of 233// two and also be of some minimal width (that is also a power of two). 234// 235// This is solved by first taking our picture size and rounding it up to the 236// multiple of the minimal width. The binary representation of this rounded 237// value gives us the tiles we need: a bit of value one means we need a tile of 238// that size. 239void TiledPictureRenderer::setupPowerOf2Tiles() { 240 int rounded_value = fPicture->width(); 241 if (fPicture->width() % fTileMinPowerOf2Width != 0) { 242 rounded_value = fPicture->width() - (fPicture->width() % fTileMinPowerOf2Width) 243 + fTileMinPowerOf2Width; 244 } 245 246 int num_bits = SkScalarCeilToInt(SkScalarLog2(SkIntToScalar(fPicture->width()))); 247 int largest_possible_tile_size = 1 << num_bits; 248 249 // The tile height is constant for a particular picture. 250 for (int tile_y_start = 0; tile_y_start < fPicture->height(); tile_y_start += fTileHeight) { 251 int tile_x_start = 0; 252 int current_width = largest_possible_tile_size; 253 254 while (current_width >= fTileMinPowerOf2Width) { 255 // It is very important this is a bitwise AND. 256 if (current_width & rounded_value) { 257 this->addTile(tile_x_start, tile_y_start, current_width, fTileHeight); 258 tile_x_start += current_width; 259 } 260 261 current_width >>= 1; 262 } 263 } 264} 265 266void TiledPictureRenderer::deleteTiles() { 267 for (int i = 0; i < fTiles.count(); ++i) { 268 SkDELETE(fTiles[i]); 269 } 270 271 fTiles.reset(); 272} 273 274/////////////////////////////////////////////////////////////////////////////////////////////// 275// Draw using Pipe 276 277struct TileData { 278 TileData(SkCanvas* canvas, ThreadSafePipeController* controller); 279 SkCanvas* fCanvas; 280 ThreadSafePipeController* fController; 281 SkThread fThread; 282}; 283 284static void DrawTile(void* data) { 285 SkGraphics::SetTLSFontCacheLimit(1 * 1024 * 1024); 286 TileData* tileData = static_cast<TileData*>(data); 287 tileData->fController->playback(tileData->fCanvas); 288 tileData->fCanvas->flush(); 289} 290 291TileData::TileData(SkCanvas* canvas, ThreadSafePipeController* controller) 292: fCanvas(canvas) 293, fController(controller) 294, fThread(&DrawTile, static_cast<void*>(this)) {} 295 296/////////////////////////////////////////////////////////////////////////////////////////////// 297// Draw using Picture 298 299struct CloneData { 300 CloneData(SkCanvas* target, SkPicture* original); 301 SkCanvas* fCanvas; 302 SkPicture* fClone; 303 SkThread fThread; 304}; 305 306static void DrawClonedTile(void* data) { 307 SkGraphics::SetTLSFontCacheLimit(1 * 1024 * 1024); 308 CloneData* cloneData = static_cast<CloneData*>(data); 309 cloneData->fCanvas->drawPicture(*cloneData->fClone); 310 cloneData->fCanvas->flush(); 311} 312 313CloneData::CloneData(SkCanvas* target, SkPicture* clone) 314: fCanvas(target) 315, fClone(clone) 316, fThread(&DrawClonedTile, static_cast<void*>(this)) {} 317 318/////////////////////////////////////////////////////////////////////////////////////////////// 319 320void TiledPictureRenderer::drawTiles() { 321 if (fMultiThreaded) { 322 if (fUsePipe) { 323 // First, draw into a pipe controller 324 SkGPipeWriter writer; 325 ThreadSafePipeController controller(fTiles.count()); 326 SkCanvas* pipeCanvas = writer.startRecording(&controller, 327 SkGPipeWriter::kSimultaneousReaders_Flag); 328 pipeCanvas->drawPicture(*(fPicture)); 329 writer.endRecording(); 330 331 // Create and start the threads. 332 TileData** tileData = SkNEW_ARRAY(TileData*, fTiles.count()); 333 SkAutoTDeleteArray<TileData*> deleteTileData(tileData); 334 for (int i = 0; i < fTiles.count(); i++) { 335 tileData[i] = SkNEW_ARGS(TileData, (fTiles[i], &controller)); 336 if (!tileData[i]->fThread.start()) { 337 SkDebugf("could not start thread %i\n", i); 338 } 339 } 340 for (int i = 0; i < fTiles.count(); i++) { 341 tileData[i]->fThread.join(); 342 SkDELETE(tileData[i]); 343 } 344 } else { 345 SkPicture* clones = SkNEW_ARRAY(SkPicture, fTiles.count()); 346 SkAutoTDeleteArray<SkPicture> autodelete(clones); 347 fPicture->clone(clones, fTiles.count()); 348 CloneData** cloneData = SkNEW_ARRAY(CloneData*, fTiles.count()); 349 SkAutoTDeleteArray<CloneData*> deleteCloneData(cloneData); 350 for (int i = 0; i < fTiles.count(); i++) { 351 cloneData[i] = SkNEW_ARGS(CloneData, (fTiles[i], &clones[i])); 352 if (!cloneData[i]->fThread.start()) { 353 SkDebugf("Could not start picture thread %i\n", i); 354 } 355 } 356 for (int i = 0; i < fTiles.count(); i++) { 357 cloneData[i]->fThread.join(); 358 SkDELETE(cloneData[i]); 359 } 360 } 361 } else { 362 for (int i = 0; i < fTiles.count(); ++i) { 363 fTiles[i]->drawPicture(*(fPicture)); 364 fTiles[i]->flush(); 365 } 366 } 367} 368 369void TiledPictureRenderer::copyTilesToCanvas() { 370 for (int i = 0; i < fTiles.count(); ++i) { 371 // Since SkPicture performs a save and restore when being drawn to a 372 // canvas, we can be confident that the transform matrix of the canvas 373 // is what we set when creating the tiles. 374 SkMatrix matrix = fTiles[i]->getTotalMatrix(); 375 SkScalar tile_x_start = matrix.getTranslateX(); 376 SkScalar tile_y_start = matrix.getTranslateY(); 377 378 SkBitmap source = fTiles[i]->getDevice()->accessBitmap(false); 379 380 fCanvas->drawBitmap(source, -tile_x_start, -tile_y_start); 381 } 382 fCanvas->flush(); 383} 384 385void PlaybackCreationRenderer::setup() { 386 SkCanvas* recorder = fReplayer.beginRecording(fPicture->width(), fPicture->height()); 387 fPicture->draw(recorder); 388} 389 390void PlaybackCreationRenderer::render(bool doExtraWorkToDrawToBaseCanvas) { 391 fReplayer.endRecording(); 392} 393 394} 395