Home | History | Annotate | Download | only in core
      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         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 
    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, flags | SkCanvas::kMatrixClip_SaveFlag);
    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 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
    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(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
    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(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 
    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 }
    420