Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 /* | |
| 2 * Copyright 2013 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 "SkCanvasUtils.h" | |
| 9 #include "SkCanvas.h" | |
| 10 #include "SkDevice.h" | |
| 11 #include "SkNWayCanvas.h" | |
| 12 #include "SkSurface.h" | |
| 13 #include "SkTScopedPtr.h" | |
|
mtklein
2013/08/28 18:20:46
Ben will scream. I think the Ben-friendly version
djsollen
2013/08/29 15:09:19
Done.
| |
| 14 #include "SkWriter32.h" | |
| 15 | |
| 16 #define CANVAS_STATE_VERSION 1 | |
| 17 | |
| 18 enum RasterConfigs { | |
| 19 kRGB_565_RasterConfig = 4, | |
|
scroggo
2013/08/28 17:22:01
Maybe a comment explaining why these start with 4?
djsollen
2013/08/29 15:09:19
Done.
| |
| 20 kARGB_4444_RasterConfig = 5, | |
| 21 kARGB_8888_RasterConfig = 6 | |
| 22 }; | |
| 23 typedef int32_t RasterConfig; | |
|
mtklein
2013/08/28 18:20:46
Can you plaster a few warnings around to remind th
djsollen
2013/08/29 15:09:19
Done.
| |
| 24 | |
| 25 enum CanvasBackends { | |
| 26 kUnknown_CanvasBackend = 0, | |
| 27 kRaster_CanvasBackend = 1, | |
| 28 kGPU_CanvasBackend = 2, | |
| 29 kPDF_CanvasBackend = 3 | |
| 30 }; | |
| 31 typedef int32_t CanvasBackend; | |
| 32 | |
| 33 struct SkMCState { | |
| 34 // The Matrix and Clip are relative to |pixels|, not the source canvas. | |
| 35 float matrix[9]; // The matrix currently in effect on the canvas. | |
| 36 | |
| 37 // this only works for non-antialiased clips | |
| 38 int32_t clipRectCount; // Number of rects in |clip_rects|. | |
| 39 int32_t* clipRects; // Clip area: 4 ints per rect in {x,y,w,h} format. | |
|
mtklein
2013/08/28 18:20:46
Can't hurt to have a struct for this, right? Then
djsollen
2013/08/29 15:09:19
Done.
| |
| 40 | |
|
mtklein
2013/08/28 18:20:46
stray newline
djsollen
2013/08/29 15:09:19
Done.
| |
| 41 }; | |
| 42 | |
| 43 // NOTE: If you add more members, bump CanvasState::version. | |
| 44 struct SkCanvasLayerState { | |
| 45 CanvasBackend type; // |pixel| format: a value from AwPixelConfig. | |
|
mtklein
2013/08/28 18:20:46
Comment is confusing?
djsollen
2013/08/29 15:09:19
Done.
| |
| 46 int32_t x, y; | |
| 47 int32_t width; // In pixels. | |
| 48 int32_t height; // In pixels. | |
| 49 | |
| 50 SkMCState mcState; | |
| 51 | |
| 52 union { | |
| 53 struct { | |
| 54 RasterConfig config; // |pixel| format: a value from RasterConfigs. | |
| 55 int32_t rowBytes; // Number of bytes from start of one line to nex t. | |
|
scroggo
2013/08/28 17:22:01
nit: alignment of //'s
Also, should rowBytes be s
djsollen
2013/08/29 15:09:19
No size_t is not guaranteed to always be 32 bits.
| |
| 56 void* pixels; // The pixels, all (height * row_bytes) of them . | |
| 57 } raster; | |
| 58 struct { | |
| 59 int32_t textureID; | |
| 60 } gpu; | |
| 61 } content; | |
|
mtklein
2013/08/28 18:20:46
You may find it more readable to remove the name "
djsollen
2013/08/29 15:09:19
Done.
| |
| 62 }; | |
| 63 | |
| 64 class SkCanvasState { | |
|
scroggo
2013/08/28 17:22:01
Should this inherit from SkNoncopyable and hide th
djsollen
2013/08/29 15:09:19
This needs to stay as close to a simple struct as
| |
| 65 public: | |
| 66 SkCanvasState(const SkISize& size) { | |
| 67 version = CANVAS_STATE_VERSION; | |
| 68 width = size.width(); | |
| 69 height = size.height(); | |
| 70 layerCount = 0; | |
| 71 layers = NULL; | |
| 72 } | |
| 73 | |
| 74 ~SkCanvasState() { | |
| 75 // loop through the layers and free the data allocated to the clipRects | |
| 76 for (int i = 0; i < layerCount; ++i) { | |
| 77 sk_free(layers[i].mcState.clipRects); | |
| 78 } | |
| 79 | |
| 80 sk_free(mcState.clipRects); | |
|
scroggo
2013/08/28 17:22:01
Should we set this to NULL in the constructor?
mtklein
2013/08/28 18:20:46
More than that, what about initializing the struct
| |
| 81 sk_free(layers); | |
| 82 } | |
| 83 | |
| 84 int32_t version; // The CanvasState version this struct was built with. | |
|
scroggo
2013/08/28 17:22:01
Is there a reason these don't follow our conventio
mtklein
2013/08/28 18:20:46
Given that they're public members, I personally li
mtklein
2013/08/28 18:20:46
Add a reminder that we can never reorder around th
| |
| 85 int32_t width; | |
| 86 int32_t height; | |
| 87 | |
| 88 SkMCState mcState; | |
| 89 | |
| 90 int32_t layerCount; | |
| 91 SkCanvasLayerState* layers; | |
| 92 }; | |
| 93 | |
| 94 //////////////////////////////////////////////////////////////////////////////// | |
| 95 | |
| 96 class ClipValidator : public SkCanvas::ClipVisitor { | |
| 97 public: | |
|
scroggo
2013/08/28 17:22:01
nit: spacing. We typically have no spaces before "
djsollen
2013/08/29 15:09:19
Done.
| |
| 98 ClipValidator() : failed_(false) {} | |
| 99 bool failed() { return failed_; } | |
| 100 | |
| 101 // ClipVisitor | |
| 102 virtual void clipRect(const SkRect& rect, SkRegion::Op op, bool antialias) { | |
|
scroggo
2013/08/28 17:22:01
SK_OVERRIDE?
djsollen
2013/08/29 15:09:19
Done.
| |
| 103 failed_ |= antialias; | |
| 104 } | |
| 105 | |
| 106 virtual void clipPath(const SkPath&, SkRegion::Op, bool antialias) { | |
|
scroggo
2013/08/28 17:22:01
SK_OVERRIDE?
djsollen
2013/08/29 15:09:19
Done.
| |
| 107 failed_ |= antialias; | |
| 108 } | |
| 109 | |
| 110 private: | |
|
scroggo
2013/08/28 17:22:01
Same spacing nit.
djsollen
2013/08/29 15:09:19
Done.
| |
| 111 bool failed_; | |
| 112 }; | |
| 113 | |
| 114 static void setup_MC_state(SkMCState* state, const SkMatrix& matrix, const SkReg ion& clip) { | |
| 115 // initialize the struct | |
| 116 state->clipRectCount = 0; | |
| 117 | |
| 118 // capture the matrix | |
| 119 for (int i = 0; i < 9; i++) { | |
| 120 state->matrix[i] = matrix.get(i); | |
| 121 } | |
| 122 | |
| 123 // capture the clip | |
| 124 const int clipBufferSize = 3 * sizeof(SkIRect); | |
| 125 char clipBuffer[clipBufferSize]; | |
| 126 SkWriter32 clipWriter(sizeof(SkIRect), clipBuffer, clipBufferSize); | |
| 127 | |
| 128 if (!clip.isEmpty()) { | |
| 129 // only returns the b/w clip so aa clips fail | |
| 130 SkRegion::Iterator clip_iterator(clip); | |
| 131 for (; !clip_iterator.done(); clip_iterator.next()) { | |
| 132 clipWriter.writeIRect(clip_iterator.rect()); | |
| 133 state->clipRectCount++; | |
| 134 } | |
| 135 } | |
| 136 | |
| 137 // allocate memory for the clip then and copy them to the struct | |
| 138 state->clipRects = (int32_t*) sk_malloc_throw(clipWriter.size()); | |
| 139 clipWriter.flatten(state->clipRects); | |
| 140 } | |
| 141 | |
| 142 | |
| 143 | |
| 144 SkCanvasState* SkCanvasUtils::CaptureCanvasState(SkCanvas* canvas) { | |
|
scroggo
2013/08/28 17:22:01
Should we check for a NULL canvas?
djsollen
2013/08/29 15:09:19
Done.
| |
| 145 // Check the clip can be decomposed into simple rectangles. | |
|
scroggo
2013/08/28 17:22:01
Comment seems inaccurate; the validator checks for
djsollen
2013/08/29 15:09:19
non-antialiased arbitrary paths are OK as the get
| |
| 146 ClipValidator validator; | |
| 147 canvas->replayClips(&validator); | |
| 148 if (validator.failed()) { | |
| 149 SkDEBUGF(("CaptureCanvasState does not currently support canvases with a ntialiased clips")); | |
| 150 return NULL; | |
| 151 } | |
| 152 | |
| 153 const SkISize canvasSize = canvas->getDeviceSize(); | |
| 154 SkTScopedPtr<SkCanvasState> canvasState(SkNEW_ARGS(SkCanvasState, (canvasSiz e))); | |
|
scroggo
2013/08/28 17:22:01
Aren't we moving away from SkTScopedPtr?
djsollen
2013/08/29 15:09:19
Done.
| |
| 155 | |
| 156 // decompose the total matrix and clip | |
| 157 setup_MC_state(&canvasState->mcState, canvas->getTotalMatrix(), canvas->getT otalClip()); | |
| 158 | |
| 159 // decompose the layers | |
| 160 const int layerBufferSize = 3 * sizeof(SkCanvasLayerState); | |
|
mtklein
2013/08/28 18:20:46
Remind us where 3 comes from? Just a guess about
djsollen
2013/08/29 15:09:19
Done.
| |
| 161 char layerBuffer[layerBufferSize]; | |
| 162 SkWriter32 layerWriter(sizeof(SkCanvasLayerState), layerBuffer, layerBufferS ize); | |
| 163 | |
| 164 for (SkCanvas::LayerIter layer(canvas, false); !layer.done(); layer.next()) { | |
|
mtklein
2013/08/28 18:20:46
can you add an inline "false /*meaning*/" comment
djsollen
2013/08/29 15:09:19
Done.
| |
| 165 // if the layer is clipped out then we can skip it | |
| 166 if (layer.clip().isEmpty()) { | |
| 167 continue; | |
| 168 } | |
| 169 | |
| 170 // we currently only work for a bitmap backed devices | |
|
scroggo
2013/08/28 17:22:01
nit: The word 'a' is not needed here
djsollen
2013/08/29 15:09:19
Done.
| |
| 171 const SkBitmap& bitmap = layer.device()->accessBitmap(true); | |
|
mtklein
2013/08/28 18:20:46
Same for true?
djsollen
2013/08/29 15:09:19
Done.
| |
| 172 if (bitmap.empty() || bitmap.isNull() || !bitmap.lockPixelsAreWritable() ) { | |
| 173 return NULL; | |
| 174 } | |
| 175 | |
| 176 SkCanvasLayerState* layerState = (SkCanvasLayerState*) layerWriter.reser ve(sizeof(SkCanvasLayerState)); | |
|
scroggo
2013/08/28 17:22:01
100 chars.
| |
| 177 layerState->type = kRaster_CanvasBackend; | |
| 178 layerState->x = layer.x(); | |
| 179 layerState->y = layer.y(); | |
| 180 layerState->width = bitmap.width(); | |
| 181 layerState->height = bitmap.height(); | |
| 182 | |
| 183 layerState->content.raster.config = | |
|
scroggo
2013/08/28 17:22:01
nit: I find a switch statement much more readable
| |
| 184 bitmap.config() == SkBitmap::kARGB_8888_Config ? kARGB_8888_RasterConf ig : | |
| 185 bitmap.config() == SkBitmap::kARGB_4444_Config ? kARGB_4444_RasterConf ig : | |
|
mtklein
2013/08/28 18:20:46
Can't we just screw 4444 now?
| |
| 186 bitmap.config() == SkBitmap::kRGB_565_Config ? kRGB_565_RasterConfig : -1; | |
|
mtklein
2013/08/28 18:20:46
return NULL on the failure case?
djsollen
2013/08/29 15:09:19
Done.
| |
| 187 layerState->content.raster.rowBytes = bitmap.rowBytes(); | |
| 188 layerState->content.raster.pixels = bitmap.getPixels(); | |
| 189 | |
| 190 if (layerState->content.raster.config < 0) { | |
|
mtklein
2013/08/28 18:20:46
Oh. :) Maybe move this up with the rest of raste
| |
| 191 return NULL; | |
| 192 } | |
| 193 | |
| 194 setup_MC_state(&layerState->mcState, layer.matrix(), layer.clip()); | |
| 195 canvasState->layerCount++; | |
| 196 } | |
| 197 | |
| 198 // allocate memory for the layers and then and copy them to the struct | |
| 199 canvasState->layers = (SkCanvasLayerState*) sk_malloc_throw(layerWriter.size ()); | |
|
mtklein
2013/08/28 18:20:46
Slight paranoia... add an assert that layerWriter.
djsollen
2013/08/29 15:09:19
Done.
| |
| 200 layerWriter.flatten(canvasState->layers); | |
| 201 | |
| 202 // for now, just ignore any client supplied DrawFilter. | |
| 203 if (canvas->getDrawFilter()) { | |
| 204 SkDEBUGF(("CaptureCanvasState will ignore the canvas' draw filter.")); | |
| 205 } | |
| 206 | |
| 207 return canvasState.release(); | |
| 208 } | |
| 209 | |
| 210 //////////////////////////////////////////////////////////////////////////////// | |
| 211 | |
| 212 static void setup_canvas_from_MC_state(const SkMCState& state, SkCanvas* canvas) { | |
| 213 // reconstruct the matrix | |
| 214 SkMatrix matrix; | |
| 215 for (int i = 0; i < 9; i++) { | |
| 216 matrix.set(i, state.matrix[i]); | |
| 217 } | |
| 218 | |
| 219 // reconstruct the clip | |
| 220 SkRegion clip; | |
| 221 for (int i = 0; i < state.clipRectCount; ++i) { | |
| 222 const int32_t rect_offset = i * 4; | |
| 223 clip.op(SkIRect::MakeLTRB(state.clipRects[rect_offset + 0], | |
| 224 state.clipRects[rect_offset + 1], | |
| 225 state.clipRects[rect_offset + 2], | |
| 226 state.clipRects[rect_offset + 3]), | |
| 227 SkRegion::kUnion_Op); | |
|
mtklein
2013/08/28 18:20:46
dedent SkRegion::kUnion_Op to line up with SkIRect
djsollen
2013/08/29 15:09:19
Done.
| |
| 228 } | |
| 229 | |
| 230 canvas->setMatrix(matrix); | |
| 231 canvas->setClipRegion(clip); | |
| 232 } | |
| 233 | |
| 234 static SkCanvas* create_canvas_from_canvas_layer(const SkCanvasLayerState& layer State) { | |
| 235 SkASSERT(kRaster_CanvasBackend == layerState.type); | |
| 236 | |
| 237 SkBitmap bitmap; | |
| 238 SkBitmap::Config config = | |
| 239 layerState.content.raster.config == kARGB_8888_RasterConfig ? SkBitmap:: kARGB_8888_Config : | |
| 240 layerState.content.raster.config == kRGB_565_RasterConfig ? SkBitmap::kR GB_565_Config : | |
| 241 SkBitmap::kNo_Config; | |
|
mtklein
2013/08/28 18:20:46
Why the asymmetry for 4444? We let a 4444 canvas
djsollen
2013/08/29 15:09:19
Done.
| |
| 242 | |
| 243 if (config == SkBitmap::kNo_Config) { | |
| 244 return NULL; | |
| 245 } | |
| 246 | |
| 247 bitmap.setConfig(config, layerState.width, layerState.height, | |
| 248 layerState.content.raster.rowBytes); | |
| 249 bitmap.setPixels(layerState.content.raster.pixels); | |
| 250 | |
| 251 SkASSERT(!bitmap.empty()); | |
| 252 SkASSERT(!bitmap.isNull()); | |
| 253 | |
| 254 // create a device & canvas | |
| 255 SkAutoTUnref<SkDevice> device(SkNEW_ARGS(SkDevice, (bitmap))); | |
| 256 SkAutoTUnref<SkCanvas> canvas(SkNEW_ARGS(SkCanvas, (device.get()))); | |
|
mtklein
2013/08/28 18:20:46
Given that you don't otherwise use the device, and
scroggo
2013/08/28 20:42:02
The SkCanvas constructor will ref the device, so w
| |
| 257 | |
| 258 // setup the matrix and clip | |
| 259 setup_canvas_from_MC_state(layerState.mcState, canvas.get()); | |
| 260 | |
| 261 return canvas.detach(); | |
| 262 } | |
| 263 | |
| 264 SkCanvas* SkCanvasUtils::CreateFromCanvasState(SkCanvasState* state) { | |
|
scroggo
2013/08/28 17:22:01
Should we check for a NULL state?
| |
| 265 | |
| 266 // check that the versions match | |
| 267 if (CANVAS_STATE_VERSION != state->version) { | |
| 268 SkDebugf("CreateFromCanvasState version does not match the one use to cr eate the input"); | |
| 269 return NULL; | |
| 270 } | |
| 271 | |
| 272 if (state->layerCount < 1) | |
|
scroggo
2013/08/28 17:22:01
Braces.
| |
| 273 return NULL; | |
|
scroggo
2013/08/28 17:22:01
4 space tabs
| |
| 274 | |
| 275 SkAutoTUnref<SkNWayCanvas> canvas(SkNEW_ARGS(SkNWayCanvas, (state->width, st ate->height))); | |
| 276 | |
| 277 // setup the matrix and clip on the n-way canvas | |
| 278 setup_canvas_from_MC_state(state->mcState, canvas); | |
| 279 | |
| 280 // Iterate over the layers and add them to the n-way canvas | |
| 281 for (int i = 0; i < state->layerCount; ++i) { | |
| 282 SkAutoTUnref<SkCanvas> canvasLayer(create_canvas_from_canvas_layer(state ->layers[i])); | |
| 283 if (!canvasLayer.get()) { | |
| 284 return NULL; | |
| 285 } | |
| 286 canvas->addCanvas(canvasLayer.get()); | |
|
mtklein
2013/08/28 18:20:46
Would it be effectively the same to call canvasLay
scroggo
2013/08/28 20:42:02
No, addCanvas refs it, so we want canvasLayer to g
djsollen
2013/08/29 15:09:19
the detach would run first which would push the re
| |
| 287 } | |
| 288 | |
| 289 return canvas.detach(); | |
| 290 } | |
| 291 | |
| 292 //////////////////////////////////////////////////////////////////////////////// | |
| 293 | |
| 294 void SkCanvasUtils::ReleaseCanvasState(SkCanvasState* state) { | |
| 295 SkDELETE(state); | |
| 296 } | |
| OLD | NEW |