SkMatrixClipStateMgr.h revision 105a4a584c4c2c84c24e102112326b15683673f5
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 "SkTArray.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 55 class MatrixClipState { 56 public: 57 class MatrixInfo { 58 public: 59 SkMatrix fMatrix; 60 // TODO: add an internal dictionary and an ID here 61 }; 62 63 class ClipInfo : public SkNoncopyable { 64 public: 65 ClipInfo() {} 66 67 bool clipRect(const SkRect& rect, 68 SkRegion::Op op, 69 bool doAA, 70 const SkMatrix& matrix) { 71 ClipOp& newClip = fClips.push_back(); 72 newClip.fClipType = kRect_ClipType; 73 newClip.fGeom.fRRect.setRect(rect); // storing the clipRect in the RRect 74 newClip.fOp = op; 75 newClip.fDoAA = doAA; 76 newClip.fMatrix = matrix; 77 newClip.fOffset = kInvalidJumpOffset; 78 return false; 79 } 80 81 bool clipRRect(const SkRRect& rrect, 82 SkRegion::Op op, 83 bool doAA, 84 const SkMatrix& matrix) { 85 ClipOp& newClip = fClips.push_back(); 86 newClip.fClipType = kRRect_ClipType; 87 newClip.fGeom.fRRect = rrect; 88 newClip.fOp = op; 89 newClip.fDoAA = doAA; 90 newClip.fMatrix = matrix; 91 newClip.fOffset = kInvalidJumpOffset; 92 return false; 93 } 94 95 bool clipPath(SkPictureRecord* picRecord, 96 const SkPath& path, 97 SkRegion::Op op, 98 bool doAA, 99 const SkMatrix& matrix); 100 bool clipRegion(SkPictureRecord* picRecord, 101 const SkRegion& region, 102 SkRegion::Op op, 103 const SkMatrix& matrix); 104 void writeClip(SkMatrix* curMat, 105 SkPictureRecord* picRecord, 106 bool* overrideFirstOp); 107 void fillInSkips(SkWriter32* writer, int32_t restoreOffset); 108 109#ifdef SK_DEBUG 110 void checkOffsetNotEqual(int32_t offset) { 111 for (int i = 0; i < fClips.count(); ++i) { 112 ClipOp& curClip = fClips[i]; 113 SkASSERT(offset != curClip.fOffset); 114 } 115 } 116#endif 117 private: 118 enum ClipType { 119 kRect_ClipType, 120 kRRect_ClipType, 121 kPath_ClipType, 122 kRegion_ClipType 123 }; 124 125 static const int kInvalidJumpOffset = -1; 126 127 class ClipOp { 128 public: 129 ClipOp() {} 130 ~ClipOp() { 131 if (kRegion_ClipType == fClipType) { 132 SkDELETE(fGeom.fRegion); 133 } 134 } 135 136 ClipType fClipType; 137 138 union { 139 SkRRect fRRect; // also stores clipRect 140 int fPathID; 141 // TODO: add internal dictionary of regions 142 // This parameter forces us to have a dtor and thus use 143 // SkTArray rather then SkTDArray! 144 const SkRegion* fRegion; 145 } fGeom; 146 147 bool fDoAA; 148 SkRegion::Op fOp; 149 150 // The CTM in effect when this clip call was issued 151 // TODO: add an internal dictionary and replace with ID 152 SkMatrix fMatrix; 153 154 // The offset of this clipOp's "jump-to-offset" location in the skp. 155 // -1 means the offset hasn't been written. 156 int32_t fOffset; 157 }; 158 159 SkTArray<ClipOp> fClips; 160 161 typedef SkNoncopyable INHERITED; 162 }; 163 164 MatrixClipState(MatrixClipState* prev, int flags) 165#ifdef SK_DEBUG 166 : fPrev(prev) 167#endif 168 { 169 if (NULL == prev) { 170 fLayerID = 0; 171 172 fMatrixInfoStorage.fMatrix.reset(); 173 fMatrixInfo = &fMatrixInfoStorage; 174 fClipInfo = &fClipInfoStorage; // ctor handles init of fClipInfoStorage 175 176 // The identity/wide-open-clip state is current by default 177 fMCStateID = kIdentityWideOpenStateID; 178 } 179 else { 180 fLayerID = prev->fLayerID; 181 182 if (flags & SkCanvas::kMatrix_SaveFlag) { 183 fMatrixInfoStorage = *prev->fMatrixInfo; 184 fMatrixInfo = &fMatrixInfoStorage; 185 } else { 186 fMatrixInfo = prev->fMatrixInfo; 187 } 188 189 if (flags & SkCanvas::kClip_SaveFlag) { 190 // We don't copy the ClipOps of the previous clip states 191 fClipInfo = &fClipInfoStorage; 192 } else { 193 fClipInfo = prev->fClipInfo; 194 } 195 196 // Initially a new save/saveLayer represents the same MC state 197 // as its predecessor. 198 fMCStateID = prev->fMCStateID; 199 } 200 201 fIsSaveLayer = false; 202 } 203 204 MatrixInfo* fMatrixInfo; 205 MatrixInfo fMatrixInfoStorage; 206 207 ClipInfo* fClipInfo; 208 ClipInfo fClipInfoStorage; 209 210 // Tracks the current depth of saveLayers to support the isDrawingToLayer call 211 int fLayerID; 212 // Does this MC state represent a saveLayer call? 213 bool fIsSaveLayer; 214 215 // The next two fields are only valid when fIsSaveLayer is set. 216 int32_t fSaveLayerBaseStateID; 217 bool fSaveLayerBracketed; 218 219#ifdef SK_DEBUG 220 MatrixClipState* fPrev; // debugging aid 221#endif 222 223 int32_t fMCStateID; 224 }; 225 226 enum CallType { 227 kMatrix_CallType, 228 kClip_CallType, 229 kOther_CallType 230 }; 231 232 SkMatrixClipStateMgr(); 233 234 void init(SkPictureRecord* picRecord) { 235 // Note: we're not taking a ref here. It is expected that the SkMatrixClipStateMgr 236 // is owned by the SkPictureRecord object 237 fPicRecord = picRecord; 238 } 239 240 // TODO: need to override canvas' getSaveCount. Right now we pass the 241 // save* and restore calls on to the base SkCanvas in SkPictureRecord but 242 // this duplicates effort. 243 int getSaveCount() const { return fMatrixClipStack.count(); } 244 245 int save(SkCanvas::SaveFlags flags); 246 247 int saveLayer(const SkRect* bounds, const SkPaint* paint, SkCanvas::SaveFlags flags); 248 249 bool isDrawingToLayer() const { 250 return fCurMCState->fLayerID > 0; 251 } 252 253 void restore(); 254 255 bool translate(SkScalar dx, SkScalar dy) { 256 this->call(kMatrix_CallType); 257 return fCurMCState->fMatrixInfo->fMatrix.preTranslate(dx, dy); 258 } 259 260 bool scale(SkScalar sx, SkScalar sy) { 261 this->call(kMatrix_CallType); 262 return fCurMCState->fMatrixInfo->fMatrix.preScale(sx, sy); 263 } 264 265 bool rotate(SkScalar degrees) { 266 this->call(kMatrix_CallType); 267 return fCurMCState->fMatrixInfo->fMatrix.preRotate(degrees); 268 } 269 270 bool skew(SkScalar sx, SkScalar sy) { 271 this->call(kMatrix_CallType); 272 return fCurMCState->fMatrixInfo->fMatrix.preSkew(sx, sy); 273 } 274 275 bool concat(const SkMatrix& matrix) { 276 this->call(kMatrix_CallType); 277 return fCurMCState->fMatrixInfo->fMatrix.preConcat(matrix); 278 } 279 280 void setMatrix(const SkMatrix& matrix) { 281 this->call(kMatrix_CallType); 282 fCurMCState->fMatrixInfo->fMatrix = matrix; 283 } 284 285 bool clipRect(const SkRect& rect, SkRegion::Op op, bool doAA) { 286 this->call(SkMatrixClipStateMgr::kClip_CallType); 287 return fCurMCState->fClipInfo->clipRect(rect, op, doAA, 288 fCurMCState->fMatrixInfo->fMatrix); 289 } 290 291 bool clipRRect(const SkRRect& rrect, SkRegion::Op op, bool doAA) { 292 this->call(SkMatrixClipStateMgr::kClip_CallType); 293 return fCurMCState->fClipInfo->clipRRect(rrect, op, doAA, 294 fCurMCState->fMatrixInfo->fMatrix); 295 } 296 297 bool clipPath(const SkPath& path, SkRegion::Op op, bool doAA) { 298 this->call(SkMatrixClipStateMgr::kClip_CallType); 299 return fCurMCState->fClipInfo->clipPath(fPicRecord, path, op, doAA, 300 fCurMCState->fMatrixInfo->fMatrix); 301 } 302 303 bool clipRegion(const SkRegion& region, SkRegion::Op op) { 304 this->call(SkMatrixClipStateMgr::kClip_CallType); 305 return fCurMCState->fClipInfo->clipRegion(fPicRecord, region, op, 306 fCurMCState->fMatrixInfo->fMatrix); 307 } 308 309 bool call(CallType callType); 310 311 void fillInSkips(SkWriter32* writer, int32_t restoreOffset) { 312 // Since we write out the entire clip stack at each block start we 313 // need to update the skips for the entire stack each time too. 314 SkDeque::F2BIter iter(fMatrixClipStack); 315 316 for (const MatrixClipState* state = (const MatrixClipState*) iter.next(); 317 state != NULL; 318 state = (const MatrixClipState*) iter.next()) { 319 state->fClipInfo->fillInSkips(writer, restoreOffset); 320 } 321 } 322 323 void finish(); 324 325protected: 326 SkPictureRecord* fPicRecord; 327 328 uint32_t fMatrixClipStackStorage[43]; // sized to fit 2 clip states 329 SkDeque fMatrixClipStack; 330 MatrixClipState* fCurMCState; 331 332 // The MCStateID of the state currently in effect in the byte stream. 0 if none. 333 int32_t fCurOpenStateID; 334 335 SkDEBUGCODE(void validate();) 336 337 static void WriteDeltaMat(SkPictureRecord* picRecord, 338 const SkMatrix& current, 339 const SkMatrix& desired); 340 static int32_t NewMCStateID(); 341}; 342 343#endif