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
11bool 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
27bool 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
40void 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.
54void 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
100SkMatrixClipStateMgr::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
120SkMatrixClipStateMgr::~SkMatrixClipStateMgr() {
121    for (int i = 0; i < fRegionDict.count(); ++i) {
122        SkDELETE(fRegionDict[i]);
123    }
124
125    SkDELETE(fSkipOffsets);
126}
127
128
129int 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
139int SkMatrixClipStateMgr::save(SkCanvas::SaveFlags flags) {
140    SkDEBUGCODE(this->validate();)
141
142    return this->MCStackPush(flags);
143}
144
145int 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
182void 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
238int32_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
246bool SkMatrixClipStateMgr::isNestingMCState(int stateID) {
247    return fStateIDStack.count() > 0 && fStateIDStack[fStateIDStack.count()-1] == fCurOpenStateID;
248}
249
250bool 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
359void 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
370void 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
384void 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
406int SkMatrixClipStateMgr::addRegionToDict(const SkRegion& region) {
407    int index = fRegionDict.count();
408    *fRegionDict.append() = SkNEW(SkRegion(region));
409    return index;
410}
411
412int 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