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 #ifndef SkMatrixClipStateMgr_DEFINED 8 #define SkMatrixClipStateMgr_DEFINED 9 10 #include "SkCanvas.h" 11 #include "SkMatrix.h" 12 #include "SkRegion.h" 13 #include "SkRRect.h" 14 #include "SkTypes.h" 15 #include "SkTDArray.h" 16 17 class SkPictureRecord; 18 class SkWriter32; 19 20 // The SkMatrixClipStateMgr collapses the matrix/clip state of an SkPicture into 21 // a series of save/restore blocks of consistent matrix clip state, e.g.: 22 // 23 // save 24 // clip(s) 25 // concat 26 // ... draw ops ... 27 // restore 28 // 29 // SaveLayers simply add another level, e.g.: 30 // 31 // save 32 // clip(s) 33 // concat 34 // ... draw ops ... 35 // saveLayer 36 // save 37 // clip(s) 38 // concat 39 // ... draw ops ... 40 // restore 41 // restore 42 // restore 43 // 44 // As a side effect of this process all saves and saveLayers will become 45 // kMatrixClip_SaveFlag style saves/saveLayers. 46 47 // The SkMatrixClipStateMgr works by intercepting all the save*, restore, clip*, 48 // and matrix calls sent to SkCanvas in order to track the current matrix/clip 49 // state. All the other canvas calls get funnelled into a generic "call" entry 50 // point that signals that a state block is required. 51 class SkMatrixClipStateMgr { 52 public: 53 static const int32_t kIdentityWideOpenStateID = 0; 54 static const int kIdentityMatID = 0; 55 56 class MatrixClipState : SkNoncopyable { 57 public: 58 class MatrixInfo { 59 public: reset()60 void reset() { 61 fMatrixID = kIdentityMatID; 62 fMatrix.reset(); 63 } 64 preTranslate(SkScalar dx,SkScalar dy)65 void preTranslate(SkScalar dx, SkScalar dy) { 66 fMatrixID = -1; 67 fMatrix.preTranslate(dx, dy); 68 } 69 preScale(SkScalar sx,SkScalar sy)70 void preScale(SkScalar sx, SkScalar sy) { 71 fMatrixID = -1; 72 fMatrix.preScale(sx, sy); 73 } 74 preRotate(SkScalar degrees)75 void preRotate(SkScalar degrees) { 76 fMatrixID = -1; 77 fMatrix.preRotate(degrees); 78 } 79 preSkew(SkScalar sx,SkScalar sy)80 void preSkew(SkScalar sx, SkScalar sy) { 81 fMatrixID = -1; 82 fMatrix.preSkew(sx, sy); 83 } 84 preConcat(const SkMatrix & matrix)85 void preConcat(const SkMatrix& matrix) { 86 fMatrixID = -1; 87 fMatrix.preConcat(matrix); 88 } 89 setMatrix(const SkMatrix & matrix)90 void setMatrix(const SkMatrix& matrix) { 91 fMatrixID = -1; 92 fMatrix = matrix; 93 } 94 getID(SkMatrixClipStateMgr * mgr)95 int getID(SkMatrixClipStateMgr* mgr) { 96 if (fMatrixID >= 0) { 97 return fMatrixID; 98 } 99 100 fMatrixID = mgr->addMatToDict(fMatrix); 101 return fMatrixID; 102 } 103 104 private: 105 SkMatrix fMatrix; 106 int fMatrixID; 107 108 typedef SkNoncopyable INHERITED; 109 }; 110 111 class ClipInfo : SkNoncopyable { 112 public: ClipInfo()113 ClipInfo() {} 114 clipRect(const SkRect & rect,SkRegion::Op op,bool doAA,int matrixID)115 bool clipRect(const SkRect& rect, 116 SkRegion::Op op, 117 bool doAA, 118 int matrixID) { 119 ClipOp* newClip = fClips.append(); 120 newClip->fClipType = kRect_ClipType; 121 newClip->fGeom.fRRect.setRect(rect); // storing the clipRect in the RRect 122 newClip->fOp = op; 123 newClip->fDoAA = doAA; 124 newClip->fMatrixID = matrixID; 125 return false; 126 } 127 clipRRect(const SkRRect & rrect,SkRegion::Op op,bool doAA,int matrixID)128 bool clipRRect(const SkRRect& rrect, 129 SkRegion::Op op, 130 bool doAA, 131 int matrixID) { 132 ClipOp* newClip = fClips.append(); 133 newClip->fClipType = kRRect_ClipType; 134 newClip->fGeom.fRRect = rrect; 135 newClip->fOp = op; 136 newClip->fDoAA = doAA; 137 newClip->fMatrixID = matrixID; 138 return false; 139 } 140 141 bool clipPath(SkPictureRecord* picRecord, 142 const SkPath& path, 143 SkRegion::Op op, 144 bool doAA, 145 int matrixID); 146 bool clipRegion(SkPictureRecord* picRecord, 147 int regionID, 148 SkRegion::Op op, 149 int matrixID); 150 void writeClip(int* curMatID, SkMatrixClipStateMgr* mgr); 151 152 SkDEBUGCODE(int numClips() const { return fClips.count(); }) 153 154 private: 155 enum ClipType { 156 kRect_ClipType, 157 kRRect_ClipType, 158 kPath_ClipType, 159 kRegion_ClipType 160 }; 161 162 class ClipOp { 163 public: 164 ClipType fClipType; 165 166 union { 167 SkRRect fRRect; // also stores clip rect 168 int fPathID; 169 int fRegionID; 170 } fGeom; 171 172 bool fDoAA; 173 SkRegion::Op fOp; 174 175 // The CTM in effect when this clip call was issued 176 int fMatrixID; 177 }; 178 179 SkTDArray<ClipOp> fClips; 180 181 typedef SkNoncopyable INHERITED; 182 }; 183 MatrixClipState(MatrixClipState * prev,int flags)184 MatrixClipState(MatrixClipState* prev, int flags) 185 : fPrev(prev) 186 { 187 fHasOpen = false; 188 189 if (NULL == prev) { 190 fLayerID = 0; 191 192 fMatrixInfoStorage.reset(); 193 fMatrixInfo = &fMatrixInfoStorage; 194 fClipInfo = &fClipInfoStorage; // ctor handles init of fClipInfoStorage 195 196 // The identity/wide-open-clip state is current by default 197 fMCStateID = kIdentityWideOpenStateID; 198 #ifdef SK_DEBUG 199 fExpectedDepth = 1; 200 #endif 201 } 202 else { 203 fLayerID = prev->fLayerID; 204 205 if (flags & SkCanvas::kMatrix_SaveFlag) { 206 fMatrixInfoStorage = *prev->fMatrixInfo; 207 fMatrixInfo = &fMatrixInfoStorage; 208 } else { 209 fMatrixInfo = prev->fMatrixInfo; 210 } 211 212 if (flags & SkCanvas::kClip_SaveFlag) { 213 // We don't copy the ClipOps of the previous clip states 214 fClipInfo = &fClipInfoStorage; 215 } else { 216 fClipInfo = prev->fClipInfo; 217 } 218 219 // Initially a new save/saveLayer represents the same MC state 220 // as its predecessor. 221 fMCStateID = prev->fMCStateID; 222 #ifdef SK_DEBUG 223 fExpectedDepth = prev->fExpectedDepth; 224 #endif 225 } 226 227 fIsSaveLayer = false; 228 } 229 230 MatrixInfo* fMatrixInfo; 231 MatrixInfo fMatrixInfoStorage; 232 233 ClipInfo* fClipInfo; 234 ClipInfo fClipInfoStorage; 235 236 // Tracks the current depth of saveLayers to support the isDrawingToLayer call 237 int fLayerID; 238 // Does this MC state represent a saveLayer call? 239 bool fIsSaveLayer; 240 241 // The next field is only valid when fIsSaveLayer is set. 242 SkTDArray<int>* fSavedSkipOffsets; 243 244 // Does the MC state have an open block in the skp? 245 bool fHasOpen; 246 247 MatrixClipState* fPrev; 248 249 #ifdef SK_DEBUG 250 int fExpectedDepth; // debugging aid 251 #endif 252 253 int32_t fMCStateID; 254 }; 255 256 enum CallType { 257 kMatrix_CallType, 258 kClip_CallType, 259 kOther_CallType 260 }; 261 262 SkMatrixClipStateMgr(); 263 ~SkMatrixClipStateMgr(); 264 init(SkPictureRecord * picRecord)265 void init(SkPictureRecord* picRecord) { 266 // Note: we're not taking a ref here. It is expected that the SkMatrixClipStateMgr 267 // is owned by the SkPictureRecord object 268 fPicRecord = picRecord; 269 } 270 getPicRecord()271 SkPictureRecord* getPicRecord() { return fPicRecord; } 272 273 // TODO: need to override canvas' getSaveCount. Right now we pass the 274 // save* and restore calls on to the base SkCanvas in SkPictureRecord but 275 // this duplicates effort. getSaveCount()276 int getSaveCount() const { return fMatrixClipStack.count(); } 277 278 int save(SkCanvas::SaveFlags flags); 279 280 int saveLayer(const SkRect* bounds, const SkPaint* paint, SkCanvas::SaveFlags flags); 281 isDrawingToLayer()282 bool isDrawingToLayer() const { 283 return fCurMCState->fLayerID > 0; 284 } 285 286 void restore(); 287 translate(SkScalar dx,SkScalar dy)288 void translate(SkScalar dx, SkScalar dy) { 289 this->call(kMatrix_CallType); 290 fCurMCState->fMatrixInfo->preTranslate(dx, dy); 291 } 292 scale(SkScalar sx,SkScalar sy)293 void scale(SkScalar sx, SkScalar sy) { 294 this->call(kMatrix_CallType); 295 fCurMCState->fMatrixInfo->preScale(sx, sy); 296 } 297 rotate(SkScalar degrees)298 void rotate(SkScalar degrees) { 299 this->call(kMatrix_CallType); 300 fCurMCState->fMatrixInfo->preRotate(degrees); 301 } 302 skew(SkScalar sx,SkScalar sy)303 void skew(SkScalar sx, SkScalar sy) { 304 this->call(kMatrix_CallType); 305 fCurMCState->fMatrixInfo->preSkew(sx, sy); 306 } 307 concat(const SkMatrix & matrix)308 void concat(const SkMatrix& matrix) { 309 this->call(kMatrix_CallType); 310 fCurMCState->fMatrixInfo->preConcat(matrix); 311 } 312 setMatrix(const SkMatrix & matrix)313 void setMatrix(const SkMatrix& matrix) { 314 this->call(kMatrix_CallType); 315 fCurMCState->fMatrixInfo->setMatrix(matrix); 316 } 317 clipRect(const SkRect & rect,SkRegion::Op op,bool doAA)318 bool clipRect(const SkRect& rect, SkRegion::Op op, bool doAA) { 319 this->call(SkMatrixClipStateMgr::kClip_CallType); 320 return fCurMCState->fClipInfo->clipRect(rect, op, doAA, 321 fCurMCState->fMatrixInfo->getID(this)); 322 } 323 clipRRect(const SkRRect & rrect,SkRegion::Op op,bool doAA)324 bool clipRRect(const SkRRect& rrect, SkRegion::Op op, bool doAA) { 325 this->call(SkMatrixClipStateMgr::kClip_CallType); 326 return fCurMCState->fClipInfo->clipRRect(rrect, op, doAA, 327 fCurMCState->fMatrixInfo->getID(this)); 328 } 329 clipPath(const SkPath & path,SkRegion::Op op,bool doAA)330 bool clipPath(const SkPath& path, SkRegion::Op op, bool doAA) { 331 this->call(SkMatrixClipStateMgr::kClip_CallType); 332 return fCurMCState->fClipInfo->clipPath(fPicRecord, path, op, doAA, 333 fCurMCState->fMatrixInfo->getID(this)); 334 } 335 clipRegion(const SkRegion & region,SkRegion::Op op)336 bool clipRegion(const SkRegion& region, SkRegion::Op op) { 337 this->call(SkMatrixClipStateMgr::kClip_CallType); 338 int regionID = this->addRegionToDict(region); 339 return fCurMCState->fClipInfo->clipRegion(fPicRecord, regionID, op, 340 fCurMCState->fMatrixInfo->getID(this)); 341 } 342 343 bool call(CallType callType); 344 345 void fillInSkips(SkWriter32* writer, int32_t restoreOffset); 346 347 void finish(); 348 349 protected: 350 SkPictureRecord* fPicRecord; 351 352 uint32_t fMatrixClipStackStorage[43]; // sized to fit 2 clip states 353 SkDeque fMatrixClipStack; 354 MatrixClipState* fCurMCState; 355 356 // This dictionary doesn't actually de-duplicate the matrices (except for the 357 // identity matrix). It merely stores the matrices and allows them to be looked 358 // up by ID later. The de-duplication mainly falls upon the matrix/clip stack 359 // which stores the ID so a revisited clip/matrix (via popping the stack) will 360 // use the same ID. 361 SkTDArray<SkMatrix> fMatrixDict; 362 363 SkTDArray<SkRegion*> fRegionDict; 364 365 // The MCStateID of the state currently in effect in the byte stream. 0 if none. 366 int32_t fCurOpenStateID; 367 // The skip offsets for the current open state. These are the locations in the 368 // skp that must be filled in when the current open state is closed. These are 369 // here rather then distributed across the MatrixClipState's because saveLayers 370 // can cause MC states to be nested. 371 SkTDArray<int32_t> *fSkipOffsets; // TODO: should we store u32 or size_t instead? 372 373 SkDEBUGCODE(void validate();) 374 375 int MCStackPush(SkCanvas::SaveFlags flags); 376 addClipOffset(size_t offset)377 void addClipOffset(size_t offset) { 378 SkASSERT(NULL != fSkipOffsets); 379 SkASSERT(kIdentityWideOpenStateID != fCurOpenStateID); 380 SkASSERT(fCurMCState->fHasOpen); 381 SkASSERT(!fCurMCState->fIsSaveLayer); 382 383 *fSkipOffsets->append() = SkToS32(offset); 384 } 385 386 void writeDeltaMat(int currentMatID, int desiredMatID); 387 static int32_t NewMCStateID(); 388 389 int addRegionToDict(const SkRegion& region); lookupRegion(int index)390 const SkRegion* lookupRegion(int index) { 391 SkASSERT(index >= 0 && index < fRegionDict.count()); 392 return fRegionDict[index]; 393 } 394 395 // TODO: add stats to check if the dictionary really does 396 // reduce the size of the SkPicture. 397 int addMatToDict(const SkMatrix& mat); lookupMat(int index)398 const SkMatrix& lookupMat(int index) { 399 SkASSERT(index >= 0 && index < fMatrixDict.count()); 400 return fMatrixDict[index]; 401 } 402 403 bool isNestingMCState(int stateID); 404 405 #ifdef SK_DEBUG 406 int fActualDepth; 407 #endif 408 409 // save layers are nested within a specific MC state. This stack tracks 410 // the nesting MC state's ID as save layers are pushed and popped. 411 SkTDArray<int> fStateIDStack; 412 }; 413 414 #endif 415