OLD | NEW |
| (Empty) |
1 /* | |
2 * Copyright 2014 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 #ifndef SkMatrixClipStateMgr_DEFINED | |
8 #define SkMatrixClipStateMgr_DEFINED | |
9 | |
10 #include "SkCanvas.h" | |
11 #include "SkMatrix.h" | |
12 #include "SkRegion.h" | |
13 #include "SkRRect.h" | |
14 #include "SkTypes.h" | |
15 #include "SkTDArray.h" | |
16 | |
17 class SkPictureRecord; | |
18 class SkWriter32; | |
19 | |
20 // The SkMatrixClipStateMgr collapses the matrix/clip state of an SkPicture into | |
21 // a series of save/restore blocks of consistent matrix clip state, e.g.: | |
22 // | |
23 // save | |
24 // clip(s) | |
25 // concat | |
26 // ... draw ops ... | |
27 // restore | |
28 // | |
29 // SaveLayers simply add another level, e.g.: | |
30 // | |
31 // save | |
32 // clip(s) | |
33 // concat | |
34 // ... draw ops ... | |
35 // saveLayer | |
36 // save | |
37 // clip(s) | |
38 // concat | |
39 // ... draw ops ... | |
40 // restore | |
41 // restore | |
42 // restore | |
43 // | |
44 // As a side effect of this process all saves and saveLayers will become | |
45 // kMatrixClip_SaveFlag style saves/saveLayers. | |
46 | |
47 // The SkMatrixClipStateMgr works by intercepting all the save*, restore, clip*, | |
48 // and matrix calls sent to SkCanvas in order to track the current matrix/clip | |
49 // state. All the other canvas calls get funnelled into a generic "call" entry | |
50 // point that signals that a state block is required. | |
51 class SkMatrixClipStateMgr { | |
52 public: | |
53 static const int32_t kIdentityWideOpenStateID = 0; | |
54 static const int kIdentityMatID = 0; | |
55 | |
56 class MatrixClipState : SkNoncopyable { | |
57 public: | |
58 class MatrixInfo { | |
59 public: | |
60 void reset() { | |
61 fMatrixID = kIdentityMatID; | |
62 fMatrix.reset(); | |
63 } | |
64 | |
65 void preTranslate(SkScalar dx, SkScalar dy) { | |
66 fMatrixID = -1; | |
67 fMatrix.preTranslate(dx, dy); | |
68 } | |
69 | |
70 void preScale(SkScalar sx, SkScalar sy) { | |
71 fMatrixID = -1; | |
72 fMatrix.preScale(sx, sy); | |
73 } | |
74 | |
75 void preRotate(SkScalar degrees) { | |
76 fMatrixID = -1; | |
77 fMatrix.preRotate(degrees); | |
78 } | |
79 | |
80 void preSkew(SkScalar sx, SkScalar sy) { | |
81 fMatrixID = -1; | |
82 fMatrix.preSkew(sx, sy); | |
83 } | |
84 | |
85 void preConcat(const SkMatrix& matrix) { | |
86 fMatrixID = -1; | |
87 fMatrix.preConcat(matrix); | |
88 } | |
89 | |
90 void setMatrix(const SkMatrix& matrix) { | |
91 fMatrixID = -1; | |
92 fMatrix = matrix; | |
93 } | |
94 | |
95 int getID(SkMatrixClipStateMgr* mgr) { | |
96 if (fMatrixID >= 0) { | |
97 return fMatrixID; | |
98 } | |
99 | |
100 fMatrixID = mgr->addMatToDict(fMatrix); | |
101 return fMatrixID; | |
102 } | |
103 | |
104 private: | |
105 SkMatrix fMatrix; | |
106 int fMatrixID; | |
107 | |
108 typedef SkNoncopyable INHERITED; | |
109 }; | |
110 | |
111 class ClipInfo : SkNoncopyable { | |
112 public: | |
113 ClipInfo() {} | |
114 | |
115 bool clipRect(const SkRect& rect, | |
116 SkRegion::Op op, | |
117 bool doAA, | |
118 int matrixID) { | |
119 ClipOp* newClip = fClips.append(); | |
120 newClip->fClipType = kRect_ClipType; | |
121 newClip->fGeom.fRRect.setRect(rect); // storing the clipRect i
n the RRect | |
122 newClip->fOp = op; | |
123 newClip->fDoAA = doAA; | |
124 newClip->fMatrixID = matrixID; | |
125 return false; | |
126 } | |
127 | |
128 bool clipRRect(const SkRRect& rrect, | |
129 SkRegion::Op op, | |
130 bool doAA, | |
131 int matrixID) { | |
132 ClipOp* newClip = fClips.append(); | |
133 newClip->fClipType = kRRect_ClipType; | |
134 newClip->fGeom.fRRect = rrect; | |
135 newClip->fOp = op; | |
136 newClip->fDoAA = doAA; | |
137 newClip->fMatrixID = matrixID; | |
138 return false; | |
139 } | |
140 | |
141 bool clipPath(SkPictureRecord* picRecord, | |
142 const SkPath& path, | |
143 SkRegion::Op op, | |
144 bool doAA, | |
145 int matrixID); | |
146 bool clipRegion(SkPictureRecord* picRecord, | |
147 int regionID, | |
148 SkRegion::Op op, | |
149 int matrixID); | |
150 void writeClip(int* curMatID, SkMatrixClipStateMgr* mgr); | |
151 | |
152 SkDEBUGCODE(int numClips() const { return fClips.count(); }) | |
153 | |
154 private: | |
155 enum ClipType { | |
156 kRect_ClipType, | |
157 kRRect_ClipType, | |
158 kPath_ClipType, | |
159 kRegion_ClipType | |
160 }; | |
161 | |
162 class ClipOp { | |
163 public: | |
164 ClipType fClipType; | |
165 | |
166 union { | |
167 SkRRect fRRect; // also stores clip rect | |
168 int fPathID; | |
169 int fRegionID; | |
170 } fGeom; | |
171 | |
172 bool fDoAA; | |
173 SkRegion::Op fOp; | |
174 | |
175 // The CTM in effect when this clip call was issued | |
176 int fMatrixID; | |
177 }; | |
178 | |
179 SkTDArray<ClipOp> fClips; | |
180 | |
181 typedef SkNoncopyable INHERITED; | |
182 }; | |
183 | |
184 MatrixClipState(MatrixClipState* prev) | |
185 : fPrev(prev) | |
186 { | |
187 fHasOpen = false; | |
188 | |
189 if (NULL == prev) { | |
190 fLayerID = 0; | |
191 | |
192 fMatrixInfoStorage.reset(); | |
193 fMatrixInfo = &fMatrixInfoStorage; | |
194 fClipInfo = &fClipInfoStorage; // ctor handles init of fClipInf
oStorage | |
195 | |
196 // The identity/wide-open-clip state is current by default | |
197 fMCStateID = kIdentityWideOpenStateID; | |
198 #ifdef SK_DEBUG | |
199 fExpectedDepth = 1; | |
200 #endif | |
201 } | |
202 else { | |
203 fLayerID = prev->fLayerID; | |
204 | |
205 fMatrixInfoStorage = *prev->fMatrixInfo; | |
206 fMatrixInfo = &fMatrixInfoStorage; | |
207 | |
208 // We don't copy the ClipOps of the previous clip states | |
209 fClipInfo = &fClipInfoStorage; | |
210 | |
211 // Initially a new save/saveLayer represents the same MC state | |
212 // as its predecessor. | |
213 fMCStateID = prev->fMCStateID; | |
214 #ifdef SK_DEBUG | |
215 fExpectedDepth = prev->fExpectedDepth; | |
216 #endif | |
217 } | |
218 | |
219 fIsSaveLayer = false; | |
220 } | |
221 | |
222 MatrixInfo* fMatrixInfo; | |
223 MatrixInfo fMatrixInfoStorage; | |
224 | |
225 ClipInfo* fClipInfo; | |
226 ClipInfo fClipInfoStorage; | |
227 | |
228 // Tracks the current depth of saveLayers to support the isDrawingToLaye
r call | |
229 int fLayerID; | |
230 // Does this MC state represent a saveLayer call? | |
231 bool fIsSaveLayer; | |
232 | |
233 // The next field is only valid when fIsSaveLayer is set. | |
234 SkTDArray<int>* fSavedSkipOffsets; | |
235 | |
236 // Does the MC state have an open block in the skp? | |
237 bool fHasOpen; | |
238 | |
239 MatrixClipState* fPrev; | |
240 | |
241 #ifdef SK_DEBUG | |
242 int fExpectedDepth; // debugging aid | |
243 #endif | |
244 | |
245 int32_t fMCStateID; | |
246 }; | |
247 | |
248 enum CallType { | |
249 kMatrix_CallType, | |
250 kClip_CallType, | |
251 kOther_CallType | |
252 }; | |
253 | |
254 SkMatrixClipStateMgr(); | |
255 ~SkMatrixClipStateMgr(); | |
256 | |
257 void init(SkPictureRecord* picRecord) { | |
258 // Note: we're not taking a ref here. It is expected that the SkMatrixCl
ipStateMgr | |
259 // is owned by the SkPictureRecord object | |
260 fPicRecord = picRecord; | |
261 } | |
262 | |
263 SkPictureRecord* getPicRecord() { return fPicRecord; } | |
264 | |
265 // TODO: need to override canvas' getSaveCount. Right now we pass the | |
266 // save* and restore calls on to the base SkCanvas in SkPictureRecord but | |
267 // this duplicates effort. | |
268 int getSaveCount() const { return fMatrixClipStack.count(); } | |
269 | |
270 int save(); | |
271 | |
272 int saveLayer(const SkRect* bounds, const SkPaint* paint, SkCanvas::SaveFlag
s flags); | |
273 | |
274 bool isDrawingToLayer() const { | |
275 return fCurMCState->fLayerID > 0; | |
276 } | |
277 | |
278 void restore(); | |
279 | |
280 void translate(SkScalar dx, SkScalar dy) { | |
281 this->call(kMatrix_CallType); | |
282 fCurMCState->fMatrixInfo->preTranslate(dx, dy); | |
283 } | |
284 | |
285 void scale(SkScalar sx, SkScalar sy) { | |
286 this->call(kMatrix_CallType); | |
287 fCurMCState->fMatrixInfo->preScale(sx, sy); | |
288 } | |
289 | |
290 void rotate(SkScalar degrees) { | |
291 this->call(kMatrix_CallType); | |
292 fCurMCState->fMatrixInfo->preRotate(degrees); | |
293 } | |
294 | |
295 void skew(SkScalar sx, SkScalar sy) { | |
296 this->call(kMatrix_CallType); | |
297 fCurMCState->fMatrixInfo->preSkew(sx, sy); | |
298 } | |
299 | |
300 void concat(const SkMatrix& matrix) { | |
301 this->call(kMatrix_CallType); | |
302 fCurMCState->fMatrixInfo->preConcat(matrix); | |
303 } | |
304 | |
305 void setMatrix(const SkMatrix& matrix) { | |
306 this->call(kMatrix_CallType); | |
307 fCurMCState->fMatrixInfo->setMatrix(matrix); | |
308 } | |
309 | |
310 bool clipRect(const SkRect& rect, SkRegion::Op op, bool doAA) { | |
311 this->call(SkMatrixClipStateMgr::kClip_CallType); | |
312 return fCurMCState->fClipInfo->clipRect(rect, op, doAA, | |
313 fCurMCState->fMatrixInfo->getID(
this)); | |
314 } | |
315 | |
316 bool clipRRect(const SkRRect& rrect, SkRegion::Op op, bool doAA) { | |
317 this->call(SkMatrixClipStateMgr::kClip_CallType); | |
318 return fCurMCState->fClipInfo->clipRRect(rrect, op, doAA, | |
319 fCurMCState->fMatrixInfo->getID
(this)); | |
320 } | |
321 | |
322 bool clipPath(const SkPath& path, SkRegion::Op op, bool doAA) { | |
323 this->call(SkMatrixClipStateMgr::kClip_CallType); | |
324 return fCurMCState->fClipInfo->clipPath(fPicRecord, path, op, doAA, | |
325 fCurMCState->fMatrixInfo->getID(
this)); | |
326 } | |
327 | |
328 bool clipRegion(const SkRegion& region, SkRegion::Op op) { | |
329 this->call(SkMatrixClipStateMgr::kClip_CallType); | |
330 int regionID = this->addRegionToDict(region); | |
331 return fCurMCState->fClipInfo->clipRegion(fPicRecord, regionID, op, | |
332 fCurMCState->fMatrixInfo->getI
D(this)); | |
333 } | |
334 | |
335 bool call(CallType callType); | |
336 | |
337 void fillInSkips(SkWriter32* writer, int32_t restoreOffset); | |
338 | |
339 void finish(); | |
340 | |
341 protected: | |
342 SkPictureRecord* fPicRecord; | |
343 | |
344 uint32_t fMatrixClipStackStorage[43]; // sized to fit 2 clip states | |
345 SkDeque fMatrixClipStack; | |
346 MatrixClipState* fCurMCState; | |
347 | |
348 // This dictionary doesn't actually de-duplicate the matrices (except for th
e | |
349 // identity matrix). It merely stores the matrices and allows them to be loo
ked | |
350 // up by ID later. The de-duplication mainly falls upon the matrix/clip stac
k | |
351 // which stores the ID so a revisited clip/matrix (via popping the stack) wi
ll | |
352 // use the same ID. | |
353 SkTDArray<SkMatrix> fMatrixDict; | |
354 | |
355 SkTDArray<SkRegion*> fRegionDict; | |
356 | |
357 // The MCStateID of the state currently in effect in the byte stream. 0 if n
one. | |
358 int32_t fCurOpenStateID; | |
359 // The skip offsets for the current open state. These are the locations in t
he | |
360 // skp that must be filled in when the current open state is closed. These a
re | |
361 // here rather then distributed across the MatrixClipState's because saveLay
ers | |
362 // can cause MC states to be nested. | |
363 SkTDArray<int32_t> *fSkipOffsets; // TODO: should we store u32 or size_t i
nstead? | |
364 | |
365 SkDEBUGCODE(void validate();) | |
366 | |
367 int MCStackPush(); | |
368 | |
369 void addClipOffset(size_t offset) { | |
370 SkASSERT(NULL != fSkipOffsets); | |
371 SkASSERT(kIdentityWideOpenStateID != fCurOpenStateID); | |
372 SkASSERT(fCurMCState->fHasOpen); | |
373 SkASSERT(!fCurMCState->fIsSaveLayer); | |
374 | |
375 *fSkipOffsets->append() = SkToS32(offset); | |
376 } | |
377 | |
378 void writeDeltaMat(int currentMatID, int desiredMatID); | |
379 static int32_t NewMCStateID(); | |
380 | |
381 int addRegionToDict(const SkRegion& region); | |
382 const SkRegion* lookupRegion(int index) { | |
383 SkASSERT(index >= 0 && index < fRegionDict.count()); | |
384 return fRegionDict[index]; | |
385 } | |
386 | |
387 // TODO: add stats to check if the dictionary really does | |
388 // reduce the size of the SkPicture. | |
389 int addMatToDict(const SkMatrix& mat); | |
390 const SkMatrix& lookupMat(int index) { | |
391 SkASSERT(index >= 0 && index < fMatrixDict.count()); | |
392 return fMatrixDict[index]; | |
393 } | |
394 | |
395 bool isNestingMCState(int stateID); | |
396 | |
397 #ifdef SK_DEBUG | |
398 int fActualDepth; | |
399 #endif | |
400 | |
401 // save layers are nested within a specific MC state. This stack tracks | |
402 // the nesting MC state's ID as save layers are pushed and popped. | |
403 SkTDArray<int> fStateIDStack; | |
404 }; | |
405 | |
406 #endif | |
OLD | NEW |