| OLD | NEW |
| (Empty) |
| 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: | |
| 60 void reset() { | |
| 61 fMatrixID = kIdentityMatID; | |
| 62 fMatrix.reset(); | |
| 63 } | |
| 64 | |
| 65 void preTranslate(SkScalar dx, SkScalar dy) { | |
| 66 fMatrixID = -1; | |
| 67 fMatrix.preTranslate(dx, dy); | |
| 68 } | |
| 69 | |
| 70 void preScale(SkScalar sx, SkScalar sy) { | |
| 71 fMatrixID = -1; | |
| 72 fMatrix.preScale(sx, sy); | |
| 73 } | |
| 74 | |
| 75 void preRotate(SkScalar degrees) { | |
| 76 fMatrixID = -1; | |
| 77 fMatrix.preRotate(degrees); | |
| 78 } | |
| 79 | |
| 80 void preSkew(SkScalar sx, SkScalar sy) { | |
| 81 fMatrixID = -1; | |
| 82 fMatrix.preSkew(sx, sy); | |
| 83 } | |
| 84 | |
| 85 void preConcat(const SkMatrix& matrix) { | |
| 86 fMatrixID = -1; | |
| 87 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 typedef SkNoncopyable INHERITED; | |
| 109 }; | |
| 110 | |
| 111 class ClipInfo : SkNoncopyable { | |
| 112 public: | |
| 113 ClipInfo() {} | |
| 114 | |
| 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 i
n the RRect | |
| 122 newClip->fOp = op; | |
| 123 newClip->fDoAA = doAA; | |
| 124 newClip->fMatrixID = matrixID; | |
| 125 return false; | |
| 126 } | |
| 127 | |
| 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 | |
| 184 MatrixClipState(MatrixClipState* prev) | |
| 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 fClipInf
oStorage | |
| 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 fMatrixInfoStorage = *prev->fMatrixInfo; | |
| 206 fMatrixInfo = &fMatrixInfoStorage; | |
| 207 | |
| 208 // We don't copy the ClipOps of the previous clip states | |
| 209 fClipInfo = &fClipInfoStorage; | |
| 210 | |
| 211 // Initially a new save/saveLayer represents the same MC state | |
| 212 // as its predecessor. | |
| 213 fMCStateID = prev->fMCStateID; | |
| 214 #ifdef SK_DEBUG | |
| 215 fExpectedDepth = prev->fExpectedDepth; | |
| 216 #endif | |
| 217 } | |
| 218 | |
| 219 fIsSaveLayer = false; | |
| 220 } | |
| 221 | |
| 222 MatrixInfo* fMatrixInfo; | |
| 223 MatrixInfo fMatrixInfoStorage; | |
| 224 | |
| 225 ClipInfo* fClipInfo; | |
| 226 ClipInfo fClipInfoStorage; | |
| 227 | |
| 228 // Tracks the current depth of saveLayers to support the isDrawingToLaye
r call | |
| 229 int fLayerID; | |
| 230 // Does this MC state represent a saveLayer call? | |
| 231 bool fIsSaveLayer; | |
| 232 | |
| 233 // The next field is only valid when fIsSaveLayer is set. | |
| 234 SkTDArray<int>* fSavedSkipOffsets; | |
| 235 | |
| 236 // Does the MC state have an open block in the skp? | |
| 237 bool fHasOpen; | |
| 238 | |
| 239 MatrixClipState* fPrev; | |
| 240 | |
| 241 #ifdef SK_DEBUG | |
| 242 int fExpectedDepth; // debugging aid | |
| 243 #endif | |
| 244 | |
| 245 int32_t fMCStateID; | |
| 246 }; | |
| 247 | |
| 248 enum CallType { | |
| 249 kMatrix_CallType, | |
| 250 kClip_CallType, | |
| 251 kOther_CallType | |
| 252 }; | |
| 253 | |
| 254 SkMatrixClipStateMgr(); | |
| 255 ~SkMatrixClipStateMgr(); | |
| 256 | |
| 257 void init(SkPictureRecord* picRecord) { | |
| 258 // Note: we're not taking a ref here. It is expected that the SkMatrixCl
ipStateMgr | |
| 259 // is owned by the SkPictureRecord object | |
| 260 fPicRecord = picRecord; | |
| 261 } | |
| 262 | |
| 263 SkPictureRecord* getPicRecord() { return fPicRecord; } | |
| 264 | |
| 265 // TODO: need to override canvas' getSaveCount. Right now we pass the | |
| 266 // save* and restore calls on to the base SkCanvas in SkPictureRecord but | |
| 267 // this duplicates effort. | |
| 268 int getSaveCount() const { return fMatrixClipStack.count(); } | |
| 269 | |
| 270 int save(); | |
| 271 | |
| 272 int saveLayer(const SkRect* bounds, const SkPaint* paint, SkCanvas::SaveFlag
s flags); | |
| 273 | |
| 274 bool isDrawingToLayer() const { | |
| 275 return fCurMCState->fLayerID > 0; | |
| 276 } | |
| 277 | |
| 278 void restore(); | |
| 279 | |
| 280 void translate(SkScalar dx, SkScalar dy) { | |
| 281 this->call(kMatrix_CallType); | |
| 282 fCurMCState->fMatrixInfo->preTranslate(dx, dy); | |
| 283 } | |
| 284 | |
| 285 void scale(SkScalar sx, SkScalar sy) { | |
| 286 this->call(kMatrix_CallType); | |
| 287 fCurMCState->fMatrixInfo->preScale(sx, sy); | |
| 288 } | |
| 289 | |
| 290 void rotate(SkScalar degrees) { | |
| 291 this->call(kMatrix_CallType); | |
| 292 fCurMCState->fMatrixInfo->preRotate(degrees); | |
| 293 } | |
| 294 | |
| 295 void skew(SkScalar sx, SkScalar sy) { | |
| 296 this->call(kMatrix_CallType); | |
| 297 fCurMCState->fMatrixInfo->preSkew(sx, sy); | |
| 298 } | |
| 299 | |
| 300 void concat(const SkMatrix& matrix) { | |
| 301 this->call(kMatrix_CallType); | |
| 302 fCurMCState->fMatrixInfo->preConcat(matrix); | |
| 303 } | |
| 304 | |
| 305 void setMatrix(const SkMatrix& matrix) { | |
| 306 this->call(kMatrix_CallType); | |
| 307 fCurMCState->fMatrixInfo->setMatrix(matrix); | |
| 308 } | |
| 309 | |
| 310 bool clipRect(const SkRect& rect, SkRegion::Op op, bool doAA) { | |
| 311 this->call(SkMatrixClipStateMgr::kClip_CallType); | |
| 312 return fCurMCState->fClipInfo->clipRect(rect, op, doAA, | |
| 313 fCurMCState->fMatrixInfo->getID(
this)); | |
| 314 } | |
| 315 | |
| 316 bool clipRRect(const SkRRect& rrect, SkRegion::Op op, bool doAA) { | |
| 317 this->call(SkMatrixClipStateMgr::kClip_CallType); | |
| 318 return fCurMCState->fClipInfo->clipRRect(rrect, op, doAA, | |
| 319 fCurMCState->fMatrixInfo->getID
(this)); | |
| 320 } | |
| 321 | |
| 322 bool clipPath(const SkPath& path, SkRegion::Op op, bool doAA) { | |
| 323 this->call(SkMatrixClipStateMgr::kClip_CallType); | |
| 324 return fCurMCState->fClipInfo->clipPath(fPicRecord, path, op, doAA, | |
| 325 fCurMCState->fMatrixInfo->getID(
this)); | |
| 326 } | |
| 327 | |
| 328 bool clipRegion(const SkRegion& region, SkRegion::Op op) { | |
| 329 this->call(SkMatrixClipStateMgr::kClip_CallType); | |
| 330 int regionID = this->addRegionToDict(region); | |
| 331 return fCurMCState->fClipInfo->clipRegion(fPicRecord, regionID, op, | |
| 332 fCurMCState->fMatrixInfo->getI
D(this)); | |
| 333 } | |
| 334 | |
| 335 bool call(CallType callType); | |
| 336 | |
| 337 void fillInSkips(SkWriter32* writer, int32_t restoreOffset); | |
| 338 | |
| 339 void finish(); | |
| 340 | |
| 341 protected: | |
| 342 SkPictureRecord* fPicRecord; | |
| 343 | |
| 344 uint32_t fMatrixClipStackStorage[43]; // sized to fit 2 clip states | |
| 345 SkDeque fMatrixClipStack; | |
| 346 MatrixClipState* fCurMCState; | |
| 347 | |
| 348 // This dictionary doesn't actually de-duplicate the matrices (except for th
e | |
| 349 // identity matrix). It merely stores the matrices and allows them to be loo
ked | |
| 350 // up by ID later. The de-duplication mainly falls upon the matrix/clip stac
k | |
| 351 // which stores the ID so a revisited clip/matrix (via popping the stack) wi
ll | |
| 352 // use the same ID. | |
| 353 SkTDArray<SkMatrix> fMatrixDict; | |
| 354 | |
| 355 SkTDArray<SkRegion*> fRegionDict; | |
| 356 | |
| 357 // The MCStateID of the state currently in effect in the byte stream. 0 if n
one. | |
| 358 int32_t fCurOpenStateID; | |
| 359 // The skip offsets for the current open state. These are the locations in t
he | |
| 360 // skp that must be filled in when the current open state is closed. These a
re | |
| 361 // here rather then distributed across the MatrixClipState's because saveLay
ers | |
| 362 // can cause MC states to be nested. | |
| 363 SkTDArray<int32_t> *fSkipOffsets; // TODO: should we store u32 or size_t i
nstead? | |
| 364 | |
| 365 SkDEBUGCODE(void validate();) | |
| 366 | |
| 367 int MCStackPush(); | |
| 368 | |
| 369 void addClipOffset(size_t offset) { | |
| 370 SkASSERT(NULL != fSkipOffsets); | |
| 371 SkASSERT(kIdentityWideOpenStateID != fCurOpenStateID); | |
| 372 SkASSERT(fCurMCState->fHasOpen); | |
| 373 SkASSERT(!fCurMCState->fIsSaveLayer); | |
| 374 | |
| 375 *fSkipOffsets->append() = SkToS32(offset); | |
| 376 } | |
| 377 | |
| 378 void writeDeltaMat(int currentMatID, int desiredMatID); | |
| 379 static int32_t NewMCStateID(); | |
| 380 | |
| 381 int addRegionToDict(const SkRegion& region); | |
| 382 const SkRegion* lookupRegion(int index) { | |
| 383 SkASSERT(index >= 0 && index < fRegionDict.count()); | |
| 384 return fRegionDict[index]; | |
| 385 } | |
| 386 | |
| 387 // TODO: add stats to check if the dictionary really does | |
| 388 // reduce the size of the SkPicture. | |
| 389 int addMatToDict(const SkMatrix& mat); | |
| 390 const SkMatrix& lookupMat(int index) { | |
| 391 SkASSERT(index >= 0 && index < fMatrixDict.count()); | |
| 392 return fMatrixDict[index]; | |
| 393 } | |
| 394 | |
| 395 bool isNestingMCState(int stateID); | |
| 396 | |
| 397 #ifdef SK_DEBUG | |
| 398 int fActualDepth; | |
| 399 #endif | |
| 400 | |
| 401 // save layers are nested within a specific MC state. This stack tracks | |
| 402 // the nesting MC state's ID as save layers are pushed and popped. | |
| 403 SkTDArray<int> fStateIDStack; | |
| 404 }; | |
| 405 | |
| 406 #endif | |
| OLD | NEW |