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 "include/utils/SkCanvasStateUtils.h" |
9 | |
10 | #include "include/core/SkCanvas.h" |
11 | #include "src/core/SkClipOpPriv.h" |
12 | #include "src/core/SkDevice.h" |
13 | #include "src/core/SkRasterClip.h" |
14 | #include "src/core/SkWriter32.h" |
15 | #include "src/utils/SkCanvasStack.h" |
16 | |
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 A NEW |
22 | * NEW SUBCLASS OF SkCanvasState. SUCH CHANGES SHOULD ONLY BE MADE IF ABSOLUTELY |
23 | * NECESSARY! |
24 | * |
25 | * In order to test changes, run the CanvasState tests. gyp/canvas_state_lib.gyp |
26 | * describes how to create a library to pass to the CanvasState tests. The tests |
27 | * should succeed when building the library with your changes and passing that to |
28 | * the tests running in the unchanged Skia. |
29 | */ |
30 | enum RasterConfigs { |
31 | kUnknown_RasterConfig = 0, |
32 | kRGB_565_RasterConfig = 1, |
33 | kARGB_8888_RasterConfig = 2 |
34 | }; |
35 | typedef int32_t RasterConfig; |
36 | |
37 | enum CanvasBackends { |
38 | kUnknown_CanvasBackend = 0, |
39 | kRaster_CanvasBackend = 1, |
40 | kGPU_CanvasBackend = 2, |
41 | kPDF_CanvasBackend = 3 |
42 | }; |
43 | typedef int32_t CanvasBackend; |
44 | |
45 | struct ClipRect { |
46 | int32_t left, top, right, bottom; |
47 | }; |
48 | |
49 | struct SkMCState { |
50 | float matrix[9]; |
51 | // NOTE: this only works for non-antialiased clips |
52 | int32_t clipRectCount; |
53 | ClipRect* clipRects; |
54 | }; |
55 | |
56 | // NOTE: If you add more members, create a new subclass of SkCanvasState with a |
57 | // new CanvasState::version. |
58 | struct SkCanvasLayerState { |
59 | CanvasBackend type; |
60 | int32_t x, y; |
61 | int32_t width; |
62 | int32_t height; |
63 | |
64 | SkMCState mcState; |
65 | |
66 | union { |
67 | struct { |
68 | RasterConfig config; // pixel format: a value from RasterConfigs. |
69 | uint64_t rowBytes; // Number of bytes from start of one line to next. |
70 | void* pixels; // The pixels, all (height * rowBytes) of them. |
71 | } raster; |
72 | struct { |
73 | int32_t textureID; |
74 | } gpu; |
75 | }; |
76 | }; |
77 | |
78 | class SkCanvasState { |
79 | public: |
80 | SkCanvasState(int32_t version, SkCanvas* canvas) { |
81 | SkASSERT(canvas); |
82 | this->version = version; |
83 | width = canvas->getBaseLayerSize().width(); |
84 | height = canvas->getBaseLayerSize().height(); |
85 | |
86 | } |
87 | |
88 | /** |
89 | * The version this struct was built with. This field must always appear |
90 | * first in the struct so that when the versions don't match (and the |
91 | * remaining contents and size are potentially different) we can still |
92 | * compare the version numbers. |
93 | */ |
94 | int32_t version; |
95 | int32_t width; |
96 | int32_t height; |
97 | int32_t alignmentPadding; |
98 | }; |
99 | |
100 | class SkCanvasState_v1 : public SkCanvasState { |
101 | public: |
102 | static const int32_t kVersion = 1; |
103 | |
104 | SkCanvasState_v1(SkCanvas* canvas) : INHERITED(kVersion, canvas) { |
105 | layerCount = 0; |
106 | layers = nullptr; |
107 | mcState.clipRectCount = 0; |
108 | mcState.clipRects = nullptr; |
109 | originalCanvas = canvas; |
110 | } |
111 | |
112 | ~SkCanvasState_v1() { |
113 | // loop through the layers and free the data allocated to the clipRects |
114 | for (int i = 0; i < layerCount; ++i) { |
115 | sk_free(layers[i].mcState.clipRects); |
116 | } |
117 | |
118 | sk_free(mcState.clipRects); |
119 | sk_free(layers); |
120 | } |
121 | |
122 | SkMCState mcState; |
123 | |
124 | int32_t layerCount; |
125 | SkCanvasLayerState* layers; |
126 | private: |
127 | SkCanvas* originalCanvas; |
128 | typedef SkCanvasState INHERITED; |
129 | }; |
130 | |
131 | //////////////////////////////////////////////////////////////////////////////// |
132 | |
133 | static void setup_MC_state(SkMCState* state, const SkMatrix& matrix, const SkIRect& clip) { |
134 | // initialize the struct |
135 | state->clipRectCount = 0; |
136 | |
137 | // capture the matrix |
138 | for (int i = 0; i < 9; i++) { |
139 | state->matrix[i] = matrix.get(i); |
140 | } |
141 | |
142 | /* |
143 | * We only support a single clipRect, so we take the clip's bounds. Clients have long made |
144 | * this assumption anyway, so this restriction is fine. |
145 | */ |
146 | SkSWriter32<sizeof(ClipRect)> clipWriter; |
147 | |
148 | if (!clip.isEmpty()) { |
149 | state->clipRectCount = 1; |
150 | state->clipRects = (ClipRect*)sk_malloc_throw(sizeof(ClipRect)); |
151 | state->clipRects->left = clip.fLeft; |
152 | state->clipRects->top = clip.fTop; |
153 | state->clipRects->right = clip.fRight; |
154 | state->clipRects->bottom = clip.fBottom; |
155 | } |
156 | } |
157 | |
158 | |
159 | |
160 | SkCanvasState* SkCanvasStateUtils::CaptureCanvasState(SkCanvas* canvas) { |
161 | SkASSERT(canvas); |
162 | |
163 | // Check the clip can be decomposed into rectangles (i.e. no soft clips). |
164 | if (canvas->androidFramework_isClipAA()) { |
165 | return nullptr; |
166 | } |
167 | |
168 | std::unique_ptr<SkCanvasState_v1> canvasState(new SkCanvasState_v1(canvas)); |
169 | |
170 | setup_MC_state(&canvasState->mcState, canvas->getTotalMatrix(), canvas->getDeviceClipBounds()); |
171 | |
172 | /* |
173 | * decompose the layers |
174 | * |
175 | * storage is allocated on the stack for the first 3 layers. It is common in |
176 | * some view systems (e.g. Android) that a few non-clipped layers are present |
177 | * and we will not need to malloc any additional memory in those cases. |
178 | */ |
179 | SkSWriter32<3*sizeof(SkCanvasLayerState)> layerWriter; |
180 | int layerCount = 0; |
181 | for (SkCanvas::LayerIter layer(canvas); !layer.done(); layer.next()) { |
182 | |
183 | // we currently only work for bitmap backed devices |
184 | SkPixmap pmap; |
185 | if (!layer.device()->accessPixels(&pmap) || 0 == pmap.width() || 0 == pmap.height()) { |
186 | return nullptr; |
187 | } |
188 | |
189 | SkCanvasLayerState* layerState = |
190 | (SkCanvasLayerState*) layerWriter.reserve(sizeof(SkCanvasLayerState)); |
191 | layerState->type = kRaster_CanvasBackend; |
192 | layerState->x = layer.x(); |
193 | layerState->y = layer.y(); |
194 | layerState->width = pmap.width(); |
195 | layerState->height = pmap.height(); |
196 | |
197 | switch (pmap.colorType()) { |
198 | case kN32_SkColorType: |
199 | layerState->raster.config = kARGB_8888_RasterConfig; |
200 | break; |
201 | case kRGB_565_SkColorType: |
202 | layerState->raster.config = kRGB_565_RasterConfig; |
203 | break; |
204 | default: |
205 | return nullptr; |
206 | } |
207 | layerState->raster.rowBytes = pmap.rowBytes(); |
208 | layerState->raster.pixels = pmap.writable_addr(); |
209 | |
210 | setup_MC_state(&layerState->mcState, layer.matrix(), layer.clipBounds()); |
211 | layerCount++; |
212 | } |
213 | |
214 | // allocate memory for the layers and then and copy them to the struct |
215 | SkASSERT(layerWriter.bytesWritten() == layerCount * sizeof(SkCanvasLayerState)); |
216 | canvasState->layerCount = layerCount; |
217 | canvasState->layers = (SkCanvasLayerState*) sk_malloc_throw(layerWriter.bytesWritten()); |
218 | layerWriter.flatten(canvasState->layers); |
219 | |
220 | return canvasState.release(); |
221 | } |
222 | |
223 | //////////////////////////////////////////////////////////////////////////////// |
224 | |
225 | static void setup_canvas_from_MC_state(const SkMCState& state, SkCanvas* canvas) { |
226 | // reconstruct the matrix |
227 | SkMatrix matrix; |
228 | for (int i = 0; i < 9; i++) { |
229 | matrix.set(i, state.matrix[i]); |
230 | } |
231 | |
232 | // only realy support 1 rect, so if the caller (legacy?) sent us more, we just take the bounds |
233 | // of what they sent. |
234 | SkIRect bounds = SkIRect::MakeEmpty(); |
235 | if (state.clipRectCount > 0) { |
236 | bounds.setLTRB(state.clipRects[0].left, |
237 | state.clipRects[0].top, |
238 | state.clipRects[0].right, |
239 | state.clipRects[0].bottom); |
240 | for (int i = 1; i < state.clipRectCount; ++i) { |
241 | bounds.join({state.clipRects[i].left, |
242 | state.clipRects[i].top, |
243 | state.clipRects[i].right, |
244 | state.clipRects[i].bottom}); |
245 | } |
246 | } |
247 | |
248 | canvas->clipRect(SkRect::Make(bounds)); |
249 | canvas->concat(matrix); |
250 | } |
251 | |
252 | static std::unique_ptr<SkCanvas> |
253 | make_canvas_from_canvas_layer(const SkCanvasLayerState& layerState) { |
254 | SkASSERT(kRaster_CanvasBackend == layerState.type); |
255 | |
256 | SkBitmap bitmap; |
257 | SkColorType colorType = |
258 | layerState.raster.config == kARGB_8888_RasterConfig ? kN32_SkColorType : |
259 | layerState.raster.config == kRGB_565_RasterConfig ? kRGB_565_SkColorType : |
260 | kUnknown_SkColorType; |
261 | |
262 | if (colorType == kUnknown_SkColorType) { |
263 | return nullptr; |
264 | } |
265 | |
266 | bitmap.installPixels(SkImageInfo::Make(layerState.width, layerState.height, |
267 | colorType, kPremul_SkAlphaType), |
268 | layerState.raster.pixels, (size_t) layerState.raster.rowBytes); |
269 | |
270 | SkASSERT(!bitmap.empty()); |
271 | SkASSERT(!bitmap.isNull()); |
272 | |
273 | std::unique_ptr<SkCanvas> canvas(new SkCanvas(bitmap)); |
274 | |
275 | // setup the matrix and clip |
276 | setup_canvas_from_MC_state(layerState.mcState, canvas.get()); |
277 | |
278 | return canvas; |
279 | } |
280 | |
281 | std::unique_ptr<SkCanvas> SkCanvasStateUtils::MakeFromCanvasState(const SkCanvasState* state) { |
282 | SkASSERT(state); |
283 | // Currently there is only one possible version. |
284 | SkASSERT(SkCanvasState_v1::kVersion == state->version); |
285 | |
286 | const SkCanvasState_v1* state_v1 = static_cast<const SkCanvasState_v1*>(state); |
287 | |
288 | if (state_v1->layerCount < 1) { |
289 | return nullptr; |
290 | } |
291 | |
292 | std::unique_ptr<SkCanvasStack> canvas(new SkCanvasStack(state->width, state->height)); |
293 | |
294 | // setup the matrix and clip on the n-way canvas |
295 | setup_canvas_from_MC_state(state_v1->mcState, canvas.get()); |
296 | |
297 | // Iterate over the layers and add them to the n-way canvas |
298 | for (int i = state_v1->layerCount - 1; i >= 0; --i) { |
299 | std::unique_ptr<SkCanvas> canvasLayer = make_canvas_from_canvas_layer(state_v1->layers[i]); |
300 | if (!canvasLayer.get()) { |
301 | return nullptr; |
302 | } |
303 | canvas->pushCanvas(std::move(canvasLayer), SkIPoint::Make(state_v1->layers[i].x, |
304 | state_v1->layers[i].y)); |
305 | } |
306 | |
307 | return std::move(canvas); |
308 | } |
309 | |
310 | //////////////////////////////////////////////////////////////////////////////// |
311 | |
312 | void SkCanvasStateUtils::ReleaseCanvasState(SkCanvasState* state) { |
313 | SkASSERT(!state || SkCanvasState_v1::kVersion == state->version); |
314 | // Upcast to the correct version of SkCanvasState. This avoids having a virtual destructor on |
315 | // SkCanvasState. That would be strange since SkCanvasState has no other virtual functions, and |
316 | // instead uses the field "version" to determine how to behave. |
317 | delete static_cast<SkCanvasState_v1*>(state); |
318 | } |
319 | |