|
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 "SkCanvasStateUtils.h" |
|
9 |
|
10 #include "SkBitmapDevice.h" |
|
11 #include "SkCanvas.h" |
|
12 #include "SkCanvasStack.h" |
|
13 #include "SkErrorInternals.h" |
|
14 #include "SkWriter32.h" |
|
15 |
|
16 #define CANVAS_STATE_VERSION 1 |
|
17 /* |
|
18 * WARNING: The structs below are part of a stable ABI and as such we explicitly |
|
19 * use unambigious primitives (e.g. int32_t instead of an enum). |
|
20 * |
|
21 * ANY CHANGES TO THE STRUCTS BELOW THAT IMPACT THE ABI SHOULD RESULT IN AN |
|
22 * UPDATE OF THE CANVAS_STATE_VERSION. SUCH CHANGES SHOULD ONLY BE MADE IF |
|
23 * ABSOLUTELY NECESSARY! |
|
24 */ |
|
25 enum RasterConfigs { |
|
26 kUnknown_RasterConfig = 0, |
|
27 kRGB_565_RasterConfig = 1, |
|
28 kARGB_8888_RasterConfig = 2 |
|
29 }; |
|
30 typedef int32_t RasterConfig; |
|
31 |
|
32 enum CanvasBackends { |
|
33 kUnknown_CanvasBackend = 0, |
|
34 kRaster_CanvasBackend = 1, |
|
35 kGPU_CanvasBackend = 2, |
|
36 kPDF_CanvasBackend = 3 |
|
37 }; |
|
38 typedef int32_t CanvasBackend; |
|
39 |
|
40 struct ClipRect { |
|
41 int32_t left, top, right, bottom; |
|
42 }; |
|
43 |
|
44 struct SkMCState { |
|
45 float matrix[9]; |
|
46 // NOTE: this only works for non-antialiased clips |
|
47 int32_t clipRectCount; |
|
48 ClipRect* clipRects; |
|
49 }; |
|
50 |
|
51 // NOTE: If you add more members, bump CanvasState::version. |
|
52 struct SkCanvasLayerState { |
|
53 CanvasBackend type; |
|
54 int32_t x, y; |
|
55 int32_t width; |
|
56 int32_t height; |
|
57 |
|
58 SkMCState mcState; |
|
59 |
|
60 union { |
|
61 struct { |
|
62 RasterConfig config; // pixel format: a value from RasterConfigs. |
|
63 size_t rowBytes; // Number of bytes from start of one line to next. |
|
64 void* pixels; // The pixels, all (height * rowBytes) of them. |
|
65 } raster; |
|
66 struct { |
|
67 int32_t textureID; |
|
68 } gpu; |
|
69 }; |
|
70 }; |
|
71 |
|
72 class SkCanvasState { |
|
73 public: |
|
74 SkCanvasState(SkCanvas* canvas) { |
|
75 SkASSERT(canvas); |
|
76 version = CANVAS_STATE_VERSION; |
|
77 width = canvas->getDeviceSize().width(); |
|
78 height = canvas->getDeviceSize().height(); |
|
79 layerCount = 0; |
|
80 layers = NULL; |
|
81 originalCanvas = SkRef(canvas); |
|
82 |
|
83 mcState.clipRectCount = 0; |
|
84 mcState.clipRects = NULL; |
|
85 } |
|
86 |
|
87 ~SkCanvasState() { |
|
88 // loop through the layers and free the data allocated to the clipRects |
|
89 for (int i = 0; i < layerCount; ++i) { |
|
90 sk_free(layers[i].mcState.clipRects); |
|
91 } |
|
92 |
|
93 sk_free(mcState.clipRects); |
|
94 sk_free(layers); |
|
95 |
|
96 // it is now safe to free the canvas since there should be no remaining |
|
97 // references to the content that is referenced by this canvas (e.g. pixels) |
|
98 originalCanvas->unref(); |
|
99 } |
|
100 |
|
101 /** |
|
102 * The version this struct was built with. This field must always appear |
|
103 * first in the struct so that when the versions don't match (and the |
|
104 * remaining contents and size are potentially different) we can still |
|
105 * compare the version numbers. |
|
106 */ |
|
107 int32_t version; |
|
108 |
|
109 int32_t width; |
|
110 int32_t height; |
|
111 |
|
112 SkMCState mcState; |
|
113 |
|
114 int32_t layerCount; |
|
115 SkCanvasLayerState* layers; |
|
116 |
|
117 private: |
|
118 SkCanvas* originalCanvas; |
|
119 }; |
|
120 |
|
121 //////////////////////////////////////////////////////////////////////////////// |
|
122 |
|
123 class ClipValidator : public SkCanvas::ClipVisitor { |
|
124 public: |
|
125 ClipValidator() : fFailed(false) {} |
|
126 bool failed() { return fFailed; } |
|
127 |
|
128 // ClipVisitor |
|
129 virtual void clipRect(const SkRect& rect, SkRegion::Op op, bool antialias) SK_OVERRIDE { |
|
130 fFailed |= antialias; |
|
131 } |
|
132 |
|
133 virtual void clipRRect(const SkRRect& rrect, SkRegion::Op op, bool antialias) SK_OVERRIDE { |
|
134 fFailed |= antialias; |
|
135 } |
|
136 |
|
137 virtual void clipPath(const SkPath&, SkRegion::Op, bool antialias) SK_OVERRIDE { |
|
138 fFailed |= antialias; |
|
139 } |
|
140 |
|
141 private: |
|
142 bool fFailed; |
|
143 }; |
|
144 |
|
145 static void setup_MC_state(SkMCState* state, const SkMatrix& matrix, const SkRegion& clip) { |
|
146 // initialize the struct |
|
147 state->clipRectCount = 0; |
|
148 |
|
149 // capture the matrix |
|
150 for (int i = 0; i < 9; i++) { |
|
151 state->matrix[i] = matrix.get(i); |
|
152 } |
|
153 |
|
154 /* |
|
155 * capture the clip |
|
156 * |
|
157 * storage is allocated on the stack for the first 4 rects. This value was |
|
158 * chosen somewhat arbitrarily, but does allow us to represent simple clips |
|
159 * and some more common complex clips (e.g. a clipRect with a sub-rect |
|
160 * clipped out of its interior) without needing to malloc any additional memory. |
|
161 */ |
|
162 SkSWriter32<4*sizeof(ClipRect)> clipWriter; |
|
163 |
|
164 if (!clip.isEmpty()) { |
|
165 // only returns the b/w clip so aa clips fail |
|
166 SkRegion::Iterator clip_iterator(clip); |
|
167 for (; !clip_iterator.done(); clip_iterator.next()) { |
|
168 // this assumes the SkIRect is stored in l,t,r,b ordering which |
|
169 // matches the ordering of our ClipRect struct |
|
170 clipWriter.writeIRect(clip_iterator.rect()); |
|
171 state->clipRectCount++; |
|
172 } |
|
173 } |
|
174 |
|
175 // allocate memory for the clip then and copy them to the struct |
|
176 state->clipRects = (ClipRect*) sk_malloc_throw(clipWriter.bytesWritten()); |
|
177 clipWriter.flatten(state->clipRects); |
|
178 } |
|
179 |
|
180 |
|
181 |
|
182 SkCanvasState* SkCanvasStateUtils::CaptureCanvasState(SkCanvas* canvas) { |
|
183 SkASSERT(canvas); |
|
184 |
|
185 // Check the clip can be decomposed into rectangles (i.e. no soft clips). |
|
186 ClipValidator validator; |
|
187 canvas->replayClips(&validator); |
|
188 if (validator.failed()) { |
|
189 SkErrorInternals::SetError(kInvalidOperation_SkError, |
|
190 "CaptureCanvasState does not support canvases with antialiased clips.\n"); |
|
191 return NULL; |
|
192 } |
|
193 |
|
194 SkAutoTDelete<SkCanvasState> canvasState(SkNEW_ARGS(SkCanvasState, (canvas))); |
|
195 |
|
196 // decompose the total matrix and clip |
|
197 setup_MC_state(&canvasState->mcState, canvas->getTotalMatrix(), |
|
198 canvas->internal_private_getTotalClip()); |
|
199 |
|
200 /* |
|
201 * decompose the layers |
|
202 * |
|
203 * storage is allocated on the stack for the first 3 layers. It is common in |
|
204 * some view systems (e.g. Android) that a few non-clipped layers are present |
|
205 * and we will not need to malloc any additional memory in those cases. |
|
206 */ |
|
207 SkSWriter32<3*sizeof(SkCanvasLayerState)> layerWriter; |
|
208 int layerCount = 0; |
|
209 for (SkCanvas::LayerIter layer(canvas, true/*skipEmptyClips*/); !layer.done(); layer.next()) { |
|
210 |
|
211 // we currently only work for bitmap backed devices |
|
212 const SkBitmap& bitmap = layer.device()->accessBitmap(true/*changePixels*/); |
|
213 if (bitmap.empty() || bitmap.isNull() || !bitmap.lockPixelsAreWritable()) { |
|
214 return NULL; |
|
215 } |
|
216 |
|
217 SkCanvasLayerState* layerState = |
|
218 (SkCanvasLayerState*) layerWriter.reserve(sizeof(SkCanvasLayerState)); |
|
219 layerState->type = kRaster_CanvasBackend; |
|
220 layerState->x = layer.x(); |
|
221 layerState->y = layer.y(); |
|
222 layerState->width = bitmap.width(); |
|
223 layerState->height = bitmap.height(); |
|
224 |
|
225 switch (bitmap.colorType()) { |
|
226 case kPMColor_SkColorType: |
|
227 layerState->raster.config = kARGB_8888_RasterConfig; |
|
228 break; |
|
229 case kRGB_565_SkColorType: |
|
230 layerState->raster.config = kRGB_565_RasterConfig; |
|
231 break; |
|
232 default: |
|
233 return NULL; |
|
234 } |
|
235 layerState->raster.rowBytes = bitmap.rowBytes(); |
|
236 layerState->raster.pixels = bitmap.getPixels(); |
|
237 |
|
238 setup_MC_state(&layerState->mcState, layer.matrix(), layer.clip()); |
|
239 layerCount++; |
|
240 } |
|
241 |
|
242 // allocate memory for the layers and then and copy them to the struct |
|
243 SkASSERT(layerWriter.bytesWritten() == layerCount * sizeof(SkCanvasLayerState)); |
|
244 canvasState->layerCount = layerCount; |
|
245 canvasState->layers = (SkCanvasLayerState*) sk_malloc_throw(layerWriter.bytesWritten()); |
|
246 layerWriter.flatten(canvasState->layers); |
|
247 |
|
248 // for now, just ignore any client supplied DrawFilter. |
|
249 if (canvas->getDrawFilter()) { |
|
250 // SkDEBUGF(("CaptureCanvasState will ignore the canvases draw filter.\n")); |
|
251 } |
|
252 |
|
253 return canvasState.detach(); |
|
254 } |
|
255 |
|
256 //////////////////////////////////////////////////////////////////////////////// |
|
257 |
|
258 static void setup_canvas_from_MC_state(const SkMCState& state, SkCanvas* canvas) { |
|
259 // reconstruct the matrix |
|
260 SkMatrix matrix; |
|
261 for (int i = 0; i < 9; i++) { |
|
262 matrix.set(i, state.matrix[i]); |
|
263 } |
|
264 |
|
265 // reconstruct the clip |
|
266 SkRegion clip; |
|
267 for (int i = 0; i < state.clipRectCount; ++i) { |
|
268 clip.op(SkIRect::MakeLTRB(state.clipRects[i].left, |
|
269 state.clipRects[i].top, |
|
270 state.clipRects[i].right, |
|
271 state.clipRects[i].bottom), |
|
272 SkRegion::kUnion_Op); |
|
273 } |
|
274 |
|
275 canvas->setMatrix(matrix); |
|
276 canvas->setClipRegion(clip); |
|
277 } |
|
278 |
|
279 static SkCanvas* create_canvas_from_canvas_layer(const SkCanvasLayerState& layerState) { |
|
280 SkASSERT(kRaster_CanvasBackend == layerState.type); |
|
281 |
|
282 SkBitmap bitmap; |
|
283 SkColorType colorType = |
|
284 layerState.raster.config == kARGB_8888_RasterConfig ? kPMColor_SkColorType : |
|
285 layerState.raster.config == kRGB_565_RasterConfig ? kRGB_565_SkColorType : |
|
286 kUnknown_SkColorType; |
|
287 |
|
288 if (colorType == kUnknown_SkColorType) { |
|
289 return NULL; |
|
290 } |
|
291 |
|
292 bitmap.installPixels(SkImageInfo::Make(layerState.width, layerState.height, |
|
293 colorType, kPremul_SkAlphaType), |
|
294 layerState.raster.pixels, layerState.raster.rowBytes, |
|
295 NULL, NULL); |
|
296 |
|
297 SkASSERT(!bitmap.empty()); |
|
298 SkASSERT(!bitmap.isNull()); |
|
299 |
|
300 SkAutoTUnref<SkCanvas> canvas(SkNEW_ARGS(SkCanvas, (bitmap))); |
|
301 |
|
302 // setup the matrix and clip |
|
303 setup_canvas_from_MC_state(layerState.mcState, canvas.get()); |
|
304 |
|
305 return canvas.detach(); |
|
306 } |
|
307 |
|
308 SkCanvas* SkCanvasStateUtils::CreateFromCanvasState(const SkCanvasState* state) { |
|
309 SkASSERT(state); |
|
310 |
|
311 // check that the versions match |
|
312 if (CANVAS_STATE_VERSION != state->version) { |
|
313 SkDebugf("CreateFromCanvasState version does not match the one use to create the input"); |
|
314 return NULL; |
|
315 } |
|
316 |
|
317 if (state->layerCount < 1) { |
|
318 return NULL; |
|
319 } |
|
320 |
|
321 SkAutoTUnref<SkCanvasStack> canvas(SkNEW_ARGS(SkCanvasStack, (state->width, state->height))); |
|
322 |
|
323 // setup the matrix and clip on the n-way canvas |
|
324 setup_canvas_from_MC_state(state->mcState, canvas); |
|
325 |
|
326 // Iterate over the layers and add them to the n-way canvas |
|
327 for (int i = state->layerCount - 1; i >= 0; --i) { |
|
328 SkAutoTUnref<SkCanvas> canvasLayer(create_canvas_from_canvas_layer(state->layers[i])); |
|
329 if (!canvasLayer.get()) { |
|
330 return NULL; |
|
331 } |
|
332 canvas->pushCanvas(canvasLayer.get(), SkIPoint::Make(state->layers[i].x, |
|
333 state->layers[i].y)); |
|
334 } |
|
335 |
|
336 return canvas.detach(); |
|
337 } |
|
338 |
|
339 //////////////////////////////////////////////////////////////////////////////// |
|
340 |
|
341 void SkCanvasStateUtils::ReleaseCanvasState(SkCanvasState* state) { |
|
342 SkDELETE(state); |
|
343 } |