michael@0: /* michael@0: * Copyright 2014 Google Inc. michael@0: * michael@0: * Use of this source code is governed by a BSD-style license that can be michael@0: * found in the LICENSE file. michael@0: */ michael@0: #ifndef SkMatrixClipStateMgr_DEFINED michael@0: #define SkMatrixClipStateMgr_DEFINED michael@0: michael@0: #include "SkCanvas.h" michael@0: #include "SkMatrix.h" michael@0: #include "SkRegion.h" michael@0: #include "SkRRect.h" michael@0: #include "SkTypes.h" michael@0: #include "SkTDArray.h" michael@0: michael@0: class SkPictureRecord; michael@0: class SkWriter32; michael@0: michael@0: // The SkMatrixClipStateMgr collapses the matrix/clip state of an SkPicture into michael@0: // a series of save/restore blocks of consistent matrix clip state, e.g.: michael@0: // michael@0: // save michael@0: // clip(s) michael@0: // concat michael@0: // ... draw ops ... michael@0: // restore michael@0: // michael@0: // SaveLayers simply add another level, e.g.: michael@0: // michael@0: // save michael@0: // clip(s) michael@0: // concat michael@0: // ... draw ops ... michael@0: // saveLayer michael@0: // save michael@0: // clip(s) michael@0: // concat michael@0: // ... draw ops ... michael@0: // restore michael@0: // restore michael@0: // restore michael@0: // michael@0: // As a side effect of this process all saves and saveLayers will become michael@0: // kMatrixClip_SaveFlag style saves/saveLayers. michael@0: michael@0: // The SkMatrixClipStateMgr works by intercepting all the save*, restore, clip*, michael@0: // and matrix calls sent to SkCanvas in order to track the current matrix/clip michael@0: // state. All the other canvas calls get funnelled into a generic "call" entry michael@0: // point that signals that a state block is required. michael@0: class SkMatrixClipStateMgr { michael@0: public: michael@0: static const int32_t kIdentityWideOpenStateID = 0; michael@0: static const int kIdentityMatID = 0; michael@0: michael@0: class MatrixClipState : public SkNoncopyable { michael@0: public: michael@0: class MatrixInfo { michael@0: public: michael@0: void reset() { michael@0: fMatrixID = kIdentityMatID; michael@0: fMatrix.reset(); michael@0: } michael@0: michael@0: bool preTranslate(SkScalar dx, SkScalar dy) { michael@0: fMatrixID = -1; michael@0: return fMatrix.preTranslate(dx, dy); michael@0: } michael@0: michael@0: bool preScale(SkScalar sx, SkScalar sy) { michael@0: fMatrixID = -1; michael@0: return fMatrix.preScale(sx, sy); michael@0: } michael@0: michael@0: bool preRotate(SkScalar degrees) { michael@0: fMatrixID = -1; michael@0: return fMatrix.preRotate(degrees); michael@0: } michael@0: michael@0: bool preSkew(SkScalar sx, SkScalar sy) { michael@0: fMatrixID = -1; michael@0: return fMatrix.preSkew(sx, sy); michael@0: } michael@0: michael@0: bool preConcat(const SkMatrix& matrix) { michael@0: fMatrixID = -1; michael@0: return fMatrix.preConcat(matrix); michael@0: } michael@0: michael@0: void setMatrix(const SkMatrix& matrix) { michael@0: fMatrixID = -1; michael@0: fMatrix = matrix; michael@0: } michael@0: michael@0: int getID(SkMatrixClipStateMgr* mgr) { michael@0: if (fMatrixID >= 0) { michael@0: return fMatrixID; michael@0: } michael@0: michael@0: fMatrixID = mgr->addMatToDict(fMatrix); michael@0: return fMatrixID; michael@0: } michael@0: michael@0: private: michael@0: SkMatrix fMatrix; michael@0: int fMatrixID; michael@0: michael@0: typedef SkNoncopyable INHERITED; michael@0: }; michael@0: michael@0: class ClipInfo : public SkNoncopyable { michael@0: public: michael@0: ClipInfo() {} michael@0: michael@0: bool clipRect(const SkRect& rect, michael@0: SkRegion::Op op, michael@0: bool doAA, michael@0: int matrixID) { michael@0: ClipOp* newClip = fClips.append(); michael@0: newClip->fClipType = kRect_ClipType; michael@0: newClip->fGeom.fRRect.setRect(rect); // storing the clipRect in the RRect michael@0: newClip->fOp = op; michael@0: newClip->fDoAA = doAA; michael@0: newClip->fMatrixID = matrixID; michael@0: return false; michael@0: } michael@0: michael@0: bool clipRRect(const SkRRect& rrect, michael@0: SkRegion::Op op, michael@0: bool doAA, michael@0: int matrixID) { michael@0: ClipOp* newClip = fClips.append(); michael@0: newClip->fClipType = kRRect_ClipType; michael@0: newClip->fGeom.fRRect = rrect; michael@0: newClip->fOp = op; michael@0: newClip->fDoAA = doAA; michael@0: newClip->fMatrixID = matrixID; michael@0: return false; michael@0: } michael@0: michael@0: bool clipPath(SkPictureRecord* picRecord, michael@0: const SkPath& path, michael@0: SkRegion::Op op, michael@0: bool doAA, michael@0: int matrixID); michael@0: bool clipRegion(SkPictureRecord* picRecord, michael@0: int regionID, michael@0: SkRegion::Op op, michael@0: int matrixID); michael@0: void writeClip(int* curMatID, SkMatrixClipStateMgr* mgr); michael@0: michael@0: SkDEBUGCODE(int numClips() const { return fClips.count(); }) michael@0: michael@0: private: michael@0: enum ClipType { michael@0: kRect_ClipType, michael@0: kRRect_ClipType, michael@0: kPath_ClipType, michael@0: kRegion_ClipType michael@0: }; michael@0: michael@0: class ClipOp { michael@0: public: michael@0: ClipType fClipType; michael@0: michael@0: union { michael@0: SkRRect fRRect; // also stores clip rect michael@0: int fPathID; michael@0: int fRegionID; michael@0: } fGeom; michael@0: michael@0: bool fDoAA; michael@0: SkRegion::Op fOp; michael@0: michael@0: // The CTM in effect when this clip call was issued michael@0: int fMatrixID; michael@0: }; michael@0: michael@0: SkTDArray fClips; michael@0: michael@0: typedef SkNoncopyable INHERITED; michael@0: }; michael@0: michael@0: MatrixClipState(MatrixClipState* prev, int flags) michael@0: : fPrev(prev) michael@0: { michael@0: fHasOpen = false; michael@0: michael@0: if (NULL == prev) { michael@0: fLayerID = 0; michael@0: michael@0: fMatrixInfoStorage.reset(); michael@0: fMatrixInfo = &fMatrixInfoStorage; michael@0: fClipInfo = &fClipInfoStorage; // ctor handles init of fClipInfoStorage michael@0: michael@0: // The identity/wide-open-clip state is current by default michael@0: fMCStateID = kIdentityWideOpenStateID; michael@0: #ifdef SK_DEBUG michael@0: fExpectedDepth = 1; michael@0: #endif michael@0: } michael@0: else { michael@0: fLayerID = prev->fLayerID; michael@0: michael@0: if (flags & SkCanvas::kMatrix_SaveFlag) { michael@0: fMatrixInfoStorage = *prev->fMatrixInfo; michael@0: fMatrixInfo = &fMatrixInfoStorage; michael@0: } else { michael@0: fMatrixInfo = prev->fMatrixInfo; michael@0: } michael@0: michael@0: if (flags & SkCanvas::kClip_SaveFlag) { michael@0: // We don't copy the ClipOps of the previous clip states michael@0: fClipInfo = &fClipInfoStorage; michael@0: } else { michael@0: fClipInfo = prev->fClipInfo; michael@0: } michael@0: michael@0: // Initially a new save/saveLayer represents the same MC state michael@0: // as its predecessor. michael@0: fMCStateID = prev->fMCStateID; michael@0: #ifdef SK_DEBUG michael@0: fExpectedDepth = prev->fExpectedDepth; michael@0: #endif michael@0: } michael@0: michael@0: fIsSaveLayer = false; michael@0: } michael@0: michael@0: MatrixInfo* fMatrixInfo; michael@0: MatrixInfo fMatrixInfoStorage; michael@0: michael@0: ClipInfo* fClipInfo; michael@0: ClipInfo fClipInfoStorage; michael@0: michael@0: // Tracks the current depth of saveLayers to support the isDrawingToLayer call michael@0: int fLayerID; michael@0: // Does this MC state represent a saveLayer call? michael@0: bool fIsSaveLayer; michael@0: michael@0: // The next field is only valid when fIsSaveLayer is set. michael@0: SkTDArray* fSavedSkipOffsets; michael@0: michael@0: // Does the MC state have an open block in the skp? michael@0: bool fHasOpen; michael@0: michael@0: MatrixClipState* fPrev; michael@0: michael@0: #ifdef SK_DEBUG michael@0: int fExpectedDepth; // debugging aid michael@0: #endif michael@0: michael@0: int32_t fMCStateID; michael@0: }; michael@0: michael@0: enum CallType { michael@0: kMatrix_CallType, michael@0: kClip_CallType, michael@0: kOther_CallType michael@0: }; michael@0: michael@0: SkMatrixClipStateMgr(); michael@0: ~SkMatrixClipStateMgr(); michael@0: michael@0: void init(SkPictureRecord* picRecord) { michael@0: // Note: we're not taking a ref here. It is expected that the SkMatrixClipStateMgr michael@0: // is owned by the SkPictureRecord object michael@0: fPicRecord = picRecord; michael@0: } michael@0: michael@0: SkPictureRecord* getPicRecord() { return fPicRecord; } michael@0: michael@0: // TODO: need to override canvas' getSaveCount. Right now we pass the michael@0: // save* and restore calls on to the base SkCanvas in SkPictureRecord but michael@0: // this duplicates effort. michael@0: int getSaveCount() const { return fMatrixClipStack.count(); } michael@0: michael@0: int save(SkCanvas::SaveFlags flags); michael@0: michael@0: int saveLayer(const SkRect* bounds, const SkPaint* paint, SkCanvas::SaveFlags flags); michael@0: michael@0: bool isDrawingToLayer() const { michael@0: return fCurMCState->fLayerID > 0; michael@0: } michael@0: michael@0: void restore(); michael@0: michael@0: bool translate(SkScalar dx, SkScalar dy) { michael@0: this->call(kMatrix_CallType); michael@0: return fCurMCState->fMatrixInfo->preTranslate(dx, dy); michael@0: } michael@0: michael@0: bool scale(SkScalar sx, SkScalar sy) { michael@0: this->call(kMatrix_CallType); michael@0: return fCurMCState->fMatrixInfo->preScale(sx, sy); michael@0: } michael@0: michael@0: bool rotate(SkScalar degrees) { michael@0: this->call(kMatrix_CallType); michael@0: return fCurMCState->fMatrixInfo->preRotate(degrees); michael@0: } michael@0: michael@0: bool skew(SkScalar sx, SkScalar sy) { michael@0: this->call(kMatrix_CallType); michael@0: return fCurMCState->fMatrixInfo->preSkew(sx, sy); michael@0: } michael@0: michael@0: bool concat(const SkMatrix& matrix) { michael@0: this->call(kMatrix_CallType); michael@0: return fCurMCState->fMatrixInfo->preConcat(matrix); michael@0: } michael@0: michael@0: void setMatrix(const SkMatrix& matrix) { michael@0: this->call(kMatrix_CallType); michael@0: fCurMCState->fMatrixInfo->setMatrix(matrix); michael@0: } michael@0: michael@0: bool clipRect(const SkRect& rect, SkRegion::Op op, bool doAA) { michael@0: this->call(SkMatrixClipStateMgr::kClip_CallType); michael@0: return fCurMCState->fClipInfo->clipRect(rect, op, doAA, michael@0: fCurMCState->fMatrixInfo->getID(this)); michael@0: } michael@0: michael@0: bool clipRRect(const SkRRect& rrect, SkRegion::Op op, bool doAA) { michael@0: this->call(SkMatrixClipStateMgr::kClip_CallType); michael@0: return fCurMCState->fClipInfo->clipRRect(rrect, op, doAA, michael@0: fCurMCState->fMatrixInfo->getID(this)); michael@0: } michael@0: michael@0: bool clipPath(const SkPath& path, SkRegion::Op op, bool doAA) { michael@0: this->call(SkMatrixClipStateMgr::kClip_CallType); michael@0: return fCurMCState->fClipInfo->clipPath(fPicRecord, path, op, doAA, michael@0: fCurMCState->fMatrixInfo->getID(this)); michael@0: } michael@0: michael@0: bool clipRegion(const SkRegion& region, SkRegion::Op op) { michael@0: this->call(SkMatrixClipStateMgr::kClip_CallType); michael@0: int regionID = this->addRegionToDict(region); michael@0: return fCurMCState->fClipInfo->clipRegion(fPicRecord, regionID, op, michael@0: fCurMCState->fMatrixInfo->getID(this)); michael@0: } michael@0: michael@0: bool call(CallType callType); michael@0: michael@0: void fillInSkips(SkWriter32* writer, int32_t restoreOffset); michael@0: michael@0: void finish(); michael@0: michael@0: protected: michael@0: SkPictureRecord* fPicRecord; michael@0: michael@0: uint32_t fMatrixClipStackStorage[43]; // sized to fit 2 clip states michael@0: SkDeque fMatrixClipStack; michael@0: MatrixClipState* fCurMCState; michael@0: michael@0: // This dictionary doesn't actually de-duplicate the matrices (except for the michael@0: // identity matrix). It merely stores the matrices and allows them to be looked michael@0: // up by ID later. The de-duplication mainly falls upon the matrix/clip stack michael@0: // which stores the ID so a revisited clip/matrix (via popping the stack) will michael@0: // use the same ID. michael@0: SkTDArray fMatrixDict; michael@0: michael@0: SkTDArray fRegionDict; michael@0: michael@0: // The MCStateID of the state currently in effect in the byte stream. 0 if none. michael@0: int32_t fCurOpenStateID; michael@0: // The skip offsets for the current open state. These are the locations in the michael@0: // skp that must be filled in when the current open state is closed. These are michael@0: // here rather then distributed across the MatrixClipState's because saveLayers michael@0: // can cause MC states to be nested. michael@0: SkTDArray *fSkipOffsets; michael@0: michael@0: SkDEBUGCODE(void validate();) michael@0: michael@0: int MCStackPush(SkCanvas::SaveFlags flags); michael@0: michael@0: void addClipOffset(int offset) { michael@0: SkASSERT(NULL != fSkipOffsets); michael@0: SkASSERT(kIdentityWideOpenStateID != fCurOpenStateID); michael@0: SkASSERT(fCurMCState->fHasOpen); michael@0: SkASSERT(!fCurMCState->fIsSaveLayer); michael@0: michael@0: *fSkipOffsets->append() = offset; michael@0: } michael@0: michael@0: void writeDeltaMat(int currentMatID, int desiredMatID); michael@0: static int32_t NewMCStateID(); michael@0: michael@0: int addRegionToDict(const SkRegion& region); michael@0: const SkRegion* lookupRegion(int index) { michael@0: SkASSERT(index >= 0 && index < fRegionDict.count()); michael@0: return fRegionDict[index]; michael@0: } michael@0: michael@0: // TODO: add stats to check if the dictionary really does michael@0: // reduce the size of the SkPicture. michael@0: int addMatToDict(const SkMatrix& mat); michael@0: const SkMatrix& lookupMat(int index) { michael@0: SkASSERT(index >= 0 && index < fMatrixDict.count()); michael@0: return fMatrixDict[index]; michael@0: } michael@0: michael@0: bool isNestingMCState(int stateID); michael@0: michael@0: #ifdef SK_DEBUG michael@0: int fActualDepth; michael@0: #endif michael@0: michael@0: // save layers are nested within a specific MC state. This stack tracks michael@0: // the nesting MC state's ID as save layers are pushed and popped. michael@0: SkTDArray fStateIDStack; michael@0: }; michael@0: michael@0: #endif