|
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 |
|
8 #include "SkMatrixClipStateMgr.h" |
|
9 #include "SkPictureRecord.h" |
|
10 |
|
11 bool SkMatrixClipStateMgr::MatrixClipState::ClipInfo::clipPath(SkPictureRecord* picRecord, |
|
12 const SkPath& path, |
|
13 SkRegion::Op op, |
|
14 bool doAA, |
|
15 int matrixID) { |
|
16 int pathID = picRecord->addPathToHeap(path); |
|
17 |
|
18 ClipOp* newClip = fClips.append(); |
|
19 newClip->fClipType = kPath_ClipType; |
|
20 newClip->fGeom.fPathID = pathID; |
|
21 newClip->fOp = op; |
|
22 newClip->fDoAA = doAA; |
|
23 newClip->fMatrixID = matrixID; |
|
24 return false; |
|
25 } |
|
26 |
|
27 bool SkMatrixClipStateMgr::MatrixClipState::ClipInfo::clipRegion(SkPictureRecord* picRecord, |
|
28 int regionID, |
|
29 SkRegion::Op op, |
|
30 int matrixID) { |
|
31 ClipOp* newClip = fClips.append(); |
|
32 newClip->fClipType = kRegion_ClipType; |
|
33 newClip->fGeom.fRegionID = regionID; |
|
34 newClip->fOp = op; |
|
35 newClip->fDoAA = true; // not necessary but sanity preserving |
|
36 newClip->fMatrixID = matrixID; |
|
37 return false; |
|
38 } |
|
39 |
|
40 void SkMatrixClipStateMgr::writeDeltaMat(int currentMatID, int desiredMatID) { |
|
41 const SkMatrix& current = this->lookupMat(currentMatID); |
|
42 const SkMatrix& desired = this->lookupMat(desiredMatID); |
|
43 |
|
44 SkMatrix delta; |
|
45 bool result = current.invert(&delta); |
|
46 if (result) { |
|
47 delta.preConcat(desired); |
|
48 } |
|
49 fPicRecord->recordConcat(delta); |
|
50 } |
|
51 |
|
52 // Note: this only writes out the clips for the current save state. To get the |
|
53 // entire clip stack requires iterating of the entire matrix/clip stack. |
|
54 void SkMatrixClipStateMgr::MatrixClipState::ClipInfo::writeClip(int* curMatID, |
|
55 SkMatrixClipStateMgr* mgr) { |
|
56 for (int i = 0; i < fClips.count(); ++i) { |
|
57 ClipOp& curClip = fClips[i]; |
|
58 |
|
59 // TODO: use the matrix ID to skip writing the identity matrix |
|
60 // over and over, i.e.: |
|
61 // if (*curMatID != curClip.fMatrixID) { |
|
62 // mgr->writeDeltaMat... |
|
63 // *curMatID... |
|
64 // } |
|
65 // Right now this optimization would throw off the testing harness. |
|
66 // TODO: right now we're writing out the delta matrix from the prior |
|
67 // matrix state. This is a side-effect of writing out the entire |
|
68 // clip stack and should be resolved when that is fixed. |
|
69 mgr->writeDeltaMat(*curMatID, curClip.fMatrixID); |
|
70 *curMatID = curClip.fMatrixID; |
|
71 |
|
72 int offset = 0; |
|
73 |
|
74 switch (curClip.fClipType) { |
|
75 case kRect_ClipType: |
|
76 offset = mgr->getPicRecord()->recordClipRect(curClip.fGeom.fRRect.rect(), |
|
77 curClip.fOp, curClip.fDoAA); |
|
78 break; |
|
79 case kRRect_ClipType: |
|
80 offset = mgr->getPicRecord()->recordClipRRect(curClip.fGeom.fRRect, curClip.fOp, |
|
81 curClip.fDoAA); |
|
82 break; |
|
83 case kPath_ClipType: |
|
84 offset = mgr->getPicRecord()->recordClipPath(curClip.fGeom.fPathID, curClip.fOp, |
|
85 curClip.fDoAA); |
|
86 break; |
|
87 case kRegion_ClipType: { |
|
88 const SkRegion* region = mgr->lookupRegion(curClip.fGeom.fRegionID); |
|
89 offset = mgr->getPicRecord()->recordClipRegion(*region, curClip.fOp); |
|
90 break; |
|
91 } |
|
92 default: |
|
93 SkASSERT(0); |
|
94 } |
|
95 |
|
96 mgr->addClipOffset(offset); |
|
97 } |
|
98 } |
|
99 |
|
100 SkMatrixClipStateMgr::SkMatrixClipStateMgr() |
|
101 : fPicRecord(NULL) |
|
102 , fMatrixClipStack(sizeof(MatrixClipState), |
|
103 fMatrixClipStackStorage, |
|
104 sizeof(fMatrixClipStackStorage)) |
|
105 , fCurOpenStateID(kIdentityWideOpenStateID) { |
|
106 |
|
107 fSkipOffsets = SkNEW(SkTDArray<int>); |
|
108 |
|
109 // The first slot in the matrix dictionary is reserved for the identity matrix |
|
110 fMatrixDict.append()->reset(); |
|
111 |
|
112 fCurMCState = (MatrixClipState*)fMatrixClipStack.push_back(); |
|
113 new (fCurMCState) MatrixClipState(NULL, 0); // balanced in restore() |
|
114 |
|
115 #ifdef SK_DEBUG |
|
116 fActualDepth = 0; |
|
117 #endif |
|
118 } |
|
119 |
|
120 SkMatrixClipStateMgr::~SkMatrixClipStateMgr() { |
|
121 for (int i = 0; i < fRegionDict.count(); ++i) { |
|
122 SkDELETE(fRegionDict[i]); |
|
123 } |
|
124 |
|
125 SkDELETE(fSkipOffsets); |
|
126 } |
|
127 |
|
128 |
|
129 int SkMatrixClipStateMgr::MCStackPush(SkCanvas::SaveFlags flags) { |
|
130 MatrixClipState* newTop = (MatrixClipState*)fMatrixClipStack.push_back(); |
|
131 new (newTop) MatrixClipState(fCurMCState, flags); // balanced in restore() |
|
132 fCurMCState = newTop; |
|
133 |
|
134 SkDEBUGCODE(this->validate();) |
|
135 |
|
136 return fMatrixClipStack.count(); |
|
137 } |
|
138 |
|
139 int SkMatrixClipStateMgr::save(SkCanvas::SaveFlags flags) { |
|
140 SkDEBUGCODE(this->validate();) |
|
141 |
|
142 return this->MCStackPush(flags); |
|
143 } |
|
144 |
|
145 int SkMatrixClipStateMgr::saveLayer(const SkRect* bounds, const SkPaint* paint, |
|
146 SkCanvas::SaveFlags flags) { |
|
147 #ifdef SK_DEBUG |
|
148 if (fCurMCState->fIsSaveLayer) { |
|
149 SkASSERT(0 == fSkipOffsets->count()); |
|
150 } |
|
151 #endif |
|
152 |
|
153 // Since the saveLayer call draws something we need to potentially dump |
|
154 // out the MC state |
|
155 SkDEBUGCODE(bool saved =) this->call(kOther_CallType); |
|
156 |
|
157 int result = this->MCStackPush(flags); |
|
158 ++fCurMCState->fLayerID; |
|
159 fCurMCState->fIsSaveLayer = true; |
|
160 |
|
161 #ifdef SK_DEBUG |
|
162 if (saved) { |
|
163 fCurMCState->fExpectedDepth++; // 1 for nesting save |
|
164 } |
|
165 fCurMCState->fExpectedDepth++; // 1 for saveLayer |
|
166 #endif |
|
167 |
|
168 *fStateIDStack.append() = fCurOpenStateID; |
|
169 fCurMCState->fSavedSkipOffsets = fSkipOffsets; |
|
170 |
|
171 // TODO: recycle these rather then new & deleting them on every saveLayer/ |
|
172 // restore |
|
173 fSkipOffsets = SkNEW(SkTDArray<int>); |
|
174 |
|
175 fPicRecord->recordSaveLayer(bounds, paint, |
|
176 (SkCanvas::SaveFlags)(flags| SkCanvas::kMatrixClip_SaveFlag)); |
|
177 #ifdef SK_DEBUG |
|
178 fActualDepth++; |
|
179 #endif |
|
180 return result; |
|
181 } |
|
182 |
|
183 void SkMatrixClipStateMgr::restore() { |
|
184 SkDEBUGCODE(this->validate();) |
|
185 |
|
186 if (fCurMCState->fIsSaveLayer) { |
|
187 if (fCurMCState->fHasOpen) { |
|
188 fCurMCState->fHasOpen = false; |
|
189 fPicRecord->recordRestore(); // Close the open block inside the saveLayer |
|
190 #ifdef SK_DEBUG |
|
191 SkASSERT(fActualDepth > 0); |
|
192 fActualDepth--; |
|
193 #endif |
|
194 } else { |
|
195 SkASSERT(0 == fSkipOffsets->count()); |
|
196 } |
|
197 |
|
198 // The saveLayer's don't carry any matrix or clip state in the |
|
199 // new scheme so make sure the saveLayer's recordRestore doesn't |
|
200 // try to finalize them (i.e., fill in their skip offsets). |
|
201 fPicRecord->recordRestore(false); // close of saveLayer |
|
202 #ifdef SK_DEBUG |
|
203 SkASSERT(fActualDepth > 0); |
|
204 fActualDepth--; |
|
205 #endif |
|
206 |
|
207 SkASSERT(fStateIDStack.count() >= 1); |
|
208 fCurOpenStateID = fStateIDStack[fStateIDStack.count()-1]; |
|
209 fStateIDStack.pop(); |
|
210 |
|
211 SkASSERT(0 == fSkipOffsets->count()); |
|
212 SkASSERT(NULL != fCurMCState->fSavedSkipOffsets); |
|
213 |
|
214 SkDELETE(fSkipOffsets); |
|
215 fSkipOffsets = fCurMCState->fSavedSkipOffsets; |
|
216 } |
|
217 |
|
218 bool prevHadOpen = fCurMCState->fHasOpen; |
|
219 bool prevWasSaveLayer = fCurMCState->fIsSaveLayer; |
|
220 |
|
221 fCurMCState->~MatrixClipState(); // balanced in save() |
|
222 fMatrixClipStack.pop_back(); |
|
223 fCurMCState = (MatrixClipState*)fMatrixClipStack.back(); |
|
224 |
|
225 if (!prevWasSaveLayer) { |
|
226 fCurMCState->fHasOpen = prevHadOpen; |
|
227 } |
|
228 |
|
229 if (fCurMCState->fIsSaveLayer) { |
|
230 if (0 != fSkipOffsets->count()) { |
|
231 SkASSERT(fCurMCState->fHasOpen); |
|
232 } |
|
233 } |
|
234 |
|
235 SkDEBUGCODE(this->validate();) |
|
236 } |
|
237 |
|
238 // kIdentityWideOpenStateID (0) is reserved for the identity/wide-open clip state |
|
239 int32_t SkMatrixClipStateMgr::NewMCStateID() { |
|
240 // TODO: guard against wrap around |
|
241 // TODO: make uint32_t |
|
242 static int32_t gMCStateID = kIdentityWideOpenStateID; |
|
243 ++gMCStateID; |
|
244 return gMCStateID; |
|
245 } |
|
246 |
|
247 bool SkMatrixClipStateMgr::isNestingMCState(int stateID) { |
|
248 return fStateIDStack.count() > 0 && fStateIDStack[fStateIDStack.count()-1] == fCurOpenStateID; |
|
249 } |
|
250 |
|
251 bool SkMatrixClipStateMgr::call(CallType callType) { |
|
252 SkDEBUGCODE(this->validate();) |
|
253 |
|
254 if (kMatrix_CallType == callType || kClip_CallType == callType) { |
|
255 fCurMCState->fMCStateID = NewMCStateID(); |
|
256 SkDEBUGCODE(this->validate();) |
|
257 return false; |
|
258 } |
|
259 |
|
260 SkASSERT(kOther_CallType == callType); |
|
261 |
|
262 if (fCurMCState->fMCStateID == fCurOpenStateID) { |
|
263 // Required MC state is already active one - nothing to do |
|
264 SkDEBUGCODE(this->validate();) |
|
265 return false; |
|
266 } |
|
267 |
|
268 if (kIdentityWideOpenStateID != fCurOpenStateID && |
|
269 !this->isNestingMCState(fCurOpenStateID)) { |
|
270 // Don't write a restore if the open state is one in which a saveLayer |
|
271 // is nested. The save after the saveLayer's restore will close it. |
|
272 fPicRecord->recordRestore(); // Close the open block |
|
273 fCurMCState->fHasOpen = false; |
|
274 #ifdef SK_DEBUG |
|
275 SkASSERT(fActualDepth > 0); |
|
276 fActualDepth--; |
|
277 #endif |
|
278 } |
|
279 |
|
280 // Install the required MC state as the active one |
|
281 fCurOpenStateID = fCurMCState->fMCStateID; |
|
282 |
|
283 if (kIdentityWideOpenStateID == fCurOpenStateID) { |
|
284 SkASSERT(0 == fActualDepth); |
|
285 SkASSERT(!fCurMCState->fHasOpen); |
|
286 SkASSERT(0 == fSkipOffsets->count()); |
|
287 return false; |
|
288 } |
|
289 |
|
290 SkASSERT(!fCurMCState->fHasOpen); |
|
291 SkASSERT(0 == fSkipOffsets->count()); |
|
292 fCurMCState->fHasOpen = true; |
|
293 fPicRecord->recordSave(SkCanvas::kMatrixClip_SaveFlag); |
|
294 #ifdef SK_DEBUG |
|
295 fActualDepth++; |
|
296 SkASSERT(fActualDepth == fCurMCState->fExpectedDepth); |
|
297 #endif |
|
298 |
|
299 // write out clips |
|
300 SkDeque::Iter iter(fMatrixClipStack, SkDeque::Iter::kBack_IterStart); |
|
301 const MatrixClipState* state; |
|
302 // Loop back across the MC states until the last saveLayer. The MC |
|
303 // state in front of the saveLayer has already been written out. |
|
304 for (state = (const MatrixClipState*) iter.prev(); |
|
305 state != NULL; |
|
306 state = (const MatrixClipState*) iter.prev()) { |
|
307 if (state->fIsSaveLayer) { |
|
308 break; |
|
309 } |
|
310 } |
|
311 |
|
312 int curMatID; |
|
313 |
|
314 if (NULL == state) { |
|
315 // There was no saveLayer in the MC stack so we need to output them all |
|
316 iter.reset(fMatrixClipStack, SkDeque::Iter::kFront_IterStart); |
|
317 state = (const MatrixClipState*) iter.next(); |
|
318 curMatID = kIdentityMatID; |
|
319 } else { |
|
320 // SkDeque's iterators actually return the previous location so we |
|
321 // need to reverse and go forward one to get back on track. |
|
322 iter.next(); |
|
323 SkDEBUGCODE(const MatrixClipState* test = (const MatrixClipState*)) iter.next(); |
|
324 SkASSERT(test == state); |
|
325 |
|
326 curMatID = state->fMatrixInfo->getID(this); |
|
327 |
|
328 // TODO: this assumes that, in the case of Save|SaveLayer when the SaveLayer |
|
329 // doesn't save the clip, that the SaveLayer doesn't add any additional clip state. |
|
330 // This assumption will be removed when we explicitly store the clip state in |
|
331 // self-contained objects. It is valid for the small set of skps. |
|
332 if (NULL != state->fPrev && state->fClipInfo == state->fPrev->fClipInfo) { |
|
333 // By the above assumption the SaveLayer's MC state has already been |
|
334 // written out by the prior Save so don't output it again. |
|
335 state = (const MatrixClipState*) iter.next(); |
|
336 } |
|
337 } |
|
338 |
|
339 for ( ; state != NULL; state = (const MatrixClipState*) iter.next()) { |
|
340 state->fClipInfo->writeClip(&curMatID, this); |
|
341 } |
|
342 |
|
343 // write out matrix |
|
344 // TODO: this test isn't quite right. It should be: |
|
345 // if (curMatID != fCurMCState->fMatrixInfo->getID(this)) { |
|
346 // but right now the testing harness always expects a matrix if |
|
347 // the matrices are non-I |
|
348 if (kIdentityMatID != fCurMCState->fMatrixInfo->getID(this)) { |
|
349 // TODO: writing out the delta matrix here is an artifact of the writing |
|
350 // out of the entire clip stack (with its matrices). Ultimately we will |
|
351 // write out the CTM here when the clip state is collapsed to a single path. |
|
352 this->writeDeltaMat(curMatID, fCurMCState->fMatrixInfo->getID(this)); |
|
353 } |
|
354 |
|
355 SkDEBUGCODE(this->validate();) |
|
356 return true; |
|
357 } |
|
358 |
|
359 // Fill in the skip offsets for all the clips written in the current block |
|
360 void SkMatrixClipStateMgr::fillInSkips(SkWriter32* writer, int32_t restoreOffset) { |
|
361 for (int i = 0; i < fSkipOffsets->count(); ++i) { |
|
362 SkDEBUGCODE(int32_t peek = writer->readTAt<int32_t>((*fSkipOffsets)[i]);) |
|
363 SkASSERT(-1 == peek); |
|
364 writer->overwriteTAt<int32_t>((*fSkipOffsets)[i], restoreOffset); |
|
365 } |
|
366 |
|
367 fSkipOffsets->rewind(); |
|
368 SkASSERT(0 == fSkipOffsets->count()); |
|
369 } |
|
370 |
|
371 void SkMatrixClipStateMgr::finish() { |
|
372 if (kIdentityWideOpenStateID != fCurOpenStateID) { |
|
373 fPicRecord->recordRestore(); // Close the open block |
|
374 fCurMCState->fHasOpen = false; |
|
375 #ifdef SK_DEBUG |
|
376 SkASSERT(fActualDepth > 0); |
|
377 fActualDepth--; |
|
378 #endif |
|
379 fCurOpenStateID = kIdentityWideOpenStateID; |
|
380 SkASSERT(!fCurMCState->fHasOpen); |
|
381 } |
|
382 } |
|
383 |
|
384 #ifdef SK_DEBUG |
|
385 void SkMatrixClipStateMgr::validate() { |
|
386 if (fCurOpenStateID == fCurMCState->fMCStateID && !this->isNestingMCState(fCurOpenStateID)) { |
|
387 // The current state is the active one so it should have a skip |
|
388 // offset for each clip |
|
389 SkDeque::Iter iter(fMatrixClipStack, SkDeque::Iter::kBack_IterStart); |
|
390 int clipCount = 0; |
|
391 for (const MatrixClipState* state = (const MatrixClipState*) iter.prev(); |
|
392 state != NULL; |
|
393 state = (const MatrixClipState*) iter.prev()) { |
|
394 if (NULL == state->fPrev || state->fPrev->fClipInfo != state->fClipInfo) { |
|
395 clipCount += state->fClipInfo->numClips(); |
|
396 } |
|
397 if (state->fIsSaveLayer) { |
|
398 break; |
|
399 } |
|
400 } |
|
401 |
|
402 SkASSERT(fSkipOffsets->count() == clipCount); |
|
403 } |
|
404 } |
|
405 #endif |
|
406 |
|
407 int SkMatrixClipStateMgr::addRegionToDict(const SkRegion& region) { |
|
408 int index = fRegionDict.count(); |
|
409 *fRegionDict.append() = SkNEW(SkRegion(region)); |
|
410 return index; |
|
411 } |
|
412 |
|
413 int SkMatrixClipStateMgr::addMatToDict(const SkMatrix& mat) { |
|
414 if (mat.isIdentity()) { |
|
415 return kIdentityMatID; |
|
416 } |
|
417 |
|
418 *fMatrixDict.append() = mat; |
|
419 return fMatrixDict.count()-1; |
|
420 } |