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 const SkMatrix& m
atrix) { |
| 16 int pathID = picRecord->addPathToHeap(path); |
| 17 |
| 18 ClipOp& newClip = fClips.push_back(); |
| 19 newClip.fClipType = kPath_ClipType; |
| 20 newClip.fGeom.fPathID = pathID; |
| 21 newClip.fOp = op; |
| 22 newClip.fDoAA = doAA; |
| 23 newClip.fMatrix = matrix; |
| 24 newClip.fOffset = kInvalidJumpOffset; |
| 25 return false; |
| 26 } |
| 27 |
| 28 bool SkMatrixClipStateMgr::MatrixClipState::ClipInfo::clipRegion(SkPictureRecord
* picRecord, |
| 29 const SkRegion&
region, |
| 30 SkRegion::Op op
, |
| 31 const SkMatrix&
matrix) { |
| 32 // TODO: add a region dictionary so we don't have to copy the region in here
|
| 33 ClipOp& newClip = fClips.push_back(); |
| 34 newClip.fClipType = kRegion_ClipType; |
| 35 newClip.fGeom.fRegion = SkNEW(SkRegion(region)); |
| 36 newClip.fOp = op; |
| 37 newClip.fDoAA = true; // not necessary but sanity preserving |
| 38 newClip.fMatrix = matrix; |
| 39 newClip.fOffset = kInvalidJumpOffset; |
| 40 return false; |
| 41 } |
| 42 |
| 43 void SkMatrixClipStateMgr::WriteDeltaMat(SkPictureRecord* picRecord, |
| 44 const SkMatrix& current, |
| 45 const SkMatrix& desired) { |
| 46 SkMatrix delta; |
| 47 current.invert(&delta); |
| 48 delta.preConcat(desired); |
| 49 picRecord->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(SkMatrix* curMat
, |
| 55 SkPictureRecord*
picRecord, |
| 56 bool* overrideFi
rstOp) { |
| 57 for (int i = 0; i < fClips.count(); ++i) { |
| 58 ClipOp& curClip = fClips[i]; |
| 59 |
| 60 SkRegion::Op op = curClip.fOp; |
| 61 if (*overrideFirstOp) { |
| 62 op = SkRegion::kReplace_Op; |
| 63 *overrideFirstOp = false; |
| 64 } |
| 65 |
| 66 // TODO: re-add an internal matrix dictionary to not write out |
| 67 // redundant matrices. |
| 68 // TODO: right now we're writing out the delta matrix from the prior |
| 69 // matrix state. This is a side-effect of writing out the entire |
| 70 // clip stack and should be resolved when that is fixed. |
| 71 SkMatrixClipStateMgr::WriteDeltaMat(picRecord, *curMat, curClip.fMatrix)
; |
| 72 *curMat = curClip.fMatrix; |
| 73 |
| 74 switch (curClip.fClipType) { |
| 75 case kRect_ClipType: |
| 76 curClip.fOffset = picRecord->recordClipRect(curClip.fGeom.fRRect.rec
t(), |
| 77 op, curClip.fDoAA); |
| 78 break; |
| 79 case kRRect_ClipType: |
| 80 curClip.fOffset = picRecord->recordClipRRect(curClip.fGeom.fRRect, o
p, |
| 81 curClip.fDoAA); |
| 82 break; |
| 83 case kPath_ClipType: |
| 84 curClip.fOffset = picRecord->recordClipPath(curClip.fGeom.fPathID, o
p, |
| 85 curClip.fDoAA); |
| 86 break; |
| 87 case kRegion_ClipType: |
| 88 curClip.fOffset = picRecord->recordClipRegion(*curClip.fGeom.fRegion
, op); |
| 89 break; |
| 90 default: |
| 91 SkASSERT(0); |
| 92 } |
| 93 } |
| 94 } |
| 95 |
| 96 // Fill in the skip offsets for all the clips written in the current block |
| 97 void SkMatrixClipStateMgr::MatrixClipState::ClipInfo::fillInSkips(SkWriter32* wr
iter, |
| 98 int32_t restor
eOffset) { |
| 99 for (int i = 0; i < fClips.count(); ++i) { |
| 100 ClipOp& curClip = fClips[i]; |
| 101 |
| 102 if (-1 == curClip.fOffset) { |
| 103 continue; |
| 104 } |
| 105 SkDEBUGCODE(uint32_t peek = writer->read32At(curClip.fOffset);) |
| 106 SkASSERT(-1 == peek); |
| 107 writer->write32At(curClip.fOffset, restoreOffset); |
| 108 SkDEBUGCODE(curClip.fOffset = -1;) |
| 109 } |
| 110 } |
| 111 |
| 112 SkMatrixClipStateMgr::SkMatrixClipStateMgr() |
| 113 : fPicRecord(NULL) |
| 114 , fCurOpenStateID(kIdentityWideOpenStateID) |
| 115 , fMatrixClipStack(sizeof(MatrixClipState), |
| 116 fMatrixClipStackStorage, |
| 117 sizeof(fMatrixClipStackStorage)) { |
| 118 fCurMCState = (MatrixClipState*)fMatrixClipStack.push_back(); |
| 119 new (fCurMCState) MatrixClipState(NULL, 0); // balanced in restore() |
| 120 } |
| 121 |
| 122 |
| 123 int SkMatrixClipStateMgr::save(SkCanvas::SaveFlags flags) { |
| 124 SkDEBUGCODE(this->validate();) |
| 125 |
| 126 MatrixClipState* newTop = (MatrixClipState*)fMatrixClipStack.push_back(); |
| 127 new (newTop) MatrixClipState(fCurMCState, flags); // balanced in restore() |
| 128 fCurMCState = newTop; |
| 129 |
| 130 SkDEBUGCODE(this->validate();) |
| 131 |
| 132 return fMatrixClipStack.count(); |
| 133 } |
| 134 |
| 135 int SkMatrixClipStateMgr::saveLayer(const SkRect* bounds, const SkPaint* paint, |
| 136 SkCanvas::SaveFlags flags) { |
| 137 int result = this->save(flags); |
| 138 ++fCurMCState->fLayerID; |
| 139 fCurMCState->fIsSaveLayer = true; |
| 140 |
| 141 fCurMCState->fSaveLayerBracketed = this->call(kOther_CallType); |
| 142 fCurMCState->fSaveLayerBaseStateID = fCurOpenStateID; |
| 143 fPicRecord->recordSaveLayer(bounds, paint, |
| 144 (SkCanvas::SaveFlags)(flags| SkCanvas::kMatrixCl
ip_SaveFlag)); |
| 145 return result; |
| 146 } |
| 147 |
| 148 void SkMatrixClipStateMgr::restore() { |
| 149 SkDEBUGCODE(this->validate();) |
| 150 |
| 151 if (fCurMCState->fIsSaveLayer) { |
| 152 if (fCurMCState->fSaveLayerBaseStateID != fCurOpenStateID) { |
| 153 fPicRecord->recordRestore(); // Close the open block |
| 154 } |
| 155 // The saveLayer's don't carry any matrix or clip state in the |
| 156 // new scheme so make sure the saveLayer's recordRestore doesn't |
| 157 // try to finalize them (i.e., fill in their skip offsets). |
| 158 fPicRecord->recordRestore(false); // close of saveLayer |
| 159 |
| 160 // Close the Save that brackets the saveLayer. TODO: this doesn't handle |
| 161 // the skip offsets correctly |
| 162 if (fCurMCState->fSaveLayerBracketed) { |
| 163 fPicRecord->recordRestore(false); |
| 164 } |
| 165 |
| 166 // MC states can be allowed to fuse across saveLayer/restore boundaries |
| 167 fCurOpenStateID = kIdentityWideOpenStateID; |
| 168 } |
| 169 |
| 170 fCurMCState->~MatrixClipState(); // balanced in save() |
| 171 fMatrixClipStack.pop_back(); |
| 172 fCurMCState = (MatrixClipState*)fMatrixClipStack.back(); |
| 173 |
| 174 SkDEBUGCODE(this->validate();) |
| 175 } |
| 176 |
| 177 // kIdentityWideOpenStateID (0) is reserved for the identity/wide-open clip stat
e |
| 178 int32_t SkMatrixClipStateMgr::NewMCStateID() { |
| 179 // TODO: guard against wrap around |
| 180 // TODO: make uint32_t |
| 181 static int32_t gMCStateID = kIdentityWideOpenStateID; |
| 182 ++gMCStateID; |
| 183 return gMCStateID; |
| 184 } |
| 185 |
| 186 bool SkMatrixClipStateMgr::call(CallType callType) { |
| 187 SkDEBUGCODE(this->validate();) |
| 188 |
| 189 if (kMatrix_CallType == callType || kClip_CallType == callType) { |
| 190 fCurMCState->fMCStateID = NewMCStateID(); |
| 191 SkDEBUGCODE(this->validate();) |
| 192 return false; |
| 193 } |
| 194 |
| 195 SkASSERT(kOther_CallType == callType); |
| 196 |
| 197 if (fCurMCState->fMCStateID == fCurOpenStateID) { |
| 198 // Required MC state is already active one - nothing to do |
| 199 SkDEBUGCODE(this->validate();) |
| 200 return false; |
| 201 } |
| 202 |
| 203 if (kIdentityWideOpenStateID != fCurOpenStateID) { |
| 204 fPicRecord->recordRestore(); // Close the open block |
| 205 } |
| 206 |
| 207 // Install the required MC state as the active one |
| 208 fCurOpenStateID = fCurMCState->fMCStateID; |
| 209 |
| 210 fPicRecord->recordSave(SkCanvas::kMatrixClip_SaveFlag); |
| 211 |
| 212 // write out clips |
| 213 SkDeque::F2BIter iter(fMatrixClipStack); |
| 214 bool firstClip = true; |
| 215 |
| 216 SkMatrix curMat = SkMatrix::I(); |
| 217 for (const MatrixClipState* state = (const MatrixClipState*) iter.next(); |
| 218 state != NULL; |
| 219 state = (const MatrixClipState*) iter.next()) { |
| 220 state->fClipInfo->writeClip(&curMat, fPicRecord, &firstClip); |
| 221 } |
| 222 |
| 223 // write out matrix |
| 224 if (!fCurMCState->fMatrixInfo->fMatrix.isIdentity()) { |
| 225 // TODO: writing out the delta matrix here is an artifact of the writing
|
| 226 // out of the entire clip stack (with its matrices). Ultimately we will |
| 227 // write out the CTM here when the clip state is collapsed to a single p
ath. |
| 228 WriteDeltaMat(fPicRecord, curMat, fCurMCState->fMatrixInfo->fMatrix); |
| 229 } |
| 230 |
| 231 SkDEBUGCODE(this->validate();) |
| 232 |
| 233 return true; |
| 234 } |
| 235 |
| 236 void SkMatrixClipStateMgr::finish() { |
| 237 if (kIdentityWideOpenStateID != fCurOpenStateID) { |
| 238 fPicRecord->recordRestore(); // Close the open block |
| 239 fCurOpenStateID = kIdentityWideOpenStateID; |
| 240 } |
| 241 } |
| 242 |
| 243 #ifdef SK_DEBUG |
| 244 void SkMatrixClipStateMgr::validate() { |
| 245 if (fCurOpenStateID == fCurMCState->fMCStateID) { |
| 246 // The current state is the active one so all its skip offsets should |
| 247 // still be -1 |
| 248 SkDeque::F2BIter iter(fMatrixClipStack); |
| 249 |
| 250 for (const MatrixClipState* state = (const MatrixClipState*) iter.next()
; |
| 251 state != NULL; |
| 252 state = (const MatrixClipState*) iter.next()) { |
| 253 state->fClipInfo->checkOffsetNotEqual(-1); |
| 254 } |
| 255 } |
| 256 } |
| 257 #endif |
OLD | NEW |