'<(skia_src_path)/core/SkMaskGamma.h',
'<(skia_src_path)/core/SkMath.cpp',
'<(skia_src_path)/core/SkMatrix.cpp',
+ '<(skia_src_path)/core/SkMatrixClipStateMgr.cpp',
+ '<(skia_src_path)/core/SkMatrixClipStateMgr.h',
'<(skia_src_path)/core/SkMessageBus.h',
'<(skia_src_path)/core/SkMetaData.cpp',
'<(skia_src_path)/core/SkMipMap.cpp',
--- /dev/null
+/*
+ * Copyright 2014 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkMatrixClipStateMgr.h"
+#include "SkPictureRecord.h"
+
+bool SkMatrixClipStateMgr::MatrixClipState::ClipInfo::clipPath(SkPictureRecord* picRecord,
+ const SkPath& path,
+ SkRegion::Op op,
+ bool doAA,
+ const SkMatrix& matrix) {
+ int pathID = picRecord->addPathToHeap(path);
+
+ ClipOp& newClip = fClips.push_back();
+ newClip.fClipType = kPath_ClipType;
+ newClip.fGeom.fPathID = pathID;
+ newClip.fOp = op;
+ newClip.fDoAA = doAA;
+ newClip.fMatrix = matrix;
+ newClip.fOffset = kInvalidJumpOffset;
+ return false;
+}
+
+bool SkMatrixClipStateMgr::MatrixClipState::ClipInfo::clipRegion(SkPictureRecord* picRecord,
+ const SkRegion& region,
+ SkRegion::Op op,
+ const SkMatrix& matrix) {
+ // TODO: add a region dictionary so we don't have to copy the region in here
+ ClipOp& newClip = fClips.push_back();
+ newClip.fClipType = kRegion_ClipType;
+ newClip.fGeom.fRegion = SkNEW(SkRegion(region));
+ newClip.fOp = op;
+ newClip.fDoAA = true; // not necessary but sanity preserving
+ newClip.fMatrix = matrix;
+ newClip.fOffset = kInvalidJumpOffset;
+ return false;
+}
+
+void SkMatrixClipStateMgr::WriteDeltaMat(SkPictureRecord* picRecord,
+ const SkMatrix& current,
+ const SkMatrix& desired) {
+ SkMatrix delta;
+ current.invert(&delta);
+ delta.preConcat(desired);
+ picRecord->recordConcat(delta);
+}
+
+// Note: this only writes out the clips for the current save state. To get the
+// entire clip stack requires iterating of the entire matrix/clip stack.
+void SkMatrixClipStateMgr::MatrixClipState::ClipInfo::writeClip(SkMatrix* curMat,
+ SkPictureRecord* picRecord,
+ bool* overrideFirstOp) {
+ for (int i = 0; i < fClips.count(); ++i) {
+ ClipOp& curClip = fClips[i];
+
+ SkRegion::Op op = curClip.fOp;
+ if (*overrideFirstOp) {
+ op = SkRegion::kReplace_Op;
+ *overrideFirstOp = false;
+ }
+
+ // TODO: re-add an internal matrix dictionary to not write out
+ // redundant matrices.
+ // TODO: right now we're writing out the delta matrix from the prior
+ // matrix state. This is a side-effect of writing out the entire
+ // clip stack and should be resolved when that is fixed.
+ SkMatrixClipStateMgr::WriteDeltaMat(picRecord, *curMat, curClip.fMatrix);
+ *curMat = curClip.fMatrix;
+
+ switch (curClip.fClipType) {
+ case kRect_ClipType:
+ curClip.fOffset = picRecord->recordClipRect(curClip.fGeom.fRRect.rect(),
+ op, curClip.fDoAA);
+ break;
+ case kRRect_ClipType:
+ curClip.fOffset = picRecord->recordClipRRect(curClip.fGeom.fRRect, op,
+ curClip.fDoAA);
+ break;
+ case kPath_ClipType:
+ curClip.fOffset = picRecord->recordClipPath(curClip.fGeom.fPathID, op,
+ curClip.fDoAA);
+ break;
+ case kRegion_ClipType:
+ curClip.fOffset = picRecord->recordClipRegion(*curClip.fGeom.fRegion, op);
+ break;
+ default:
+ SkASSERT(0);
+ }
+ }
+}
+
+// Fill in the skip offsets for all the clips written in the current block
+void SkMatrixClipStateMgr::MatrixClipState::ClipInfo::fillInSkips(SkWriter32* writer,
+ int32_t restoreOffset) {
+ for (int i = 0; i < fClips.count(); ++i) {
+ ClipOp& curClip = fClips[i];
+
+ if (-1 == curClip.fOffset) {
+ continue;
+ }
+ SkDEBUGCODE(uint32_t peek = writer->read32At(curClip.fOffset);)
+ SkASSERT(-1 == peek);
+ writer->write32At(curClip.fOffset, restoreOffset);
+ SkDEBUGCODE(curClip.fOffset = -1;)
+ }
+}
+
+SkMatrixClipStateMgr::SkMatrixClipStateMgr()
+ : fPicRecord(NULL)
+ , fCurOpenStateID(kIdentityWideOpenStateID)
+ , fMatrixClipStack(sizeof(MatrixClipState),
+ fMatrixClipStackStorage,
+ sizeof(fMatrixClipStackStorage)) {
+ fCurMCState = (MatrixClipState*)fMatrixClipStack.push_back();
+ new (fCurMCState) MatrixClipState(NULL, 0); // balanced in restore()
+}
+
+
+int SkMatrixClipStateMgr::save(SkCanvas::SaveFlags flags) {
+ SkDEBUGCODE(this->validate();)
+
+ MatrixClipState* newTop = (MatrixClipState*)fMatrixClipStack.push_back();
+ new (newTop) MatrixClipState(fCurMCState, flags); // balanced in restore()
+ fCurMCState = newTop;
+
+ SkDEBUGCODE(this->validate();)
+
+ return fMatrixClipStack.count();
+}
+
+int SkMatrixClipStateMgr::saveLayer(const SkRect* bounds, const SkPaint* paint,
+ SkCanvas::SaveFlags flags) {
+ int result = this->save(flags);
+ ++fCurMCState->fLayerID;
+ fCurMCState->fIsSaveLayer = true;
+
+ fCurMCState->fSaveLayerBracketed = this->call(kOther_CallType);
+ fCurMCState->fSaveLayerBaseStateID = fCurOpenStateID;
+ fPicRecord->recordSaveLayer(bounds, paint,
+ (SkCanvas::SaveFlags)(flags| SkCanvas::kMatrixClip_SaveFlag));
+ return result;
+}
+
+void SkMatrixClipStateMgr::restore() {
+ SkDEBUGCODE(this->validate();)
+
+ if (fCurMCState->fIsSaveLayer) {
+ if (fCurMCState->fSaveLayerBaseStateID != fCurOpenStateID) {
+ fPicRecord->recordRestore(); // Close the open block
+ }
+ // The saveLayer's don't carry any matrix or clip state in the
+ // new scheme so make sure the saveLayer's recordRestore doesn't
+ // try to finalize them (i.e., fill in their skip offsets).
+ fPicRecord->recordRestore(false); // close of saveLayer
+
+ // Close the Save that brackets the saveLayer. TODO: this doesn't handle
+ // the skip offsets correctly
+ if (fCurMCState->fSaveLayerBracketed) {
+ fPicRecord->recordRestore(false);
+ }
+
+ // MC states can be allowed to fuse across saveLayer/restore boundaries
+ fCurOpenStateID = kIdentityWideOpenStateID;
+ }
+
+ fCurMCState->~MatrixClipState(); // balanced in save()
+ fMatrixClipStack.pop_back();
+ fCurMCState = (MatrixClipState*)fMatrixClipStack.back();
+
+ SkDEBUGCODE(this->validate();)
+}
+
+// kIdentityWideOpenStateID (0) is reserved for the identity/wide-open clip state
+int32_t SkMatrixClipStateMgr::NewMCStateID() {
+ // TODO: guard against wrap around
+ // TODO: make uint32_t
+ static int32_t gMCStateID = kIdentityWideOpenStateID;
+ ++gMCStateID;
+ return gMCStateID;
+}
+
+bool SkMatrixClipStateMgr::call(CallType callType) {
+ SkDEBUGCODE(this->validate();)
+
+ if (kMatrix_CallType == callType || kClip_CallType == callType) {
+ fCurMCState->fMCStateID = NewMCStateID();
+ SkDEBUGCODE(this->validate();)
+ return false;
+ }
+
+ SkASSERT(kOther_CallType == callType);
+
+ if (fCurMCState->fMCStateID == fCurOpenStateID) {
+ // Required MC state is already active one - nothing to do
+ SkDEBUGCODE(this->validate();)
+ return false;
+ }
+
+ if (kIdentityWideOpenStateID != fCurOpenStateID) {
+ fPicRecord->recordRestore(); // Close the open block
+ }
+
+ // Install the required MC state as the active one
+ fCurOpenStateID = fCurMCState->fMCStateID;
+
+ fPicRecord->recordSave(SkCanvas::kMatrixClip_SaveFlag);
+
+ // write out clips
+ SkDeque::F2BIter iter(fMatrixClipStack);
+ bool firstClip = true;
+
+ SkMatrix curMat = SkMatrix::I();
+ for (const MatrixClipState* state = (const MatrixClipState*) iter.next();
+ state != NULL;
+ state = (const MatrixClipState*) iter.next()) {
+ state->fClipInfo->writeClip(&curMat, fPicRecord, &firstClip);
+ }
+
+ // write out matrix
+ if (!fCurMCState->fMatrixInfo->fMatrix.isIdentity()) {
+ // TODO: writing out the delta matrix here is an artifact of the writing
+ // out of the entire clip stack (with its matrices). Ultimately we will
+ // write out the CTM here when the clip state is collapsed to a single path.
+ WriteDeltaMat(fPicRecord, curMat, fCurMCState->fMatrixInfo->fMatrix);
+ }
+
+ SkDEBUGCODE(this->validate();)
+
+ return true;
+}
+
+void SkMatrixClipStateMgr::finish() {
+ if (kIdentityWideOpenStateID != fCurOpenStateID) {
+ fPicRecord->recordRestore(); // Close the open block
+ fCurOpenStateID = kIdentityWideOpenStateID;
+ }
+}
+
+#ifdef SK_DEBUG
+void SkMatrixClipStateMgr::validate() {
+ if (fCurOpenStateID == fCurMCState->fMCStateID) {
+ // The current state is the active one so all its skip offsets should
+ // still be -1
+ SkDeque::F2BIter iter(fMatrixClipStack);
+
+ for (const MatrixClipState* state = (const MatrixClipState*) iter.next();
+ state != NULL;
+ state = (const MatrixClipState*) iter.next()) {
+ state->fClipInfo->checkOffsetNotEqual(-1);
+ }
+ }
+}
+#endif
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright 2014 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#ifndef SkMatrixClipStateMgr_DEFINED
+#define SkMatrixClipStateMgr_DEFINED
+
+#include "SkCanvas.h"
+#include "SkMatrix.h"
+#include "SkRegion.h"
+#include "SkRRect.h"
+#include "SkTypes.h"
+#include "SkTArray.h"
+
+class SkPictureRecord;
+class SkWriter32;
+
+// The SkMatrixClipStateMgr collapses the matrix/clip state of an SkPicture into
+// a series of save/restore blocks of consistent matrix clip state, e.g.:
+//
+// save
+// clip(s)
+// concat
+// ... draw ops ...
+// restore
+//
+// SaveLayers simply add another level, e.g.:
+//
+// save
+// clip(s)
+// concat
+// ... draw ops ...
+// saveLayer
+// save
+// clip(s)
+// concat
+// ... draw ops ...
+// restore
+// restore
+// restore
+//
+// As a side effect of this process all saves and saveLayers will become
+// kMatrixClip_SaveFlag style saves/saveLayers.
+
+// The SkMatrixClipStateMgr works by intercepting all the save*, restore, clip*,
+// and matrix calls sent to SkCanvas in order to track the current matrix/clip
+// state. All the other canvas calls get funnelled into a generic "call" entry
+// point that signals that a state block is required.
+class SkMatrixClipStateMgr {
+public:
+ static const int32_t kIdentityWideOpenStateID = 0;
+
+ class MatrixClipState {
+ public:
+ class MatrixInfo {
+ public:
+ SkMatrix fMatrix;
+ // TODO: add an internal dictionary and an ID here
+ };
+
+ class ClipInfo : public SkNoncopyable {
+ public:
+ ClipInfo() {}
+
+ bool clipRect(const SkRect& rect,
+ SkRegion::Op op,
+ bool doAA,
+ const SkMatrix& matrix) {
+ ClipOp& newClip = fClips.push_back();
+ newClip.fClipType = kRect_ClipType;
+ newClip.fGeom.fRRect.setRect(rect); // storing the clipRect in the RRect
+ newClip.fOp = op;
+ newClip.fDoAA = doAA;
+ newClip.fMatrix = matrix;
+ newClip.fOffset = kInvalidJumpOffset;
+ return false;
+ }
+
+ bool clipRRect(const SkRRect& rrect,
+ SkRegion::Op op,
+ bool doAA,
+ const SkMatrix& matrix) {
+ ClipOp& newClip = fClips.push_back();
+ newClip.fClipType = kRRect_ClipType;
+ newClip.fGeom.fRRect = rrect;
+ newClip.fOp = op;
+ newClip.fDoAA = doAA;
+ newClip.fMatrix = matrix;
+ newClip.fOffset = kInvalidJumpOffset;
+ return false;
+ }
+
+ bool clipPath(SkPictureRecord* picRecord,
+ const SkPath& path,
+ SkRegion::Op op,
+ bool doAA,
+ const SkMatrix& matrix);
+ bool clipRegion(SkPictureRecord* picRecord,
+ const SkRegion& region,
+ SkRegion::Op op,
+ const SkMatrix& matrix);
+ void writeClip(SkMatrix* curMat,
+ SkPictureRecord* picRecord,
+ bool* overrideFirstOp);
+ void fillInSkips(SkWriter32* writer, int32_t restoreOffset);
+
+#ifdef SK_DEBUG
+ void checkOffsetNotEqual(int32_t offset) {
+ for (int i = 0; i < fClips.count(); ++i) {
+ ClipOp& curClip = fClips[i];
+ SkASSERT(offset != curClip.fOffset);
+ }
+ }
+#endif
+ private:
+ enum ClipType {
+ kRect_ClipType,
+ kRRect_ClipType,
+ kPath_ClipType,
+ kRegion_ClipType
+ };
+
+ static const int kInvalidJumpOffset = -1;
+
+ class ClipOp {
+ public:
+ ClipOp() {}
+ ~ClipOp() {
+ if (kRegion_ClipType == fClipType) {
+ SkDELETE(fGeom.fRegion);
+ }
+ }
+
+ ClipType fClipType;
+
+ union {
+ SkRRect fRRect; // also stores clipRect
+ int fPathID;
+ // TODO: add internal dictionary of regions
+ // This parameter forces us to have a dtor and thus use
+ // SkTArray rather then SkTDArray!
+ const SkRegion* fRegion;
+ } fGeom;
+
+ bool fDoAA;
+ SkRegion::Op fOp;
+
+ // The CTM in effect when this clip call was issued
+ // TODO: add an internal dictionary and replace with ID
+ SkMatrix fMatrix;
+
+ // The offset of this clipOp's "jump-to-offset" location in the skp.
+ // -1 means the offset hasn't been written.
+ int32_t fOffset;
+ };
+
+ SkTArray<ClipOp> fClips;
+
+ typedef SkNoncopyable INHERITED;
+ };
+
+ MatrixClipState(MatrixClipState* prev, int flags)
+#ifdef SK_DEBUG
+ : fPrev(prev)
+#endif
+ {
+ if (NULL == prev) {
+ fLayerID = 0;
+
+ fMatrixInfoStorage.fMatrix.reset();
+ fMatrixInfo = &fMatrixInfoStorage;
+ fClipInfo = &fClipInfoStorage; // ctor handles init of fClipInfoStorage
+
+ // The identity/wide-open-clip state is current by default
+ fMCStateID = kIdentityWideOpenStateID;
+ }
+ else {
+ fLayerID = prev->fLayerID;
+
+ if (flags & SkCanvas::kMatrix_SaveFlag) {
+ fMatrixInfoStorage = *prev->fMatrixInfo;
+ fMatrixInfo = &fMatrixInfoStorage;
+ } else {
+ fMatrixInfo = prev->fMatrixInfo;
+ }
+
+ if (flags & SkCanvas::kClip_SaveFlag) {
+ // We don't copy the ClipOps of the previous clip states
+ fClipInfo = &fClipInfoStorage;
+ } else {
+ fClipInfo = prev->fClipInfo;
+ }
+
+ // Initially a new save/saveLayer represents the same MC state
+ // as its predecessor.
+ fMCStateID = prev->fMCStateID;
+ }
+
+ fIsSaveLayer = false;
+ }
+
+ MatrixInfo* fMatrixInfo;
+ MatrixInfo fMatrixInfoStorage;
+
+ ClipInfo* fClipInfo;
+ ClipInfo fClipInfoStorage;
+
+ // Tracks the current depth of saveLayers to support the isDrawingToLayer call
+ int fLayerID;
+ // Does this MC state represent a saveLayer call?
+ bool fIsSaveLayer;
+
+ // The next two fields are only valid when fIsSaveLayer is set.
+ int32_t fSaveLayerBaseStateID;
+ bool fSaveLayerBracketed;
+
+#ifdef SK_DEBUG
+ MatrixClipState* fPrev; // debugging aid
+#endif
+
+ int32_t fMCStateID;
+ };
+
+ enum CallType {
+ kMatrix_CallType,
+ kClip_CallType,
+ kOther_CallType
+ };
+
+ SkMatrixClipStateMgr();
+
+ void init(SkPictureRecord* picRecord) {
+ // Note: we're not taking a ref here. It is expected that the SkMatrixClipStateMgr
+ // is owned by the SkPictureRecord object
+ fPicRecord = picRecord;
+ }
+
+ // TODO: need to override canvas' getSaveCount. Right now we pass the
+ // save* and restore calls on to the base SkCanvas in SkPictureRecord but
+ // this duplicates effort.
+ int getSaveCount() const { return fMatrixClipStack.count(); }
+
+ int save(SkCanvas::SaveFlags flags);
+
+ int saveLayer(const SkRect* bounds, const SkPaint* paint, SkCanvas::SaveFlags flags);
+
+ bool isDrawingToLayer() const {
+ return fCurMCState->fLayerID > 0;
+ }
+
+ void restore();
+
+ bool translate(SkScalar dx, SkScalar dy) {
+ this->call(kMatrix_CallType);
+ return fCurMCState->fMatrixInfo->fMatrix.preTranslate(dx, dy);
+ }
+
+ bool scale(SkScalar sx, SkScalar sy) {
+ this->call(kMatrix_CallType);
+ return fCurMCState->fMatrixInfo->fMatrix.preScale(sx, sy);
+ }
+
+ bool rotate(SkScalar degrees) {
+ this->call(kMatrix_CallType);
+ return fCurMCState->fMatrixInfo->fMatrix.preRotate(degrees);
+ }
+
+ bool skew(SkScalar sx, SkScalar sy) {
+ this->call(kMatrix_CallType);
+ return fCurMCState->fMatrixInfo->fMatrix.preSkew(sx, sy);
+ }
+
+ bool concat(const SkMatrix& matrix) {
+ this->call(kMatrix_CallType);
+ return fCurMCState->fMatrixInfo->fMatrix.preConcat(matrix);
+ }
+
+ void setMatrix(const SkMatrix& matrix) {
+ this->call(kMatrix_CallType);
+ fCurMCState->fMatrixInfo->fMatrix = matrix;
+ }
+
+ bool clipRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
+ this->call(SkMatrixClipStateMgr::kClip_CallType);
+ return fCurMCState->fClipInfo->clipRect(rect, op, doAA,
+ fCurMCState->fMatrixInfo->fMatrix);
+ }
+
+ bool clipRRect(const SkRRect& rrect, SkRegion::Op op, bool doAA) {
+ this->call(SkMatrixClipStateMgr::kClip_CallType);
+ return fCurMCState->fClipInfo->clipRRect(rrect, op, doAA,
+ fCurMCState->fMatrixInfo->fMatrix);
+ }
+
+ bool clipPath(const SkPath& path, SkRegion::Op op, bool doAA) {
+ this->call(SkMatrixClipStateMgr::kClip_CallType);
+ return fCurMCState->fClipInfo->clipPath(fPicRecord, path, op, doAA,
+ fCurMCState->fMatrixInfo->fMatrix);
+ }
+
+ bool clipRegion(const SkRegion& region, SkRegion::Op op) {
+ this->call(SkMatrixClipStateMgr::kClip_CallType);
+ return fCurMCState->fClipInfo->clipRegion(fPicRecord, region, op,
+ fCurMCState->fMatrixInfo->fMatrix);
+ }
+
+ bool call(CallType callType);
+
+ void fillInSkips(SkWriter32* writer, int32_t restoreOffset) {
+ // Since we write out the entire clip stack at each block start we
+ // need to update the skips for the entire stack each time too.
+ SkDeque::F2BIter iter(fMatrixClipStack);
+
+ for (const MatrixClipState* state = (const MatrixClipState*) iter.next();
+ state != NULL;
+ state = (const MatrixClipState*) iter.next()) {
+ state->fClipInfo->fillInSkips(writer, restoreOffset);
+ }
+ }
+
+ void finish();
+
+protected:
+ SkPictureRecord* fPicRecord;
+
+ uint32_t fMatrixClipStackStorage[43]; // sized to fit 2 clip states
+ SkDeque fMatrixClipStack;
+ MatrixClipState* fCurMCState;
+
+ // The MCStateID of the state currently in effect in the byte stream. 0 if none.
+ int32_t fCurOpenStateID;
+
+ SkDEBUGCODE(void validate();)
+
+ static void WriteDeltaMat(SkPictureRecord* picRecord,
+ const SkMatrix& current,
+ const SkMatrix& desired);
+ static int32_t NewMCStateID();
+};
+
+#endif
\ No newline at end of file
fBitmapHeap = SkNEW(SkBitmapHeap);
fFlattenableHeap.setBitmapStorage(fBitmapHeap);
fPathHeap = NULL; // lazy allocate
+#ifndef SK_COLLAPSE_MATRIX_CLIP_STATE
fFirstSavedLayerIndex = kNoSavedLayerIndex;
+#endif
fInitialSaveCount = kNoInitialSave;
+
+#ifdef SK_COLLAPSE_MATRIX_CLIP_STATE
+ fMCMgr.init(this);
+#endif
}
SkPictureRecord::~SkPictureRecord() {
}
int SkPictureRecord::save(SaveFlags flags) {
+
+#ifdef SK_COLLAPSE_MATRIX_CLIP_STATE
+ fMCMgr.save(flags);
+#else
// record the offset to us, making it non-positive to distinguish a save
// from a clip entry.
fRestoreOffsetStack.push(-(int32_t)fWriter.bytesWritten());
this->recordSave(flags);
+#endif
return this->INHERITED::save(flags);
}
int SkPictureRecord::saveLayer(const SkRect* bounds, const SkPaint* paint,
SaveFlags flags) {
+
+ int count;
+#ifdef SK_COLLAPSE_MATRIX_CLIP_STATE
+ count = fMCMgr.saveLayer(bounds, paint, flags);
+#else
// record the offset to us, making it non-positive to distinguish a save
// from a clip entry.
fRestoreOffsetStack.push(-(int32_t)fWriter.bytesWritten());
if (kNoSavedLayerIndex == fFirstSavedLayerIndex) {
fFirstSavedLayerIndex = fRestoreOffsetStack.count();
}
+#endif
/* Don't actually call INHERITED::saveLayer, because that will try to allocate
an offscreen device (potentially very big) which we don't actually need
clip starts out the size of the picture, which is often much larger
than the size of the actual device we'll use during playback).
*/
- int count = this->INHERITED::save(flags);
+ count = this->INHERITED::save(flags);
this->clipRectBounds(bounds, flags, NULL);
return count;
}
void SkPictureRecord::recordSaveLayer(const SkRect* bounds, const SkPaint* paint,
- SaveFlags flags) {
+ SaveFlags flags) {
// op + bool for 'bounds'
uint32_t size = 2 * kUInt32Size;
if (NULL != bounds) {
}
bool SkPictureRecord::isDrawingToLayer() const {
+#ifdef SK_COLLAPSE_MATRIX_CLIP_STATE
+ return fMCMgr.isDrawingToLayer();
+#else
return fFirstSavedLayerIndex != kNoSavedLayerIndex;
+#endif
}
/*
SkASSERT(fRestoreOffsetStack.count() > 1);
#endif
+#ifdef SK_COLLAPSE_MATRIX_CLIP_STATE
+ if (fMCMgr.getSaveCount() == 1) {
+ return;
+ }
+
+ // TODO: don't write the restore to the op stream for normal saves
+ fMCMgr.restore();
+#else
// check for underflow
if (fRestoreOffsetStack.count() == 0) {
return;
}
fRestoreOffsetStack.pop();
+#endif
return this->INHERITED::restore();
}
-void SkPictureRecord::recordRestore() {
+void SkPictureRecord::recordRestore(bool fillInSkips) {
uint32_t initialOffset, size;
- this->fillRestoreOffsetPlaceholdersForCurrentStackLevel((uint32_t)fWriter.bytesWritten());
+ if (fillInSkips) {
+ this->fillRestoreOffsetPlaceholdersForCurrentStackLevel((uint32_t)fWriter.bytesWritten());
+ }
size = 1 * kUInt32Size; // RESTORE consists solely of 1 op code
initialOffset = this->addDraw(RESTORE, &size);
this->validate(initialOffset, size);
}
bool SkPictureRecord::translate(SkScalar dx, SkScalar dy) {
+#ifdef SK_COLLAPSE_MATRIX_CLIP_STATE
+ fMCMgr.translate(dx, dy);
+#else
// op + dx + dy
uint32_t size = 1 * kUInt32Size + 2 * sizeof(SkScalar);
size_t initialOffset = this->addDraw(TRANSLATE, &size);
this->addScalar(dx);
this->addScalar(dy);
this->validate(initialOffset, size);
+#endif
return this->INHERITED::translate(dx, dy);
}
bool SkPictureRecord::scale(SkScalar sx, SkScalar sy) {
+
+#ifdef SK_COLLAPSE_MATRIX_CLIP_STATE
+ fMCMgr.scale(sx, sy);
+#else
// op + sx + sy
uint32_t size = 1 * kUInt32Size + 2 * sizeof(SkScalar);
size_t initialOffset = this->addDraw(SCALE, &size);
this->addScalar(sx);
this->addScalar(sy);
this->validate(initialOffset, size);
+#endif
return this->INHERITED::scale(sx, sy);
}
bool SkPictureRecord::rotate(SkScalar degrees) {
+
+#ifdef SK_COLLAPSE_MATRIX_CLIP_STATE
+ fMCMgr.rotate(degrees);
+#else
// op + degrees
uint32_t size = 1 * kUInt32Size + sizeof(SkScalar);
size_t initialOffset = this->addDraw(ROTATE, &size);
this->addScalar(degrees);
this->validate(initialOffset, size);
+#endif
return this->INHERITED::rotate(degrees);
}
bool SkPictureRecord::skew(SkScalar sx, SkScalar sy) {
+
+#ifdef SK_COLLAPSE_MATRIX_CLIP_STATE
+ fMCMgr.skew(sx, sy);
+#else
// op + sx + sy
uint32_t size = 1 * kUInt32Size + 2 * sizeof(SkScalar);
size_t initialOffset = this->addDraw(SKEW, &size);
this->addScalar(sx);
this->addScalar(sy);
this->validate(initialOffset, size);
+#endif
return this->INHERITED::skew(sx, sy);
}
bool SkPictureRecord::concat(const SkMatrix& matrix) {
+
+#ifdef SK_COLLAPSE_MATRIX_CLIP_STATE
+ fMCMgr.concat(matrix);
+#else
this->recordConcat(matrix);
+#endif
return this->INHERITED::concat(matrix);
}
}
void SkPictureRecord::setMatrix(const SkMatrix& matrix) {
+
+#ifdef SK_COLLAPSE_MATRIX_CLIP_STATE
+ fMCMgr.setMatrix(matrix);
+#else
this->validate(fWriter.bytesWritten(), 0);
// op + matrix
uint32_t size = kUInt32Size + matrix.writeToMemory(NULL);
size_t initialOffset = this->addDraw(SET_MATRIX, &size);
this->addMatrix(matrix);
this->validate(initialOffset, size);
+#endif
this->INHERITED::setMatrix(matrix);
}
}
}
+#ifdef SK_COLLAPSE_MATRIX_CLIP_STATE
+void SkPictureRecord::fillRestoreOffsetPlaceholdersForCurrentStackLevel(uint32_t restoreOffset) {
+ fMCMgr.fillInSkips(&fWriter, restoreOffset);
+}
+#else
void SkPictureRecord::fillRestoreOffsetPlaceholdersForCurrentStackLevel(uint32_t restoreOffset) {
int32_t offset = fRestoreOffsetStack.top();
while (offset > 0) {
SkASSERT(SAVE == drawOp || SAVE_LAYER == drawOp);
#endif
}
+#endif
void SkPictureRecord::beginRecording() {
// we have to call this *after* our constructor, to ensure that it gets
void SkPictureRecord::endRecording() {
SkASSERT(kNoInitialSave != fInitialSaveCount);
this->restoreToCount(fInitialSaveCount);
+#ifdef SK_COLLAPSE_MATRIX_CLIP_STATE
+ fMCMgr.finish();
+#endif
}
+#ifdef SK_COLLAPSE_MATRIX_CLIP_STATE
+int SkPictureRecord::recordRestoreOffsetPlaceholder(SkRegion::Op op) {
+ size_t offset = fWriter.bytesWritten();
+ this->addInt(-1);
+ return offset;
+}
+#else
int SkPictureRecord::recordRestoreOffsetPlaceholder(SkRegion::Op op) {
if (fRestoreOffsetStack.isEmpty()) {
return -1;
fRestoreOffsetStack.top() = offset;
return offset;
}
+#endif
bool SkPictureRecord::clipRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
+
+#ifdef SK_COLLAPSE_MATRIX_CLIP_STATE
+ fMCMgr.clipRect(rect, op, doAA);
+#else
this->recordClipRect(rect, op, doAA);
+#endif
return this->INHERITED::clipRect(rect, op, doAA);
}
int SkPictureRecord::recordClipRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
-
// id + rect + clip params
uint32_t size = 1 * kUInt32Size + sizeof(rect) + 1 * kUInt32Size;
+#ifdef SK_COLLAPSE_MATRIX_CLIP_STATE
+ size += kUInt32Size; // + restore offset
+#else
// recordRestoreOffsetPlaceholder doesn't always write an offset
if (!fRestoreOffsetStack.isEmpty()) {
// + restore offset
size += kUInt32Size;
}
-
+#endif
size_t initialOffset = this->addDraw(CLIP_RECT, &size);
this->addRect(rect);
this->addInt(ClipParams_pack(op, doAA));
return this->SkPictureRecord::clipRect(rrect.getBounds(), op, doAA);
}
+#ifdef SK_COLLAPSE_MATRIX_CLIP_STATE
+ fMCMgr.clipRRect(rrect, op, doAA);
+#else
this->recordClipRRect(rrect, op, doAA);
+#endif
if (fRecordFlags & SkPicture::kUsePathBoundsForClip_RecordingFlag) {
return this->updateClipConservativelyUsingBounds(rrect.getBounds(), op, false);
} else {
}
int SkPictureRecord::recordClipRRect(const SkRRect& rrect, SkRegion::Op op, bool doAA) {
-
// op + rrect + clip params
uint32_t size = 1 * kUInt32Size + SkRRect::kSizeInMemory + 1 * kUInt32Size;
+#ifdef SK_COLLAPSE_MATRIX_CLIP_STATE
+ size += kUInt32Size; // + restore offset
+#else
// recordRestoreOffsetPlaceholder doesn't always write an offset
if (!fRestoreOffsetStack.isEmpty()) {
// + restore offset
size += kUInt32Size;
}
+#endif
size_t initialOffset = this->addDraw(CLIP_RRECT, &size);
this->addRRect(rrect);
this->addInt(ClipParams_pack(op, doAA));
int offset = recordRestoreOffsetPlaceholder(op);
-
this->validate(initialOffset, size);
return offset;
}
return this->clipRect(r, op, doAA);
}
+#ifdef SK_COLLAPSE_MATRIX_CLIP_STATE
+ fMCMgr.clipPath(path, op, doAA);
+#else
int pathID = this->addPathToHeap(path);
this->recordClipPath(pathID, op, doAA);
+#endif
if (fRecordFlags & SkPicture::kUsePathBoundsForClip_RecordingFlag) {
return this->updateClipConservativelyUsingBounds(path.getBounds(), op,
}
int SkPictureRecord::recordClipPath(int pathID, SkRegion::Op op, bool doAA) {
-
// op + path index + clip params
uint32_t size = 3 * kUInt32Size;
+#ifdef SK_COLLAPSE_MATRIX_CLIP_STATE
+ size += kUInt32Size; // + restore offset
+#else
// recordRestoreOffsetPlaceholder doesn't always write an offset
if (!fRestoreOffsetStack.isEmpty()) {
// + restore offset
size += kUInt32Size;
}
+#endif
size_t initialOffset = this->addDraw(CLIP_PATH, &size);
this->addInt(pathID);
this->addInt(ClipParams_pack(op, doAA));
int offset = recordRestoreOffsetPlaceholder(op);
-
this->validate(initialOffset, size);
return offset;
}
bool SkPictureRecord::clipRegion(const SkRegion& region, SkRegion::Op op) {
+
+#ifdef SK_COLLAPSE_MATRIX_CLIP_STATE
+ fMCMgr.clipRegion(region, op);
+#else
this->recordClipRegion(region, op);
+#endif
return this->INHERITED::clipRegion(region, op);
}
int SkPictureRecord::recordClipRegion(const SkRegion& region, SkRegion::Op op) {
// op + clip params + region
uint32_t size = 2 * kUInt32Size + region.writeToMemory(NULL);
+#ifdef SK_COLLAPSE_MATRIX_CLIP_STATE
+ size += kUInt32Size; // + restore offset
+#else
// recordRestoreOffsetPlaceholder doesn't always write an offset
if (!fRestoreOffsetStack.isEmpty()) {
// + restore offset
size += kUInt32Size;
}
+#endif
size_t initialOffset = this->addDraw(CLIP_REGION, &size);
this->addRegion(region);
this->addInt(ClipParams_pack(op, false));
}
void SkPictureRecord::clear(SkColor color) {
+
+#ifdef SK_COLLAPSE_MATRIX_CLIP_STATE
+ fMCMgr.call(SkMatrixClipStateMgr::kOther_CallType);
+#endif
+
// op + color
uint32_t size = 2 * kUInt32Size;
size_t initialOffset = this->addDraw(DRAW_CLEAR, &size);
}
void SkPictureRecord::drawPaint(const SkPaint& paint) {
+
+#ifdef SK_COLLAPSE_MATRIX_CLIP_STATE
+ fMCMgr.call(SkMatrixClipStateMgr::kOther_CallType);
+#endif
+
// op + paint index
uint32_t size = 2 * kUInt32Size;
size_t initialOffset = this->addDraw(DRAW_PAINT, &size);
void SkPictureRecord::drawPoints(PointMode mode, size_t count, const SkPoint pts[],
const SkPaint& paint) {
+
+#ifdef SK_COLLAPSE_MATRIX_CLIP_STATE
+ fMCMgr.call(SkMatrixClipStateMgr::kOther_CallType);
+#endif
+
// op + paint index + mode + count + point data
uint32_t size = 4 * kUInt32Size + count * sizeof(SkPoint);
size_t initialOffset = this->addDraw(DRAW_POINTS, &size);
}
void SkPictureRecord::drawOval(const SkRect& oval, const SkPaint& paint) {
+
+#ifdef SK_COLLAPSE_MATRIX_CLIP_STATE
+ fMCMgr.call(SkMatrixClipStateMgr::kOther_CallType);
+#endif
+
// op + paint index + rect
uint32_t size = 2 * kUInt32Size + sizeof(oval);
size_t initialOffset = this->addDraw(DRAW_OVAL, &size);
}
void SkPictureRecord::drawRect(const SkRect& rect, const SkPaint& paint) {
+
+#ifdef SK_COLLAPSE_MATRIX_CLIP_STATE
+ fMCMgr.call(SkMatrixClipStateMgr::kOther_CallType);
+#endif
+
// op + paint index + rect
uint32_t size = 2 * kUInt32Size + sizeof(rect);
size_t initialOffset = this->addDraw(DRAW_RECT, &size);
}
void SkPictureRecord::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
+
+#ifdef SK_COLLAPSE_MATRIX_CLIP_STATE
+ fMCMgr.call(SkMatrixClipStateMgr::kOther_CallType);
+#endif
+
if (rrect.isRect()) {
this->SkPictureRecord::drawRect(rrect.getBounds(), paint);
} else if (rrect.isOval()) {
}
void SkPictureRecord::drawPath(const SkPath& path, const SkPaint& paint) {
+
+#ifdef SK_COLLAPSE_MATRIX_CLIP_STATE
+ fMCMgr.call(SkMatrixClipStateMgr::kOther_CallType);
+#endif
+
// op + paint index + path index
uint32_t size = 3 * kUInt32Size;
size_t initialOffset = this->addDraw(DRAW_PATH, &size);
if (bitmap.drawsNothing()) {
return;
}
+
+#ifdef SK_COLLAPSE_MATRIX_CLIP_STATE
+ fMCMgr.call(SkMatrixClipStateMgr::kOther_CallType);
+#endif
+
// op + paint index + bitmap index + left + top
uint32_t size = 3 * kUInt32Size + 2 * sizeof(SkScalar);
size_t initialOffset = this->addDraw(DRAW_BITMAP, &size);
if (bitmap.drawsNothing()) {
return;
}
+
+#ifdef SK_COLLAPSE_MATRIX_CLIP_STATE
+ fMCMgr.call(SkMatrixClipStateMgr::kOther_CallType);
+#endif
// id + paint index + bitmap index + bool for 'src' + flags
uint32_t size = 5 * kUInt32Size;
if (NULL != src) {
if (bitmap.drawsNothing()) {
return;
}
+
+#ifdef SK_COLLAPSE_MATRIX_CLIP_STATE
+ fMCMgr.call(SkMatrixClipStateMgr::kOther_CallType);
+#endif
+
// id + paint index + bitmap index + matrix
uint32_t size = 3 * kUInt32Size + matrix.writeToMemory(NULL);
size_t initialOffset = this->addDraw(DRAW_BITMAP_MATRIX, &size);
if (bitmap.drawsNothing()) {
return;
}
+
+#ifdef SK_COLLAPSE_MATRIX_CLIP_STATE
+ fMCMgr.call(SkMatrixClipStateMgr::kOther_CallType);
+#endif
+
// op + paint index + bitmap id + center + dst rect
uint32_t size = 3 * kUInt32Size + sizeof(center) + sizeof(dst);
size_t initialOffset = this->addDraw(DRAW_BITMAP_NINE, &size);
if (bitmap.drawsNothing()) {
return;
}
+
+#ifdef SK_COLLAPSE_MATRIX_CLIP_STATE
+ fMCMgr.call(SkMatrixClipStateMgr::kOther_CallType);
+#endif
+
// op + paint index + bitmap index + left + top
uint32_t size = 5 * kUInt32Size;
size_t initialOffset = this->addDraw(DRAW_SPRITE, &size);
void SkPictureRecord::drawText(const void* text, size_t byteLength, SkScalar x,
SkScalar y, const SkPaint& paint) {
+
+#ifdef SK_COLLAPSE_MATRIX_CLIP_STATE
+ fMCMgr.call(SkMatrixClipStateMgr::kOther_CallType);
+#endif
+
bool fast = !paint.isVerticalText() && paint.canComputeFastBounds();
// op + paint index + length + 'length' worth of chars + x + y
void SkPictureRecord::drawPosText(const void* text, size_t byteLength,
const SkPoint pos[], const SkPaint& paint) {
+
+#ifdef SK_COLLAPSE_MATRIX_CLIP_STATE
+ fMCMgr.call(SkMatrixClipStateMgr::kOther_CallType);
+#endif
+
size_t points = paint.countText(text, byteLength);
if (0 == points)
return;
const SkScalar xpos[], SkScalar constY,
const SkPaint& paint) {
+#ifdef SK_COLLAPSE_MATRIX_CLIP_STATE
+ fMCMgr.call(SkMatrixClipStateMgr::kOther_CallType);
+#endif
+
const SkFlatData* flatPaintData = this->getFlatPaintData(paint);
this->drawPosTextHImpl(text, byteLength, xpos, constY, paint, flatPaintData);
}
void SkPictureRecord::drawTextOnPath(const void* text, size_t byteLength,
const SkPath& path, const SkMatrix* matrix,
const SkPaint& paint) {
+
+#ifdef SK_COLLAPSE_MATRIX_CLIP_STATE
+ fMCMgr.call(SkMatrixClipStateMgr::kOther_CallType);
+#endif
+
// op + paint index + length + 'length' worth of data + path index + matrix
const SkMatrix& m = matrix ? *matrix : SkMatrix::I();
uint32_t size = 3 * kUInt32Size + SkAlign4(byteLength) + kUInt32Size + m.writeToMemory(NULL);
}
void SkPictureRecord::drawPicture(SkPicture& picture) {
+
+#ifdef SK_COLLAPSE_MATRIX_CLIP_STATE
+ fMCMgr.call(SkMatrixClipStateMgr::kOther_CallType);
+#endif
+
// op + picture index
uint32_t size = 2 * kUInt32Size;
size_t initialOffset = this->addDraw(DRAW_PICTURE, &size);
const SkColor colors[], SkXfermode* xfer,
const uint16_t indices[], int indexCount,
const SkPaint& paint) {
+
+#ifdef SK_COLLAPSE_MATRIX_CLIP_STATE
+ fMCMgr.call(SkMatrixClipStateMgr::kOther_CallType);
+#endif
+
uint32_t flags = 0;
if (texs) {
flags |= DRAW_VERTICES_HAS_TEXS;
}
void SkPictureRecord::drawData(const void* data, size_t length) {
+
+#ifdef SK_COLLAPSE_MATRIX_CLIP_STATE
+ fMCMgr.call(SkMatrixClipStateMgr::kOther_CallType);
+#endif
+
// op + length + 'length' worth of data
uint32_t size = 2 * kUInt32Size + SkAlign4(length);
size_t initialOffset = this->addDraw(DRAW_DATA, &size);
#include "SkCanvas.h"
#include "SkFlattenable.h"
+#ifdef SK_COLLAPSE_MATRIX_CLIP_STATE
+#include "SkMatrixClipStateMgr.h"
+#endif
#include "SkPathHeap.h"
#include "SkPicture.h"
#include "SkPictureFlat.h"
int recordRestoreOffsetPlaceholder(SkRegion::Op);
void fillRestoreOffsetPlaceholdersForCurrentStackLevel(uint32_t restoreOffset);
+#ifndef SK_COLLAPSE_MATRIX_CLIP_STATE
SkTDArray<int32_t> fRestoreOffsetStack;
int fFirstSavedLayerIndex;
enum {
kNoSavedLayerIndex = -1
};
+#endif
/*
* Write the 'drawType' operation and chunk size to the skp. 'size'
int recordClipRegion(const SkRegion& region, SkRegion::Op op);
void recordSave(SaveFlags flags);
void recordSaveLayer(const SkRect* bounds, const SkPaint* paint, SaveFlags flags);
- void recordRestore();
+ void recordRestore(bool fillInSkips = true);
// These are set to NULL in our constructor, but may be changed by
// subclasses, in which case they will be SkSafeUnref'd in our destructor.
SkBitmapHeap* fBitmapHeap;
private:
+ friend class MatrixClipState; // for access to *Impl methods
+ friend class SkMatrixClipStateMgr; // for access to *Impl methods
+
SkChunkFlatController fFlattenableHeap;
SkPaintDictionary fPaints;
friend class SkPicturePlayback;
friend class SkPictureTester; // for unit testing
+#ifdef SK_COLLAPSE_MATRIX_CLIP_STATE
+ SkMatrixClipStateMgr fMCMgr;
+#endif
+
typedef SkCanvas INHERITED;
};
// system is ready for prime time
// bench the recording times with/without matrix/clip collapsing
-#ifdef COLLAPSE_MATRIX_CLIP_STATE
+#ifdef SK_COLLAPSE_MATRIX_CLIP_STATE
+
+// Enable/disable debugging helper code
+//#define TEST_COLLAPSE_MATRIX_CLIP_STATE 1
// Extract the command ops from the input SkPicture
static void gets_ops(SkPicture& input, SkTDArray<DrawType>* ops) {
//////////////////////////////////////////////////////////////////////////////
-#ifdef COLLAPSE_MATRIX_CLIP_STATE
+#ifdef SK_COLLAPSE_MATRIX_CLIP_STATE
static void print(const SkTDArray<DrawType>& expected, const SkTDArray<DrawType>& actual) {
SkDebugf("\n\nexpected %d --- actual %d\n", expected.count(), actual.count());
int max = SkMax32(expected.count(), actual.count());
for (int l = 0; l < kMatTypeCount; ++l) {
for (int m = 0; m < kClipTypeCount; ++m) {
for (int n = 0; n < kDrawOpTypeCount; ++n) {
-#ifdef COLLAPSE_MATRIX_CLIP_STATE
+#ifdef TEST_COLLAPSE_MATRIX_CLIP_STATE
static int testID = -1;
++testID;
if (testID < -1) {
REPORTER_ASSERT(reporter, expected.count() == actual.count());
if (expected.count() != actual.count()) {
-#ifdef COLLAPSE_MATRIX_CLIP_STATE
+#ifdef TEST_COLLAPSE_MATRIX_CLIP_STATE
print(expected, actual);
#endif
continue;
for (int i = 0; i < expected.count(); ++i) {
REPORTER_ASSERT(reporter, expected[i] == actual[i]);
-#ifdef COLLAPSE_MATRIX_CLIP_STATE
+#ifdef TEST_COLLAPSE_MATRIX_CLIP_STATE
if (expected[i] != actual[i]) {
print(expected, actual);
}