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 | |
8 #include "SkMatrixClipStateMgr.h" | |
9 #include "SkPictureRecord.h" | |
10 | |
11 bool SkMatrixClipStateMgr::MatrixClipState::ClipInfo::clipPath(SkPictureRecord*
picRecord, | |
12 const SkPath& pat
h, | |
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 SkMatrixClipStat
eMgr* 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 size_t offset = 0; | |
73 | |
74 switch (curClip.fClipType) { | |
75 case kRect_ClipType: | |
76 offset = mgr->getPicRecord()->recordClipRect(curClip.fGeom.fRRect.re
ct(), | |
77 curClip.fOp, curClip.fD
oAA); | |
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 matr
ix | |
110 fMatrixDict.append()->reset(); | |
111 | |
112 fCurMCState = (MatrixClipState*)fMatrixClipStack.push_back(); | |
113 new (fCurMCState) MatrixClipState(NULL); // 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() { | |
130 MatrixClipState* newTop = (MatrixClipState*)fMatrixClipStack.push_back(); | |
131 new (newTop) MatrixClipState(fCurMCState); // balanced in restore() | |
132 fCurMCState = newTop; | |
133 | |
134 SkDEBUGCODE(this->validate();) | |
135 | |
136 return fMatrixClipStack.count(); | |
137 } | |
138 | |
139 int SkMatrixClipStateMgr::save() { | |
140 SkDEBUGCODE(this->validate();) | |
141 | |
142 return this->MCStackPush(); | |
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(); | |
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, flags); | |
176 #ifdef SK_DEBUG | |
177 fActualDepth++; | |
178 #endif | |
179 return result; | |
180 } | |
181 | |
182 void SkMatrixClipStateMgr::restore() { | |
183 SkDEBUGCODE(this->validate();) | |
184 | |
185 if (fCurMCState->fIsSaveLayer) { | |
186 if (fCurMCState->fHasOpen) { | |
187 fCurMCState->fHasOpen = false; | |
188 fPicRecord->recordRestore(); // Close the open block inside the save
Layer | |
189 #ifdef SK_DEBUG | |
190 SkASSERT(fActualDepth > 0); | |
191 fActualDepth--; | |
192 #endif | |
193 } else { | |
194 SkASSERT(0 == fSkipOffsets->count()); | |
195 } | |
196 | |
197 // The saveLayer's don't carry any matrix or clip state in the | |
198 // new scheme so make sure the saveLayer's recordRestore doesn't | |
199 // try to finalize them (i.e., fill in their skip offsets). | |
200 fPicRecord->recordRestore(false); // close of saveLayer | |
201 #ifdef SK_DEBUG | |
202 SkASSERT(fActualDepth > 0); | |
203 fActualDepth--; | |
204 #endif | |
205 | |
206 SkASSERT(fStateIDStack.count() >= 1); | |
207 fCurOpenStateID = fStateIDStack[fStateIDStack.count()-1]; | |
208 fStateIDStack.pop(); | |
209 | |
210 SkASSERT(0 == fSkipOffsets->count()); | |
211 SkASSERT(NULL != fCurMCState->fSavedSkipOffsets); | |
212 | |
213 SkDELETE(fSkipOffsets); | |
214 fSkipOffsets = fCurMCState->fSavedSkipOffsets; | |
215 } | |
216 | |
217 bool prevHadOpen = fCurMCState->fHasOpen; | |
218 bool prevWasSaveLayer = fCurMCState->fIsSaveLayer; | |
219 | |
220 fCurMCState->~MatrixClipState(); // balanced in save() | |
221 fMatrixClipStack.pop_back(); | |
222 fCurMCState = (MatrixClipState*)fMatrixClipStack.back(); | |
223 | |
224 if (!prevWasSaveLayer) { | |
225 fCurMCState->fHasOpen = prevHadOpen; | |
226 } | |
227 | |
228 if (fCurMCState->fIsSaveLayer) { | |
229 if (0 != fSkipOffsets->count()) { | |
230 SkASSERT(fCurMCState->fHasOpen); | |
231 } | |
232 } | |
233 | |
234 SkDEBUGCODE(this->validate();) | |
235 } | |
236 | |
237 // kIdentityWideOpenStateID (0) is reserved for the identity/wide-open clip stat
e | |
238 int32_t SkMatrixClipStateMgr::NewMCStateID() { | |
239 // TODO: guard against wrap around | |
240 // TODO: make uint32_t | |
241 static int32_t gMCStateID = kIdentityWideOpenStateID; | |
242 ++gMCStateID; | |
243 return gMCStateID; | |
244 } | |
245 | |
246 bool SkMatrixClipStateMgr::isNestingMCState(int stateID) { | |
247 return fStateIDStack.count() > 0 && fStateIDStack[fStateIDStack.count()-1] =
= fCurOpenStateID; | |
248 } | |
249 | |
250 bool SkMatrixClipStateMgr::call(CallType callType) { | |
251 SkDEBUGCODE(this->validate();) | |
252 | |
253 if (kMatrix_CallType == callType || kClip_CallType == callType) { | |
254 fCurMCState->fMCStateID = NewMCStateID(); | |
255 SkDEBUGCODE(this->validate();) | |
256 return false; | |
257 } | |
258 | |
259 SkASSERT(kOther_CallType == callType); | |
260 | |
261 if (fCurMCState->fMCStateID == fCurOpenStateID) { | |
262 // Required MC state is already active one - nothing to do | |
263 SkDEBUGCODE(this->validate();) | |
264 return false; | |
265 } | |
266 | |
267 if (kIdentityWideOpenStateID != fCurOpenStateID && | |
268 !this->isNestingMCState(fCurOpenStateID)) { | |
269 // Don't write a restore if the open state is one in which a saveLayer | |
270 // is nested. The save after the saveLayer's restore will close it. | |
271 fPicRecord->recordRestore(); // Close the open block | |
272 fCurMCState->fHasOpen = false; | |
273 #ifdef SK_DEBUG | |
274 SkASSERT(fActualDepth > 0); | |
275 fActualDepth--; | |
276 #endif | |
277 } | |
278 | |
279 // Install the required MC state as the active one | |
280 fCurOpenStateID = fCurMCState->fMCStateID; | |
281 | |
282 if (kIdentityWideOpenStateID == fCurOpenStateID) { | |
283 SkASSERT(0 == fActualDepth); | |
284 SkASSERT(!fCurMCState->fHasOpen); | |
285 SkASSERT(0 == fSkipOffsets->count()); | |
286 return false; | |
287 } | |
288 | |
289 SkASSERT(!fCurMCState->fHasOpen); | |
290 SkASSERT(0 == fSkipOffsets->count()); | |
291 fCurMCState->fHasOpen = true; | |
292 fPicRecord->recordSave(); | |
293 #ifdef SK_DEBUG | |
294 fActualDepth++; | |
295 SkASSERT(fActualDepth == fCurMCState->fExpectedDepth); | |
296 #endif | |
297 | |
298 // write out clips | |
299 SkDeque::Iter iter(fMatrixClipStack, SkDeque::Iter::kBack_IterStart); | |
300 const MatrixClipState* state; | |
301 // Loop back across the MC states until the last saveLayer. The MC | |
302 // state in front of the saveLayer has already been written out. | |
303 for (state = (const MatrixClipState*) iter.prev(); | |
304 state != NULL; | |
305 state = (const MatrixClipState*) iter.prev()) { | |
306 if (state->fIsSaveLayer) { | |
307 break; | |
308 } | |
309 } | |
310 | |
311 int curMatID; | |
312 | |
313 if (NULL == state) { | |
314 // There was no saveLayer in the MC stack so we need to output them all | |
315 iter.reset(fMatrixClipStack, SkDeque::Iter::kFront_IterStart); | |
316 state = (const MatrixClipState*) iter.next(); | |
317 curMatID = kIdentityMatID; | |
318 } else { | |
319 // SkDeque's iterators actually return the previous location so we | |
320 // need to reverse and go forward one to get back on track. | |
321 iter.next(); | |
322 SkDEBUGCODE(const MatrixClipState* test = (const MatrixClipState*)) iter
.next(); | |
323 SkASSERT(test == state); | |
324 | |
325 curMatID = state->fMatrixInfo->getID(this); | |
326 | |
327 // TODO: this assumes that, in the case of Save|SaveLayer when the SaveL
ayer | |
328 // doesn't save the clip, that the SaveLayer doesn't add any additional
clip state. | |
329 // This assumption will be removed when we explicitly store the clip sta
te in | |
330 // self-contained objects. It is valid for the small set of skps. | |
331 if (NULL != state->fPrev && state->fClipInfo == state->fPrev->fClipInfo)
{ | |
332 // By the above assumption the SaveLayer's MC state has already been | |
333 // written out by the prior Save so don't output it again. | |
334 state = (const MatrixClipState*) iter.next(); | |
335 } | |
336 } | |
337 | |
338 for ( ; state != NULL; state = (const MatrixClipState*) iter.next()) { | |
339 state->fClipInfo->writeClip(&curMatID, this); | |
340 } | |
341 | |
342 // write out matrix | |
343 // TODO: this test isn't quite right. It should be: | |
344 // if (curMatID != fCurMCState->fMatrixInfo->getID(this)) { | |
345 // but right now the testing harness always expects a matrix if | |
346 // the matrices are non-I | |
347 if (kIdentityMatID != fCurMCState->fMatrixInfo->getID(this)) { | |
348 // TODO: writing out the delta matrix here is an artifact of the writing | |
349 // out of the entire clip stack (with its matrices). Ultimately we will | |
350 // write out the CTM here when the clip state is collapsed to a single p
ath. | |
351 this->writeDeltaMat(curMatID, fCurMCState->fMatrixInfo->getID(this)); | |
352 } | |
353 | |
354 SkDEBUGCODE(this->validate();) | |
355 return true; | |
356 } | |
357 | |
358 // Fill in the skip offsets for all the clips written in the current block | |
359 void SkMatrixClipStateMgr::fillInSkips(SkWriter32* writer, int32_t restoreOffset
) { | |
360 for (int i = 0; i < fSkipOffsets->count(); ++i) { | |
361 SkDEBUGCODE(int32_t peek = writer->readTAt<int32_t>((*fSkipOffsets)[i]);
) | |
362 SkASSERT(-1 == peek); | |
363 writer->overwriteTAt<int32_t>((*fSkipOffsets)[i], restoreOffset); | |
364 } | |
365 | |
366 fSkipOffsets->rewind(); | |
367 SkASSERT(0 == fSkipOffsets->count()); | |
368 } | |
369 | |
370 void SkMatrixClipStateMgr::finish() { | |
371 if (kIdentityWideOpenStateID != fCurOpenStateID) { | |
372 fPicRecord->recordRestore(); // Close the open block | |
373 fCurMCState->fHasOpen = false; | |
374 #ifdef SK_DEBUG | |
375 SkASSERT(fActualDepth > 0); | |
376 fActualDepth--; | |
377 #endif | |
378 fCurOpenStateID = kIdentityWideOpenStateID; | |
379 SkASSERT(!fCurMCState->fHasOpen); | |
380 } | |
381 } | |
382 | |
383 #ifdef SK_DEBUG | |
384 void SkMatrixClipStateMgr::validate() { | |
385 if (fCurOpenStateID == fCurMCState->fMCStateID && !this->isNestingMCState(fC
urOpenStateID)) { | |
386 // The current state is the active one so it should have a skip | |
387 // offset for each clip | |
388 SkDeque::Iter iter(fMatrixClipStack, SkDeque::Iter::kBack_IterStart); | |
389 int clipCount = 0; | |
390 for (const MatrixClipState* state = (const MatrixClipState*) iter.prev()
; | |
391 state != NULL; | |
392 state = (const MatrixClipState*) iter.prev()) { | |
393 if (NULL == state->fPrev || state->fPrev->fClipInfo != state->fClipI
nfo) { | |
394 clipCount += state->fClipInfo->numClips(); | |
395 } | |
396 if (state->fIsSaveLayer) { | |
397 break; | |
398 } | |
399 } | |
400 | |
401 SkASSERT(fSkipOffsets->count() == clipCount); | |
402 } | |
403 } | |
404 #endif | |
405 | |
406 int SkMatrixClipStateMgr::addRegionToDict(const SkRegion& region) { | |
407 int index = fRegionDict.count(); | |
408 *fRegionDict.append() = SkNEW(SkRegion(region)); | |
409 return index; | |
410 } | |
411 | |
412 int SkMatrixClipStateMgr::addMatToDict(const SkMatrix& mat) { | |
413 if (mat.isIdentity()) { | |
414 return kIdentityMatID; | |
415 } | |
416 | |
417 *fMatrixDict.append() = mat; | |
418 return fMatrixDict.count()-1; | |
419 } | |
OLD | NEW |