Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(10)

Side by Side Diff: src/utils/SkCanvasUtils.cpp

Issue 23545017: Create a semi-stable API for capturing the state of an SkCanvas and reconstructing that state acros… (Closed) Base URL: https://skia.googlecode.com/svn/trunk
Patch Set: Created 7 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« include/utils/SkCanvasUtils.h ('K') | « include/utils/SkCanvasUtils.h ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(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 }
OLDNEW
« include/utils/SkCanvasUtils.h ('K') | « include/utils/SkCanvasUtils.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698