SkMatrixClipStateMgr.h revision 22ef2c37e65e601ba78851e3dbb4ca7a0e4c7d61
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 17class SkPictureRecord; 18class 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. 51class SkMatrixClipStateMgr { 52public: 53 static const int32_t kIdentityWideOpenStateID = 0; 54 static const int kIdentityMatID = 0; 55 56 class MatrixClipState { 57 public: 58 class MatrixInfo { 59 public: 60 void reset() { 61 fMatrixID = kIdentityMatID; 62 fMatrix.reset(); 63 } 64 65 bool preTranslate(SkScalar dx, SkScalar dy) { 66 fMatrixID = -1; 67 return fMatrix.preTranslate(dx, dy); 68 } 69 70 bool preScale(SkScalar sx, SkScalar sy) { 71 fMatrixID = -1; 72 return fMatrix.preScale(sx, sy); 73 } 74 75 bool preRotate(SkScalar degrees) { 76 fMatrixID = -1; 77 return fMatrix.preRotate(degrees); 78 } 79 80 bool preSkew(SkScalar sx, SkScalar sy) { 81 fMatrixID = -1; 82 return fMatrix.preSkew(sx, sy); 83 } 84 85 bool preConcat(const SkMatrix& matrix) { 86 fMatrixID = -1; 87 return fMatrix.preConcat(matrix); 88 } 89 90 void setMatrix(const SkMatrix& matrix) { 91 fMatrixID = -1; 92 fMatrix = matrix; 93 } 94 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 109 class ClipInfo : public SkNoncopyable { 110 public: 111 ClipInfo() {} 112 113 bool clipRect(const SkRect& rect, 114 SkRegion::Op op, 115 bool doAA, 116 int matrixID) { 117 ClipOp* newClip = fClips.append(); 118 newClip->fClipType = kRect_ClipType; 119 newClip->fGeom.fRRect.setRect(rect); // storing the clipRect in the RRect 120 newClip->fOp = op; 121 newClip->fDoAA = doAA; 122 newClip->fMatrixID = matrixID; 123 newClip->fOffset = kInvalidJumpOffset; 124 return false; 125 } 126 127 bool clipRRect(const SkRRect& rrect, 128 SkRegion::Op op, 129 bool doAA, 130 int matrixID) { 131 ClipOp* newClip = fClips.append(); 132 newClip->fClipType = kRRect_ClipType; 133 newClip->fGeom.fRRect = rrect; 134 newClip->fOp = op; 135 newClip->fDoAA = doAA; 136 newClip->fMatrixID = matrixID; 137 newClip->fOffset = kInvalidJumpOffset; 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, 151 SkMatrixClipStateMgr* mgr, 152 bool* overrideFirstOp); 153 void fillInSkips(SkWriter32* writer, int32_t restoreOffset); 154 155#ifdef SK_DEBUG 156 void checkOffsetNotEqual(int32_t offset) { 157 for (int i = 0; i < fClips.count(); ++i) { 158 ClipOp& curClip = fClips[i]; 159 SkASSERT(offset != curClip.fOffset); 160 } 161 } 162#endif 163 private: 164 enum ClipType { 165 kRect_ClipType, 166 kRRect_ClipType, 167 kPath_ClipType, 168 kRegion_ClipType 169 }; 170 171 static const int kInvalidJumpOffset = -1; 172 173 class ClipOp { 174 public: 175 ClipType fClipType; 176 177 union { 178 SkRRect fRRect; // also stores clipRect 179 int fPathID; 180 int fRegionID; 181 } fGeom; 182 183 bool fDoAA; 184 SkRegion::Op fOp; 185 186 // The CTM in effect when this clip call was issued 187 int fMatrixID; 188 189 // The offset of this clipOp's "jump-to-offset" location in the skp. 190 // -1 means the offset hasn't been written. 191 int32_t fOffset; 192 }; 193 194 SkTDArray<ClipOp> fClips; 195 196 typedef SkNoncopyable INHERITED; 197 }; 198 199 MatrixClipState(MatrixClipState* prev, int flags) 200#ifdef SK_DEBUG 201 : fPrev(prev) 202#endif 203 { 204 if (NULL == prev) { 205 fLayerID = 0; 206 207 fMatrixInfoStorage.reset(); 208 fMatrixInfo = &fMatrixInfoStorage; 209 fClipInfo = &fClipInfoStorage; // ctor handles init of fClipInfoStorage 210 211 // The identity/wide-open-clip state is current by default 212 fMCStateID = kIdentityWideOpenStateID; 213 } 214 else { 215 fLayerID = prev->fLayerID; 216 217 if (flags & SkCanvas::kMatrix_SaveFlag) { 218 fMatrixInfoStorage = *prev->fMatrixInfo; 219 fMatrixInfo = &fMatrixInfoStorage; 220 } else { 221 fMatrixInfo = prev->fMatrixInfo; 222 } 223 224 if (flags & SkCanvas::kClip_SaveFlag) { 225 // We don't copy the ClipOps of the previous clip states 226 fClipInfo = &fClipInfoStorage; 227 } else { 228 fClipInfo = prev->fClipInfo; 229 } 230 231 // Initially a new save/saveLayer represents the same MC state 232 // as its predecessor. 233 fMCStateID = prev->fMCStateID; 234 } 235 236 fIsSaveLayer = false; 237 } 238 239 MatrixInfo* fMatrixInfo; 240 MatrixInfo fMatrixInfoStorage; 241 242 ClipInfo* fClipInfo; 243 ClipInfo fClipInfoStorage; 244 245 // Tracks the current depth of saveLayers to support the isDrawingToLayer call 246 int fLayerID; 247 // Does this MC state represent a saveLayer call? 248 bool fIsSaveLayer; 249 250 // The next two fields are only valid when fIsSaveLayer is set. 251 int32_t fSaveLayerBaseStateID; 252 bool fSaveLayerBracketed; 253 254#ifdef SK_DEBUG 255 MatrixClipState* fPrev; // debugging aid 256#endif 257 258 int32_t fMCStateID; 259 }; 260 261 enum CallType { 262 kMatrix_CallType, 263 kClip_CallType, 264 kOther_CallType 265 }; 266 267 SkMatrixClipStateMgr(); 268 ~SkMatrixClipStateMgr(); 269 270 void init(SkPictureRecord* picRecord) { 271 // Note: we're not taking a ref here. It is expected that the SkMatrixClipStateMgr 272 // is owned by the SkPictureRecord object 273 fPicRecord = picRecord; 274 } 275 276 SkPictureRecord* getPicRecord() { return fPicRecord; } 277 278 // TODO: need to override canvas' getSaveCount. Right now we pass the 279 // save* and restore calls on to the base SkCanvas in SkPictureRecord but 280 // this duplicates effort. 281 int getSaveCount() const { return fMatrixClipStack.count(); } 282 283 int save(SkCanvas::SaveFlags flags); 284 285 int saveLayer(const SkRect* bounds, const SkPaint* paint, SkCanvas::SaveFlags flags); 286 287 bool isDrawingToLayer() const { 288 return fCurMCState->fLayerID > 0; 289 } 290 291 void restore(); 292 293 bool translate(SkScalar dx, SkScalar dy) { 294 this->call(kMatrix_CallType); 295 return fCurMCState->fMatrixInfo->preTranslate(dx, dy); 296 } 297 298 bool scale(SkScalar sx, SkScalar sy) { 299 this->call(kMatrix_CallType); 300 return fCurMCState->fMatrixInfo->preScale(sx, sy); 301 } 302 303 bool rotate(SkScalar degrees) { 304 this->call(kMatrix_CallType); 305 return fCurMCState->fMatrixInfo->preRotate(degrees); 306 } 307 308 bool skew(SkScalar sx, SkScalar sy) { 309 this->call(kMatrix_CallType); 310 return fCurMCState->fMatrixInfo->preSkew(sx, sy); 311 } 312 313 bool concat(const SkMatrix& matrix) { 314 this->call(kMatrix_CallType); 315 return fCurMCState->fMatrixInfo->preConcat(matrix); 316 } 317 318 void setMatrix(const SkMatrix& matrix) { 319 this->call(kMatrix_CallType); 320 fCurMCState->fMatrixInfo->setMatrix(matrix); 321 } 322 323 bool clipRect(const SkRect& rect, SkRegion::Op op, bool doAA) { 324 this->call(SkMatrixClipStateMgr::kClip_CallType); 325 return fCurMCState->fClipInfo->clipRect(rect, op, doAA, 326 fCurMCState->fMatrixInfo->getID(this)); 327 } 328 329 bool clipRRect(const SkRRect& rrect, SkRegion::Op op, bool doAA) { 330 this->call(SkMatrixClipStateMgr::kClip_CallType); 331 return fCurMCState->fClipInfo->clipRRect(rrect, op, doAA, 332 fCurMCState->fMatrixInfo->getID(this)); 333 } 334 335 bool clipPath(const SkPath& path, SkRegion::Op op, bool doAA) { 336 this->call(SkMatrixClipStateMgr::kClip_CallType); 337 return fCurMCState->fClipInfo->clipPath(fPicRecord, path, op, doAA, 338 fCurMCState->fMatrixInfo->getID(this)); 339 } 340 341 bool clipRegion(const SkRegion& region, SkRegion::Op op) { 342 this->call(SkMatrixClipStateMgr::kClip_CallType); 343 int regionID = this->addRegionToDict(region); 344 return fCurMCState->fClipInfo->clipRegion(fPicRecord, regionID, op, 345 fCurMCState->fMatrixInfo->getID(this)); 346 } 347 348 bool call(CallType callType); 349 350 void fillInSkips(SkWriter32* writer, int32_t restoreOffset) { 351 // Since we write out the entire clip stack at each block start we 352 // need to update the skips for the entire stack each time too. 353 SkDeque::F2BIter iter(fMatrixClipStack); 354 355 for (const MatrixClipState* state = (const MatrixClipState*) iter.next(); 356 state != NULL; 357 state = (const MatrixClipState*) iter.next()) { 358 state->fClipInfo->fillInSkips(writer, restoreOffset); 359 } 360 } 361 362 void finish(); 363 364protected: 365 SkPictureRecord* fPicRecord; 366 367 uint32_t fMatrixClipStackStorage[43]; // sized to fit 2 clip states 368 SkDeque fMatrixClipStack; 369 MatrixClipState* fCurMCState; 370 371 // This dictionary doesn't actually de-duplicate the matrices (except for the 372 // identity matrix). It merely stores the matrices and allows them to be looked 373 // up by ID later. The de-duplication mainly falls upon the matrix/clip stack 374 // which stores the ID so a revisited clip/matrix (via popping the stack) will 375 // use the same ID. 376 SkTDArray<SkMatrix> fMatrixDict; 377 378 SkTDArray<SkRegion*> fRegionDict; 379 380 // The MCStateID of the state currently in effect in the byte stream. 0 if none. 381 int32_t fCurOpenStateID; 382 383 SkDEBUGCODE(void validate();) 384 385 void writeDeltaMat(int currentMatID, int desiredMatID); 386 static int32_t NewMCStateID(); 387 388 int addRegionToDict(const SkRegion& region); 389 const SkRegion* lookupRegion(int index) { 390 SkASSERT(index >= 0 && index < fRegionDict.count()); 391 return fRegionDict[index]; 392 } 393 394 // TODO: add stats to check if the dictionary really does 395 // reduce the size of the SkPicture. 396 int addMatToDict(const SkMatrix& mat); 397 const SkMatrix& lookupMat(int index) { 398 SkASSERT(index >= 0 && index < fMatrixDict.count()); 399 return fMatrixDict[index]; 400 } 401}; 402 403#endif 404