• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
clipPath(SkPictureRecord * picRecord,const SkPath & path,SkRegion::Op op,bool doAA,int matrixID)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 
clipRegion(SkPictureRecord * picRecord,int regionID,SkRegion::Op op,int matrixID)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 
writeDeltaMat(int currentMatID,int desiredMatID)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.
writeClip(int * curMatID,SkMatrixClipStateMgr * mgr)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         size_t 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 
SkMatrixClipStateMgr()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 
~SkMatrixClipStateMgr()120 SkMatrixClipStateMgr::~SkMatrixClipStateMgr() {
121     for (int i = 0; i < fRegionDict.count(); ++i) {
122         SkDELETE(fRegionDict[i]);
123     }
124 
125     SkDELETE(fSkipOffsets);
126 }
127 
128 
MCStackPush(SkCanvas::SaveFlags flags)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 
save(SkCanvas::SaveFlags flags)139 int SkMatrixClipStateMgr::save(SkCanvas::SaveFlags flags) {
140     SkDEBUGCODE(this->validate();)
141 
142     return this->MCStackPush(flags);
143 }
144 
saveLayer(const SkRect * bounds,const SkPaint * paint,SkCanvas::SaveFlags flags)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, flags | SkCanvas::kMatrixClip_SaveFlag);
176 #ifdef SK_DEBUG
177     fActualDepth++;
178 #endif
179     return result;
180 }
181 
restore()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 saveLayer
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 state
NewMCStateID()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 
isNestingMCState(int stateID)246 bool SkMatrixClipStateMgr::isNestingMCState(int stateID) {
247     return fStateIDStack.count() > 0 && fStateIDStack[fStateIDStack.count()-1] == fCurOpenStateID;
248 }
249 
call(CallType callType)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(SkCanvas::kMatrixClip_SaveFlag);
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 SaveLayer
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 state 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 path.
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
fillInSkips(SkWriter32 * writer,int32_t restoreOffset)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 
finish()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
validate()384 void SkMatrixClipStateMgr::validate() {
385     if (fCurOpenStateID == fCurMCState->fMCStateID && !this->isNestingMCState(fCurOpenStateID)) {
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->fClipInfo) {
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 
addRegionToDict(const SkRegion & region)406 int SkMatrixClipStateMgr::addRegionToDict(const SkRegion& region) {
407     int index = fRegionDict.count();
408     *fRegionDict.append() = SkNEW(SkRegion(region));
409     return index;
410 }
411 
addMatToDict(const SkMatrix & mat)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 }
420