| keyar@chromium.org | b3fb7c1 | 2012-08-20 21:02:49 +0000 | [diff] [blame] | 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 |  | 
| keyar@chromium.org | 451bb9f | 2012-07-26 17:27:57 +0000 | [diff] [blame] | 8 | #include "PictureRenderer.h" | 
| scroggo@google.com | 58b4ead | 2012-08-31 16:15:22 +0000 | [diff] [blame] | 9 | #include "picture_utils.h" | 
| keyar@chromium.org | 451bb9f | 2012-07-26 17:27:57 +0000 | [diff] [blame] | 10 | #include "SamplePipeControllers.h" | 
 | 11 | #include "SkCanvas.h" | 
 | 12 | #include "SkDevice.h" | 
 | 13 | #include "SkGPipe.h" | 
| scroggo@google.com | 58b4ead | 2012-08-31 16:15:22 +0000 | [diff] [blame] | 14 | #if SK_SUPPORT_GPU | 
 | 15 | #include "SkGpuDevice.h" | 
 | 16 | #endif | 
 | 17 | #include "SkGraphics.h" | 
 | 18 | #include "SkImageEncoder.h" | 
| keyar@chromium.org | ea82695 | 2012-08-23 15:24:13 +0000 | [diff] [blame] | 19 | #include "SkMatrix.h" | 
| keyar@chromium.org | 451bb9f | 2012-07-26 17:27:57 +0000 | [diff] [blame] | 20 | #include "SkPicture.h" | 
| junov@chromium.org | 9313ca4 | 2012-11-02 18:11:49 +0000 | [diff] [blame] | 21 | #include "SkRTree.h" | 
| keyar@chromium.org | ea82695 | 2012-08-23 15:24:13 +0000 | [diff] [blame] | 22 | #include "SkScalar.h" | 
| keyar@chromium.org | 9299ede | 2012-08-21 19:05:08 +0000 | [diff] [blame] | 23 | #include "SkString.h" | 
| scroggo@google.com | 58b4ead | 2012-08-31 16:15:22 +0000 | [diff] [blame] | 24 | #include "SkTemplates.h" | 
| keyar@chromium.org | 451bb9f | 2012-07-26 17:27:57 +0000 | [diff] [blame] | 25 | #include "SkTDArray.h" | 
| scroggo@google.com | 58b4ead | 2012-08-31 16:15:22 +0000 | [diff] [blame] | 26 | #include "SkThreadUtils.h" | 
| keyar@chromium.org | 451bb9f | 2012-07-26 17:27:57 +0000 | [diff] [blame] | 27 | #include "SkTypes.h" | 
| keyar@chromium.org | 4ea96c5 | 2012-08-20 15:03:29 +0000 | [diff] [blame] | 28 |  | 
| keyar@chromium.org | 451bb9f | 2012-07-26 17:27:57 +0000 | [diff] [blame] | 29 | namespace sk_tools { | 
 | 30 |  | 
 | 31 | enum { | 
 | 32 |     kDefaultTileWidth = 256, | 
 | 33 |     kDefaultTileHeight = 256 | 
 | 34 | }; | 
 | 35 |  | 
| keyar@chromium.org | 9d696c0 | 2012-08-07 17:11:33 +0000 | [diff] [blame] | 36 | void PictureRenderer::init(SkPicture* pict) { | 
| keyar@chromium.org | 78a35c5 | 2012-08-20 15:03:44 +0000 | [diff] [blame] | 37 |     SkASSERT(NULL == fPicture); | 
 | 38 |     SkASSERT(NULL == fCanvas.get()); | 
 | 39 |     if (fPicture != NULL || NULL != fCanvas.get()) { | 
| keyar@chromium.org | 9d696c0 | 2012-08-07 17:11:33 +0000 | [diff] [blame] | 40 |         return; | 
 | 41 |     } | 
 | 42 |  | 
 | 43 |     SkASSERT(pict != NULL); | 
| keyar@chromium.org | 78a35c5 | 2012-08-20 15:03:44 +0000 | [diff] [blame] | 44 |     if (NULL == pict) { | 
| keyar@chromium.org | 9d696c0 | 2012-08-07 17:11:33 +0000 | [diff] [blame] | 45 |         return; | 
 | 46 |     } | 
 | 47 |  | 
 | 48 |     fPicture = pict; | 
| junov@chromium.org | 9313ca4 | 2012-11-02 18:11:49 +0000 | [diff] [blame] | 49 |     fPicture->ref(); | 
| keyar@chromium.org | a474ce3 | 2012-08-20 15:03:57 +0000 | [diff] [blame] | 50 |     fCanvas.reset(this->setupCanvas()); | 
 | 51 | } | 
 | 52 |  | 
 | 53 | SkCanvas* PictureRenderer::setupCanvas() { | 
 | 54 |     return this->setupCanvas(fPicture->width(), fPicture->height()); | 
 | 55 | } | 
 | 56 |  | 
 | 57 | SkCanvas* PictureRenderer::setupCanvas(int width, int height) { | 
| keyar@chromium.org | 4ea96c5 | 2012-08-20 15:03:29 +0000 | [diff] [blame] | 58 |     switch(fDeviceType) { | 
 | 59 |         case kBitmap_DeviceType: { | 
 | 60 |             SkBitmap bitmap; | 
| keyar@chromium.org | a474ce3 | 2012-08-20 15:03:57 +0000 | [diff] [blame] | 61 |             sk_tools::setup_bitmap(&bitmap, width, height); | 
 | 62 |             return SkNEW_ARGS(SkCanvas, (bitmap)); | 
| keyar@chromium.org | 4ea96c5 | 2012-08-20 15:03:29 +0000 | [diff] [blame] | 63 |             break; | 
 | 64 |         } | 
 | 65 | #if SK_SUPPORT_GPU | 
 | 66 |         case kGPU_DeviceType: { | 
| keyar@chromium.org | 4ea96c5 | 2012-08-20 15:03:29 +0000 | [diff] [blame] | 67 |             SkAutoTUnref<SkGpuDevice> device(SkNEW_ARGS(SkGpuDevice, | 
| keyar@chromium.org | 0612564 | 2012-08-20 15:03:33 +0000 | [diff] [blame] | 68 |                                                     (fGrContext, SkBitmap::kARGB_8888_Config, | 
| keyar@chromium.org | a474ce3 | 2012-08-20 15:03:57 +0000 | [diff] [blame] | 69 |                                                     width, height))); | 
 | 70 |             return SkNEW_ARGS(SkCanvas, (device.get())); | 
| keyar@chromium.org | 4ea96c5 | 2012-08-20 15:03:29 +0000 | [diff] [blame] | 71 |             break; | 
 | 72 |         } | 
 | 73 | #endif | 
 | 74 |         default: | 
 | 75 |             SkASSERT(0); | 
 | 76 |     } | 
| keyar@chromium.org | a474ce3 | 2012-08-20 15:03:57 +0000 | [diff] [blame] | 77 |  | 
 | 78 |     return NULL; | 
| keyar@chromium.org | 9d696c0 | 2012-08-07 17:11:33 +0000 | [diff] [blame] | 79 | } | 
 | 80 |  | 
 | 81 | void PictureRenderer::end() { | 
| keyar@chromium.org | 77a5522 | 2012-08-20 15:03:47 +0000 | [diff] [blame] | 82 |     this->resetState(); | 
| junov@chromium.org | 9313ca4 | 2012-11-02 18:11:49 +0000 | [diff] [blame] | 83 |     SkSafeUnref(fPicture); | 
| keyar@chromium.org | 9d696c0 | 2012-08-07 17:11:33 +0000 | [diff] [blame] | 84 |     fPicture = NULL; | 
 | 85 |     fCanvas.reset(NULL); | 
 | 86 | } | 
 | 87 |  | 
| junov@chromium.org | 9313ca4 | 2012-11-02 18:11:49 +0000 | [diff] [blame] | 88 | /** Converts fPicture to a picture that uses a BBoxHierarchy. | 
 | 89 |  *  PictureRenderer subclasses that are used to test picture playback | 
 | 90 |  *  should call this method during init. | 
 | 91 |  */ | 
 | 92 | void PictureRenderer::buildBBoxHierarchy() { | 
 | 93 |     SkASSERT(NULL != fPicture); | 
 | 94 |     if (kNone_BBoxHierarchyType != fBBoxHierarchyType && NULL != fPicture) { | 
 | 95 |         SkPicture* newPicture = this->createPicture(); | 
 | 96 |         SkCanvas* recorder = newPicture->beginRecording(fPicture->width(), fPicture->height(), | 
 | 97 |                                                         this->recordFlags()); | 
 | 98 |         fPicture->draw(recorder); | 
 | 99 |         newPicture->endRecording(); | 
 | 100 |         fPicture->unref(); | 
 | 101 |         fPicture = newPicture; | 
 | 102 |     } | 
 | 103 | } | 
 | 104 |  | 
| keyar@chromium.org | 77a5522 | 2012-08-20 15:03:47 +0000 | [diff] [blame] | 105 | void PictureRenderer::resetState() { | 
| keyar@chromium.org | 28136b3 | 2012-08-20 15:04:15 +0000 | [diff] [blame] | 106 | #if SK_SUPPORT_GPU | 
 | 107 |     if (this->isUsingGpuDevice()) { | 
 | 108 |         SkGLContext* glContext = fGrContextFactory.getGLContext( | 
 | 109 |             GrContextFactory::kNative_GLContextType); | 
| keyar@chromium.org | 28136b3 | 2012-08-20 15:04:15 +0000 | [diff] [blame] | 110 |  | 
 | 111 |         SkASSERT(glContext != NULL); | 
 | 112 |         if (NULL == glContext) { | 
 | 113 |             return; | 
 | 114 |         } | 
 | 115 |  | 
| scroggo@google.com | 9a41252 | 2012-09-07 15:21:18 +0000 | [diff] [blame] | 116 |         fGrContext->flush(); | 
| keyar@chromium.org | 77a5522 | 2012-08-20 15:03:47 +0000 | [diff] [blame] | 117 |         SK_GL(*glContext, Finish()); | 
| keyar@chromium.org | 77a5522 | 2012-08-20 15:03:47 +0000 | [diff] [blame] | 118 |     } | 
| keyar@chromium.org | a40c20d | 2012-08-20 15:04:12 +0000 | [diff] [blame] | 119 | #endif | 
| keyar@chromium.org | 77a5522 | 2012-08-20 15:03:47 +0000 | [diff] [blame] | 120 | } | 
 | 121 |  | 
| junov@chromium.org | 9313ca4 | 2012-11-02 18:11:49 +0000 | [diff] [blame] | 122 | uint32_t PictureRenderer::recordFlags() { | 
 | 123 |     return kNone_BBoxHierarchyType == fBBoxHierarchyType ? 0 : | 
 | 124 |         SkPicture::kOptimizeForClippedPlayback_RecordingFlag; | 
 | 125 | } | 
 | 126 |  | 
| scroggo@google.com | b6e806b | 2012-10-03 17:32:33 +0000 | [diff] [blame] | 127 | /** | 
 | 128 |  * Write the canvas to the specified path. | 
 | 129 |  * @param canvas Must be non-null. Canvas to be written to a file. | 
 | 130 |  * @param path Path for the file to be written. Should have no extension; write() will append | 
 | 131 |  *             an appropriate one. Passed in by value so it can be modified. | 
 | 132 |  * @return bool True if the Canvas is written to a file. | 
 | 133 |  */ | 
 | 134 | static bool write(SkCanvas* canvas, SkString path) { | 
| scroggo@google.com | 81f9d2e | 2012-09-20 14:54:21 +0000 | [diff] [blame] | 135 |     SkASSERT(canvas != NULL); | 
| scroggo@google.com | b6e806b | 2012-10-03 17:32:33 +0000 | [diff] [blame] | 136 |     if (NULL == canvas) { | 
| keyar@chromium.org | 9299ede | 2012-08-21 19:05:08 +0000 | [diff] [blame] | 137 |         return false; | 
 | 138 |     } | 
 | 139 |  | 
 | 140 |     SkBitmap bitmap; | 
| scroggo@google.com | 81f9d2e | 2012-09-20 14:54:21 +0000 | [diff] [blame] | 141 |     SkISize size = canvas->getDeviceSize(); | 
 | 142 |     sk_tools::setup_bitmap(&bitmap, size.width(), size.height()); | 
| keyar@chromium.org | 9299ede | 2012-08-21 19:05:08 +0000 | [diff] [blame] | 143 |  | 
| scroggo@google.com | 81f9d2e | 2012-09-20 14:54:21 +0000 | [diff] [blame] | 144 |     canvas->readPixels(&bitmap, 0, 0); | 
| keyar@chromium.org | 9299ede | 2012-08-21 19:05:08 +0000 | [diff] [blame] | 145 |     sk_tools::force_all_opaque(bitmap); | 
 | 146 |  | 
| scroggo@google.com | 81f9d2e | 2012-09-20 14:54:21 +0000 | [diff] [blame] | 147 |     // Since path is passed in by value, it is okay to modify it. | 
 | 148 |     path.append(".png"); | 
| keyar@chromium.org | 9299ede | 2012-08-21 19:05:08 +0000 | [diff] [blame] | 149 |     return SkImageEncoder::EncodeFile(path.c_str(), bitmap, SkImageEncoder::kPNG_Type, 100); | 
 | 150 | } | 
 | 151 |  | 
| scroggo@google.com | b6e806b | 2012-10-03 17:32:33 +0000 | [diff] [blame] | 152 | /** | 
 | 153 |  * If path is non NULL, append number to it, and call write(SkCanvas*, SkString) to write the | 
 | 154 |  * provided canvas to a file. Returns true if path is NULL or if write() succeeds. | 
 | 155 |  */ | 
 | 156 | static bool writeAppendNumber(SkCanvas* canvas, const SkString* path, int number) { | 
 | 157 |     if (NULL == path) { | 
 | 158 |         return true; | 
 | 159 |     } | 
 | 160 |     SkString pathWithNumber(*path); | 
 | 161 |     pathWithNumber.appendf("%i", number); | 
 | 162 |     return write(canvas, pathWithNumber); | 
 | 163 | } | 
 | 164 |  | 
| scroggo@google.com | acfb30e | 2012-09-18 14:32:35 +0000 | [diff] [blame] | 165 | /////////////////////////////////////////////////////////////////////////////////////////////// | 
 | 166 |  | 
| scroggo@google.com | 81f9d2e | 2012-09-20 14:54:21 +0000 | [diff] [blame] | 167 | bool RecordPictureRenderer::render(const SkString*) { | 
| junov@chromium.org | 9313ca4 | 2012-11-02 18:11:49 +0000 | [diff] [blame] | 168 |     SkAutoTUnref<SkPicture> replayer(this->createPicture()); | 
 | 169 |     SkCanvas* recorder = replayer->beginRecording(fPicture->width(), fPicture->height(), | 
 | 170 |                                                   this->recordFlags()); | 
| scroggo@google.com | 9a41252 | 2012-09-07 15:21:18 +0000 | [diff] [blame] | 171 |     fPicture->draw(recorder); | 
| junov@chromium.org | 9313ca4 | 2012-11-02 18:11:49 +0000 | [diff] [blame] | 172 |     replayer->endRecording(); | 
| scroggo@google.com | 81f9d2e | 2012-09-20 14:54:21 +0000 | [diff] [blame] | 173 |     // Since this class does not actually render, return false. | 
 | 174 |     return false; | 
| scroggo@google.com | 9a41252 | 2012-09-07 15:21:18 +0000 | [diff] [blame] | 175 | } | 
 | 176 |  | 
| scroggo@google.com | 0a049b8 | 2012-11-02 22:01:26 +0000 | [diff] [blame^] | 177 | SkString RecordPictureRenderer::getConfigNameInternal() { | 
 | 178 |     return SkString("record"); | 
 | 179 | } | 
 | 180 |  | 
| scroggo@google.com | acfb30e | 2012-09-18 14:32:35 +0000 | [diff] [blame] | 181 | /////////////////////////////////////////////////////////////////////////////////////////////// | 
 | 182 |  | 
| scroggo@google.com | 81f9d2e | 2012-09-20 14:54:21 +0000 | [diff] [blame] | 183 | bool PipePictureRenderer::render(const SkString* path) { | 
| keyar@chromium.org | 9d696c0 | 2012-08-07 17:11:33 +0000 | [diff] [blame] | 184 |     SkASSERT(fCanvas.get() != NULL); | 
 | 185 |     SkASSERT(fPicture != NULL); | 
| keyar@chromium.org | 78a35c5 | 2012-08-20 15:03:44 +0000 | [diff] [blame] | 186 |     if (NULL == fCanvas.get() || NULL == fPicture) { | 
| scroggo@google.com | 81f9d2e | 2012-09-20 14:54:21 +0000 | [diff] [blame] | 187 |         return false; | 
| keyar@chromium.org | 9d696c0 | 2012-08-07 17:11:33 +0000 | [diff] [blame] | 188 |     } | 
 | 189 |  | 
 | 190 |     PipeController pipeController(fCanvas.get()); | 
| keyar@chromium.org | 451bb9f | 2012-07-26 17:27:57 +0000 | [diff] [blame] | 191 |     SkGPipeWriter writer; | 
 | 192 |     SkCanvas* pipeCanvas = writer.startRecording(&pipeController); | 
| keyar@chromium.org | 9d696c0 | 2012-08-07 17:11:33 +0000 | [diff] [blame] | 193 |     pipeCanvas->drawPicture(*fPicture); | 
| keyar@chromium.org | 451bb9f | 2012-07-26 17:27:57 +0000 | [diff] [blame] | 194 |     writer.endRecording(); | 
| scroggo@google.com | 9a41252 | 2012-09-07 15:21:18 +0000 | [diff] [blame] | 195 |     fCanvas->flush(); | 
| borenet@google.com | 070d354 | 2012-10-26 13:26:55 +0000 | [diff] [blame] | 196 |     if (NULL != path) { | 
 | 197 |         return write(fCanvas, *path); | 
 | 198 |     } | 
 | 199 |     return true; | 
| keyar@chromium.org | 451bb9f | 2012-07-26 17:27:57 +0000 | [diff] [blame] | 200 | } | 
 | 201 |  | 
| scroggo@google.com | 0a049b8 | 2012-11-02 22:01:26 +0000 | [diff] [blame^] | 202 | SkString PipePictureRenderer::getConfigNameInternal() { | 
 | 203 |     return SkString("pipe"); | 
 | 204 | } | 
 | 205 |  | 
| scroggo@google.com | acfb30e | 2012-09-18 14:32:35 +0000 | [diff] [blame] | 206 | /////////////////////////////////////////////////////////////////////////////////////////////// | 
 | 207 |  | 
| junov@chromium.org | 9313ca4 | 2012-11-02 18:11:49 +0000 | [diff] [blame] | 208 | void SimplePictureRenderer::init(SkPicture* picture) { | 
 | 209 |     INHERITED::init(picture); | 
 | 210 |     this->buildBBoxHierarchy(); | 
 | 211 | } | 
 | 212 |  | 
| scroggo@google.com | 81f9d2e | 2012-09-20 14:54:21 +0000 | [diff] [blame] | 213 | bool SimplePictureRenderer::render(const SkString* path) { | 
| keyar@chromium.org | 9d696c0 | 2012-08-07 17:11:33 +0000 | [diff] [blame] | 214 |     SkASSERT(fCanvas.get() != NULL); | 
 | 215 |     SkASSERT(fPicture != NULL); | 
| keyar@chromium.org | 78a35c5 | 2012-08-20 15:03:44 +0000 | [diff] [blame] | 216 |     if (NULL == fCanvas.get() || NULL == fPicture) { | 
| scroggo@google.com | 81f9d2e | 2012-09-20 14:54:21 +0000 | [diff] [blame] | 217 |         return false; | 
| keyar@chromium.org | 9d696c0 | 2012-08-07 17:11:33 +0000 | [diff] [blame] | 218 |     } | 
 | 219 |  | 
 | 220 |     fCanvas->drawPicture(*fPicture); | 
| scroggo@google.com | 9a41252 | 2012-09-07 15:21:18 +0000 | [diff] [blame] | 221 |     fCanvas->flush(); | 
| borenet@google.com | 070d354 | 2012-10-26 13:26:55 +0000 | [diff] [blame] | 222 |     if (NULL != path) { | 
 | 223 |         return write(fCanvas, *path); | 
 | 224 |     } | 
 | 225 |     return true; | 
| keyar@chromium.org | 451bb9f | 2012-07-26 17:27:57 +0000 | [diff] [blame] | 226 | } | 
 | 227 |  | 
| scroggo@google.com | 0a049b8 | 2012-11-02 22:01:26 +0000 | [diff] [blame^] | 228 | SkString SimplePictureRenderer::getConfigNameInternal() { | 
 | 229 |     return SkString("simple"); | 
 | 230 | } | 
 | 231 |  | 
| scroggo@google.com | acfb30e | 2012-09-18 14:32:35 +0000 | [diff] [blame] | 232 | /////////////////////////////////////////////////////////////////////////////////////////////// | 
 | 233 |  | 
| keyar@chromium.org | 451bb9f | 2012-07-26 17:27:57 +0000 | [diff] [blame] | 234 | TiledPictureRenderer::TiledPictureRenderer() | 
| scroggo@google.com | a62da2f | 2012-11-02 21:28:12 +0000 | [diff] [blame] | 235 |     : fTileWidth(kDefaultTileWidth) | 
| rileya@google.com | b947b91 | 2012-08-29 17:35:07 +0000 | [diff] [blame] | 236 |     , fTileHeight(kDefaultTileHeight) | 
| rileya@google.com | a04dc02 | 2012-09-10 19:01:38 +0000 | [diff] [blame] | 237 |     , fTileWidthPercentage(0.0) | 
| rileya@google.com | b947b91 | 2012-08-29 17:35:07 +0000 | [diff] [blame] | 238 |     , fTileHeightPercentage(0.0) | 
| scroggo@google.com | a62da2f | 2012-11-02 21:28:12 +0000 | [diff] [blame] | 239 |     , fTileMinPowerOf2Width(0) { } | 
| keyar@chromium.org | 451bb9f | 2012-07-26 17:27:57 +0000 | [diff] [blame] | 240 |  | 
| keyar@chromium.org | 9d696c0 | 2012-08-07 17:11:33 +0000 | [diff] [blame] | 241 | void TiledPictureRenderer::init(SkPicture* pict) { | 
 | 242 |     SkASSERT(pict != NULL); | 
| scroggo@google.com | acfb30e | 2012-09-18 14:32:35 +0000 | [diff] [blame] | 243 |     SkASSERT(0 == fTileRects.count()); | 
 | 244 |     if (NULL == pict || fTileRects.count() != 0) { | 
| keyar@chromium.org | 9d696c0 | 2012-08-07 17:11:33 +0000 | [diff] [blame] | 245 |         return; | 
 | 246 |     } | 
 | 247 |  | 
| scroggo@google.com | acfb30e | 2012-09-18 14:32:35 +0000 | [diff] [blame] | 248 |     // Do not call INHERITED::init(), which would create a (potentially large) canvas which is not | 
 | 249 |     // used by bench_pictures. | 
 | 250 |     fPicture = pict; | 
| junov@chromium.org | 9313ca4 | 2012-11-02 18:11:49 +0000 | [diff] [blame] | 251 |     fPicture->ref(); | 
| scroggo@google.com | a62da2f | 2012-11-02 21:28:12 +0000 | [diff] [blame] | 252 |     this->buildBBoxHierarchy(); | 
| keyar@chromium.org | cc6e5ef | 2012-07-27 20:09:26 +0000 | [diff] [blame] | 253 |  | 
 | 254 |     if (fTileWidthPercentage > 0) { | 
| robertphillips@google.com | 5d8d186 | 2012-08-15 14:36:41 +0000 | [diff] [blame] | 255 |         fTileWidth = sk_float_ceil2int(float(fTileWidthPercentage * fPicture->width() / 100)); | 
| keyar@chromium.org | cc6e5ef | 2012-07-27 20:09:26 +0000 | [diff] [blame] | 256 |     } | 
 | 257 |     if (fTileHeightPercentage > 0) { | 
| robertphillips@google.com | 5d8d186 | 2012-08-15 14:36:41 +0000 | [diff] [blame] | 258 |         fTileHeight = sk_float_ceil2int(float(fTileHeightPercentage * fPicture->height() / 100)); | 
| keyar@chromium.org | cc6e5ef | 2012-07-27 20:09:26 +0000 | [diff] [blame] | 259 |     } | 
 | 260 |  | 
| keyar@chromium.org | f4959ab | 2012-08-23 20:53:25 +0000 | [diff] [blame] | 261 |     if (fTileMinPowerOf2Width > 0) { | 
 | 262 |         this->setupPowerOf2Tiles(); | 
 | 263 |     } else { | 
 | 264 |         this->setupTiles(); | 
 | 265 |     } | 
| keyar@chromium.org | 451bb9f | 2012-07-26 17:27:57 +0000 | [diff] [blame] | 266 | } | 
 | 267 |  | 
| keyar@chromium.org | 9d696c0 | 2012-08-07 17:11:33 +0000 | [diff] [blame] | 268 | void TiledPictureRenderer::end() { | 
| scroggo@google.com | bcdf2ec | 2012-09-20 14:42:33 +0000 | [diff] [blame] | 269 |     fTileRects.reset(); | 
| keyar@chromium.org | 9d696c0 | 2012-08-07 17:11:33 +0000 | [diff] [blame] | 270 |     this->INHERITED::end(); | 
| keyar@chromium.org | 451bb9f | 2012-07-26 17:27:57 +0000 | [diff] [blame] | 271 | } | 
 | 272 |  | 
| keyar@chromium.org | 9d696c0 | 2012-08-07 17:11:33 +0000 | [diff] [blame] | 273 | void TiledPictureRenderer::setupTiles() { | 
| scroggo@google.com | acfb30e | 2012-09-18 14:32:35 +0000 | [diff] [blame] | 274 |     for (int tile_y_start = 0; tile_y_start < fPicture->height(); tile_y_start += fTileHeight) { | 
 | 275 |         for (int tile_x_start = 0; tile_x_start < fPicture->width(); tile_x_start += fTileWidth) { | 
 | 276 |             *fTileRects.append() = SkRect::MakeXYWH(SkIntToScalar(tile_x_start), | 
 | 277 |                                                     SkIntToScalar(tile_y_start), | 
 | 278 |                                                     SkIntToScalar(fTileWidth), | 
 | 279 |                                                     SkIntToScalar(fTileHeight)); | 
| keyar@chromium.org | f4959ab | 2012-08-23 20:53:25 +0000 | [diff] [blame] | 280 |         } | 
 | 281 |     } | 
 | 282 | } | 
 | 283 |  | 
 | 284 | // The goal of the powers of two tiles is to minimize the amount of wasted tile | 
 | 285 | // space in the width-wise direction and then minimize the number of tiles. The | 
 | 286 | // constraints are that every tile must have a pixel width that is a power of | 
 | 287 | // two and also be of some minimal width (that is also a power of two). | 
 | 288 | // | 
| scroggo@google.com | 58b4ead | 2012-08-31 16:15:22 +0000 | [diff] [blame] | 289 | // This is solved by first taking our picture size and rounding it up to the | 
| keyar@chromium.org | f4959ab | 2012-08-23 20:53:25 +0000 | [diff] [blame] | 290 | // multiple of the minimal width. The binary representation of this rounded | 
 | 291 | // value gives us the tiles we need: a bit of value one means we need a tile of | 
 | 292 | // that size. | 
 | 293 | void TiledPictureRenderer::setupPowerOf2Tiles() { | 
 | 294 |     int rounded_value = fPicture->width(); | 
 | 295 |     if (fPicture->width() % fTileMinPowerOf2Width != 0) { | 
 | 296 |         rounded_value = fPicture->width() - (fPicture->width() % fTileMinPowerOf2Width) | 
 | 297 |             + fTileMinPowerOf2Width; | 
 | 298 |     } | 
 | 299 |  | 
| robertphillips@google.com | 94acc70 | 2012-09-06 18:43:21 +0000 | [diff] [blame] | 300 |     int num_bits = SkScalarCeilToInt(SkScalarLog2(SkIntToScalar(fPicture->width()))); | 
| keyar@chromium.org | f4959ab | 2012-08-23 20:53:25 +0000 | [diff] [blame] | 301 |     int largest_possible_tile_size = 1 << num_bits; | 
 | 302 |  | 
 | 303 |     // The tile height is constant for a particular picture. | 
 | 304 |     for (int tile_y_start = 0; tile_y_start < fPicture->height(); tile_y_start += fTileHeight) { | 
 | 305 |         int tile_x_start = 0; | 
 | 306 |         int current_width = largest_possible_tile_size; | 
| scroggo@google.com | acfb30e | 2012-09-18 14:32:35 +0000 | [diff] [blame] | 307 |         // Set fTileWidth to be the width of the widest tile, so that each canvas is large enough | 
 | 308 |         // to draw each tile. | 
 | 309 |         fTileWidth = current_width; | 
| keyar@chromium.org | f4959ab | 2012-08-23 20:53:25 +0000 | [diff] [blame] | 310 |  | 
 | 311 |         while (current_width >= fTileMinPowerOf2Width) { | 
 | 312 |             // It is very important this is a bitwise AND. | 
 | 313 |             if (current_width & rounded_value) { | 
| scroggo@google.com | acfb30e | 2012-09-18 14:32:35 +0000 | [diff] [blame] | 314 |                 *fTileRects.append() = SkRect::MakeXYWH(SkIntToScalar(tile_x_start), | 
 | 315 |                                                         SkIntToScalar(tile_y_start), | 
 | 316 |                                                         SkIntToScalar(current_width), | 
 | 317 |                                                         SkIntToScalar(fTileHeight)); | 
| keyar@chromium.org | f4959ab | 2012-08-23 20:53:25 +0000 | [diff] [blame] | 318 |                 tile_x_start += current_width; | 
 | 319 |             } | 
 | 320 |  | 
 | 321 |             current_width >>= 1; | 
| keyar@chromium.org | 451bb9f | 2012-07-26 17:27:57 +0000 | [diff] [blame] | 322 |         } | 
 | 323 |     } | 
 | 324 | } | 
 | 325 |  | 
| scroggo@google.com | bcdf2ec | 2012-09-20 14:42:33 +0000 | [diff] [blame] | 326 | /** | 
 | 327 |  * Draw the specified playback to the canvas translated to rectangle provided, so that this mini | 
 | 328 |  * canvas represents the rectangle's portion of the overall picture. | 
 | 329 |  * Saves and restores so that the initial clip and matrix return to their state before this function | 
 | 330 |  * is called. | 
 | 331 |  */ | 
 | 332 | template<class T> | 
 | 333 | static void DrawTileToCanvas(SkCanvas* canvas, const SkRect& tileRect, T* playback) { | 
 | 334 |     int saveCount = canvas->save(); | 
 | 335 |     // Translate so that we draw the correct portion of the picture | 
 | 336 |     canvas->translate(-tileRect.fLeft, -tileRect.fTop); | 
 | 337 |     playback->draw(canvas); | 
 | 338 |     canvas->restoreToCount(saveCount); | 
 | 339 |     canvas->flush(); | 
| keyar@chromium.org | 451bb9f | 2012-07-26 17:27:57 +0000 | [diff] [blame] | 340 | } | 
 | 341 |  | 
| scroggo@google.com | 58b4ead | 2012-08-31 16:15:22 +0000 | [diff] [blame] | 342 | /////////////////////////////////////////////////////////////////////////////////////////////// | 
| scroggo@google.com | bcdf2ec | 2012-09-20 14:42:33 +0000 | [diff] [blame] | 343 |  | 
| scroggo@google.com | 81f9d2e | 2012-09-20 14:54:21 +0000 | [diff] [blame] | 344 | bool TiledPictureRenderer::render(const SkString* path) { | 
| scroggo@google.com | acfb30e | 2012-09-18 14:32:35 +0000 | [diff] [blame] | 345 |     SkASSERT(fPicture != NULL); | 
 | 346 |     if (NULL == fPicture) { | 
| scroggo@google.com | 81f9d2e | 2012-09-20 14:54:21 +0000 | [diff] [blame] | 347 |         return false; | 
| scroggo@google.com | acfb30e | 2012-09-18 14:32:35 +0000 | [diff] [blame] | 348 |     } | 
 | 349 |  | 
| scroggo@google.com | a62da2f | 2012-11-02 21:28:12 +0000 | [diff] [blame] | 350 |     // Reuse one canvas for all tiles. | 
 | 351 |     SkCanvas* canvas = this->setupCanvas(fTileWidth, fTileHeight); | 
 | 352 |     SkAutoUnref aur(canvas); | 
| scroggo@google.com | acfb30e | 2012-09-18 14:32:35 +0000 | [diff] [blame] | 353 |  | 
| scroggo@google.com | a62da2f | 2012-11-02 21:28:12 +0000 | [diff] [blame] | 354 |     bool success = true; | 
 | 355 |     for (int i = 0; i < fTileRects.count(); ++i) { | 
 | 356 |         DrawTileToCanvas(canvas, fTileRects[i], fPicture); | 
 | 357 |         if (NULL != path) { | 
 | 358 |             success &= writeAppendNumber(canvas, path, i); | 
| scroggo@google.com | 58b4ead | 2012-08-31 16:15:22 +0000 | [diff] [blame] | 359 |         } | 
| keyar@chromium.org | 163b567 | 2012-08-01 17:53:29 +0000 | [diff] [blame] | 360 |     } | 
| scroggo@google.com | a62da2f | 2012-11-02 21:28:12 +0000 | [diff] [blame] | 361 |     return success; | 
| keyar@chromium.org | 163b567 | 2012-08-01 17:53:29 +0000 | [diff] [blame] | 362 | } | 
 | 363 |  | 
| scroggo@google.com | bcdf2ec | 2012-09-20 14:42:33 +0000 | [diff] [blame] | 364 | SkCanvas* TiledPictureRenderer::setupCanvas(int width, int height) { | 
 | 365 |     SkCanvas* canvas = this->INHERITED::setupCanvas(width, height); | 
 | 366 |     SkASSERT(fPicture != NULL); | 
 | 367 |     // Clip the tile to an area that is completely in what the SkPicture says is the | 
 | 368 |     // drawn-to area. This is mostly important for tiles on the right and bottom edges | 
 | 369 |     // as they may go over this area and the picture may have some commands that | 
 | 370 |     // draw outside of this area and so should not actually be written. | 
 | 371 |     SkRect clip = SkRect::MakeWH(SkIntToScalar(fPicture->width()), | 
 | 372 |                                  SkIntToScalar(fPicture->height())); | 
 | 373 |     canvas->clipRect(clip); | 
 | 374 |     return canvas; | 
 | 375 | } | 
| scroggo@google.com | 0a049b8 | 2012-11-02 22:01:26 +0000 | [diff] [blame^] | 376 |  | 
 | 377 | SkString TiledPictureRenderer::getConfigNameInternal() { | 
 | 378 |     SkString name; | 
 | 379 |     if (fTileMinPowerOf2Width > 0) { | 
 | 380 |         name.append("pow2tile_"); | 
 | 381 |         name.appendf("%i", fTileMinPowerOf2Width); | 
 | 382 |     } else { | 
 | 383 |         name.append("tile_"); | 
 | 384 |         if (fTileWidthPercentage > 0) { | 
 | 385 |             name.appendf("%.f%%", fTileWidthPercentage); | 
 | 386 |         } else { | 
 | 387 |             name.appendf("%i", fTileWidth); | 
 | 388 |         } | 
 | 389 |     } | 
 | 390 |     name.append("x"); | 
 | 391 |     if (fTileHeightPercentage > 0) { | 
 | 392 |         name.appendf("%.f%%", fTileHeightPercentage); | 
 | 393 |     } else { | 
 | 394 |         name.appendf("%i", fTileHeight); | 
 | 395 |     } | 
 | 396 |     return name; | 
 | 397 | } | 
 | 398 |  | 
| scroggo@google.com | a62da2f | 2012-11-02 21:28:12 +0000 | [diff] [blame] | 399 | /////////////////////////////////////////////////////////////////////////////////////////////// | 
 | 400 |  | 
 | 401 | // Holds all of the information needed to draw a set of tiles. | 
 | 402 | class CloneData : public SkRunnable { | 
 | 403 |  | 
 | 404 | public: | 
 | 405 |     CloneData(SkPicture* clone, SkCanvas* canvas, SkTDArray<SkRect>& rects, int start, int end, | 
 | 406 |               SkRunnable* done) | 
 | 407 |         : fClone(clone) | 
 | 408 |         , fCanvas(canvas) | 
 | 409 |         , fPath(NULL) | 
 | 410 |         , fRects(rects) | 
 | 411 |         , fStart(start) | 
 | 412 |         , fEnd(end) | 
 | 413 |         , fSuccess(NULL) | 
 | 414 |         , fDone(done) { | 
 | 415 |         SkASSERT(fDone != NULL); | 
 | 416 |     } | 
 | 417 |  | 
 | 418 |     virtual void run() SK_OVERRIDE { | 
 | 419 |         SkGraphics::SetTLSFontCacheLimit(1024 * 1024); | 
 | 420 |         for (int i = fStart; i < fEnd; i++) { | 
 | 421 |             DrawTileToCanvas(fCanvas, fRects[i], fClone); | 
 | 422 |             if (fPath != NULL && !writeAppendNumber(fCanvas, fPath, i) | 
 | 423 |                 && fSuccess != NULL) { | 
 | 424 |                 *fSuccess = false; | 
 | 425 |                 // If one tile fails to write to a file, do not continue drawing the rest. | 
 | 426 |                 break; | 
 | 427 |             } | 
 | 428 |         } | 
 | 429 |         fDone->run(); | 
 | 430 |     } | 
 | 431 |  | 
 | 432 |     void setPathAndSuccess(const SkString* path, bool* success) { | 
 | 433 |         fPath = path; | 
 | 434 |         fSuccess = success; | 
 | 435 |     } | 
 | 436 |  | 
 | 437 | private: | 
 | 438 |     // All pointers unowned. | 
 | 439 |     SkPicture*         fClone;      // Picture to draw from. Each CloneData has a unique one which | 
 | 440 |                                     // is threadsafe. | 
 | 441 |     SkCanvas*          fCanvas;     // Canvas to draw to. Reused for each tile. | 
 | 442 |     const SkString*    fPath;       // If non-null, path to write the result to as a PNG. | 
 | 443 |     SkTDArray<SkRect>& fRects;      // All tiles of the picture. | 
 | 444 |     const int          fStart;      // Range of tiles drawn by this thread. | 
 | 445 |     const int          fEnd; | 
 | 446 |     bool*              fSuccess;    // Only meaningful if path is non-null. Shared by all threads, | 
 | 447 |                                     // and only set to false upon failure to write to a PNG. | 
 | 448 |     SkRunnable*        fDone; | 
 | 449 | }; | 
 | 450 |  | 
 | 451 | MultiCorePictureRenderer::MultiCorePictureRenderer(int threadCount) | 
 | 452 | : fNumThreads(threadCount) | 
 | 453 | , fThreadPool(threadCount) | 
 | 454 | , fCountdown(threadCount) { | 
 | 455 |     // Only need to create fNumThreads - 1 clones, since one thread will use the base | 
 | 456 |     // picture. | 
 | 457 |     fPictureClones = SkNEW_ARRAY(SkPicture, fNumThreads - 1); | 
 | 458 |     fCloneData = SkNEW_ARRAY(CloneData*, fNumThreads); | 
 | 459 | } | 
 | 460 |  | 
 | 461 | void MultiCorePictureRenderer::init(SkPicture *pict) { | 
 | 462 |     // Set fPicture and the tiles. | 
 | 463 |     this->INHERITED::init(pict); | 
 | 464 |     for (int i = 0; i < fNumThreads; ++i) { | 
 | 465 |         *fCanvasPool.append() = this->setupCanvas(this->getTileWidth(), this->getTileHeight()); | 
 | 466 |     } | 
 | 467 |     // Only need to create fNumThreads - 1 clones, since one thread will use the base picture. | 
 | 468 |     fPicture->clone(fPictureClones, fNumThreads - 1); | 
 | 469 |     // Populate each thread with the appropriate data. | 
 | 470 |     // Group the tiles into nearly equal size chunks, rounding up so we're sure to cover them all. | 
 | 471 |     const int chunkSize = (fTileRects.count() + fNumThreads - 1) / fNumThreads; | 
 | 472 |  | 
 | 473 |     for (int i = 0; i < fNumThreads; i++) { | 
 | 474 |         SkPicture* pic; | 
 | 475 |         if (i == fNumThreads-1) { | 
 | 476 |             // The last set will use the original SkPicture. | 
 | 477 |             pic = fPicture; | 
 | 478 |         } else { | 
 | 479 |             pic = &fPictureClones[i]; | 
 | 480 |         } | 
 | 481 |         const int start = i * chunkSize; | 
 | 482 |         const int end = SkMin32(start + chunkSize, fTileRects.count()); | 
 | 483 |         fCloneData[i] = SkNEW_ARGS(CloneData, | 
 | 484 |                                    (pic, fCanvasPool[i], fTileRects, start, end, &fCountdown)); | 
 | 485 |     } | 
 | 486 | } | 
 | 487 |  | 
 | 488 | bool MultiCorePictureRenderer::render(const SkString *path) { | 
 | 489 |     bool success = true; | 
 | 490 |     if (path != NULL) { | 
 | 491 |         for (int i = 0; i < fNumThreads-1; i++) { | 
 | 492 |             fCloneData[i]->setPathAndSuccess(path, &success); | 
 | 493 |         } | 
 | 494 |     } | 
 | 495 |  | 
 | 496 |     fCountdown.reset(fNumThreads); | 
 | 497 |     for (int i = 0; i < fNumThreads; i++) { | 
 | 498 |         fThreadPool.add(fCloneData[i]); | 
 | 499 |     } | 
 | 500 |     fCountdown.wait(); | 
 | 501 |  | 
 | 502 |     return success; | 
 | 503 | } | 
 | 504 |  | 
 | 505 | void MultiCorePictureRenderer::end() { | 
 | 506 |     for (int i = 0; i < fNumThreads - 1; i++) { | 
 | 507 |         SkDELETE(fCloneData[i]); | 
 | 508 |         fCloneData[i] = NULL; | 
 | 509 |     } | 
 | 510 |  | 
 | 511 |     fCanvasPool.unrefAll(); | 
 | 512 |  | 
 | 513 |     this->INHERITED::end(); | 
 | 514 | } | 
 | 515 |  | 
 | 516 | MultiCorePictureRenderer::~MultiCorePictureRenderer() { | 
 | 517 |     // Each individual CloneData was deleted in end. | 
 | 518 |     SkDELETE_ARRAY(fCloneData); | 
 | 519 |     SkDELETE_ARRAY(fPictureClones); | 
 | 520 | } | 
| scroggo@google.com | bcdf2ec | 2012-09-20 14:42:33 +0000 | [diff] [blame] | 521 |  | 
| scroggo@google.com | 0a049b8 | 2012-11-02 22:01:26 +0000 | [diff] [blame^] | 522 | SkString MultiCorePictureRenderer::getConfigNameInternal() { | 
 | 523 |     SkString name = this->INHERITED::getConfigNameInternal(); | 
 | 524 |     name.appendf("_multi_%i_threads", fNumThreads); | 
 | 525 |     return name; | 
 | 526 | } | 
 | 527 |  | 
| scroggo@google.com | acfb30e | 2012-09-18 14:32:35 +0000 | [diff] [blame] | 528 | /////////////////////////////////////////////////////////////////////////////////////////////// | 
| scroggo@google.com | 9a41252 | 2012-09-07 15:21:18 +0000 | [diff] [blame] | 529 |  | 
 | 530 | void PlaybackCreationRenderer::setup() { | 
| junov@chromium.org | 9313ca4 | 2012-11-02 18:11:49 +0000 | [diff] [blame] | 531 |     fReplayer.reset(this->createPicture()); | 
 | 532 |     SkCanvas* recorder = fReplayer->beginRecording(fPicture->width(), fPicture->height(), | 
 | 533 |                                                    this->recordFlags()); | 
| scroggo@google.com | 9a41252 | 2012-09-07 15:21:18 +0000 | [diff] [blame] | 534 |     fPicture->draw(recorder); | 
 | 535 | } | 
 | 536 |  | 
| scroggo@google.com | 81f9d2e | 2012-09-20 14:54:21 +0000 | [diff] [blame] | 537 | bool PlaybackCreationRenderer::render(const SkString*) { | 
| junov@chromium.org | 9313ca4 | 2012-11-02 18:11:49 +0000 | [diff] [blame] | 538 |     fReplayer->endRecording(); | 
| scroggo@google.com | 81f9d2e | 2012-09-20 14:54:21 +0000 | [diff] [blame] | 539 |     // Since this class does not actually render, return false. | 
 | 540 |     return false; | 
| keyar@chromium.org | 451bb9f | 2012-07-26 17:27:57 +0000 | [diff] [blame] | 541 | } | 
 | 542 |  | 
| scroggo@google.com | 0a049b8 | 2012-11-02 22:01:26 +0000 | [diff] [blame^] | 543 | SkString PlaybackCreationRenderer::getConfigNameInternal() { | 
 | 544 |     return SkString("playback_creation"); | 
 | 545 | } | 
 | 546 |  | 
| junov@chromium.org | 9313ca4 | 2012-11-02 18:11:49 +0000 | [diff] [blame] | 547 | /////////////////////////////////////////////////////////////////////////////////////////////// | 
 | 548 | // SkPicture variants for each BBoxHierarchy type | 
 | 549 |  | 
 | 550 | class RTreePicture : public SkPicture { | 
 | 551 | public: | 
 | 552 |     virtual SkBBoxHierarchy* createBBoxHierarchy() const SK_OVERRIDE{ | 
 | 553 |         static const int kRTreeMinChildren = 6; | 
 | 554 |         static const int kRTreeMaxChildren = 11; | 
 | 555 |         SkScalar aspectRatio = SkScalarDiv(SkIntToScalar(fWidth), | 
 | 556 |                                            SkIntToScalar(fHeight)); | 
 | 557 |         return SkRTree::Create(kRTreeMinChildren, kRTreeMaxChildren, | 
 | 558 |                                aspectRatio); | 
 | 559 |     } | 
 | 560 | }; | 
 | 561 |  | 
 | 562 | SkPicture* PictureRenderer::createPicture() { | 
 | 563 |     switch (fBBoxHierarchyType) { | 
 | 564 |         case kNone_BBoxHierarchyType: | 
 | 565 |             return SkNEW(SkPicture); | 
 | 566 |         case kRTree_BBoxHierarchyType: | 
 | 567 |             return SkNEW(RTreePicture); | 
 | 568 |     } | 
 | 569 |     SkASSERT(0); // invalid bbhType | 
 | 570 |     return NULL; | 
| keyar@chromium.org | 451bb9f | 2012-07-26 17:27:57 +0000 | [diff] [blame] | 571 | } | 
| junov@chromium.org | 9313ca4 | 2012-11-02 18:11:49 +0000 | [diff] [blame] | 572 |  | 
 | 573 | } // namespace sk_tools |