/*
|
* Copyright 2017 Google Inc.
|
*
|
* Use of this source code is governed by a BSD-style license that can be
|
* found in the LICENSE file.
|
*/
|
|
#ifndef SkRasterClipStack_DEFINED
|
#define SkRasterClipStack_DEFINED
|
|
#include "SkClipOp.h"
|
#include "SkDeque.h"
|
#include "SkRasterClip.h"
|
#include <new>
|
|
template <typename T> class SkTStack {
|
public:
|
SkTStack(void* storage, size_t size) : fDeque(sizeof(T), storage, size), fTop(nullptr) {}
|
~SkTStack() {
|
while (!fDeque.empty()) {
|
((T*)fDeque.back())->~T();
|
fDeque.pop_back();
|
}
|
}
|
|
bool empty() const { return fDeque.empty(); }
|
|
int count() const { return fDeque.count(); }
|
|
const T& top() const {
|
SkASSERT(fTop);
|
return *fTop;
|
}
|
|
T& top() {
|
SkASSERT(fTop);
|
return *fTop;
|
}
|
|
T* push_raw() { return (T*)fDeque.push_back(); }
|
T& push() {
|
fTop = this->push_raw();
|
new (fTop) T();
|
return *fTop;
|
}
|
T& push(const T& src) {
|
fTop = this->push_raw();
|
new (fTop) T(src);
|
return *fTop;
|
}
|
|
void pop() {
|
fTop->~T();
|
fDeque.pop_back();
|
fTop = fDeque.empty() ? nullptr : (T*)fDeque.back();
|
}
|
|
private:
|
SkDeque fDeque;
|
T* fTop;
|
};
|
|
class SkRasterClipStack : SkNoncopyable {
|
int fCounter = 0;
|
public:
|
SkRasterClipStack(int width, int height)
|
: fStack(fStorage, sizeof(fStorage))
|
, fRootBounds(SkIRect::MakeWH(width, height))
|
{
|
Rec& rec = fStack.push();
|
rec.fRC.setRect(fRootBounds);
|
rec.fDeferredCount = 0;
|
SkASSERT(fStack.count() == 1);
|
}
|
|
void setNewSize(int w, int h) {
|
fRootBounds.setXYWH(0, 0, w, h);
|
|
SkASSERT(fStack.count() == 1);
|
Rec& rec = fStack.top();
|
SkASSERT(rec.fDeferredCount == 0);
|
rec.fRC.setRect(fRootBounds);
|
}
|
|
const SkRasterClip& rc() const { return fStack.top().fRC; }
|
|
void save() {
|
fCounter += 1;
|
SkASSERT(fStack.top().fDeferredCount >= 0);
|
fStack.top().fDeferredCount += 1;
|
}
|
|
void restore() {
|
fCounter -= 1; SkASSERT(fCounter >= 0);
|
if (--fStack.top().fDeferredCount < 0) {
|
SkASSERT(fStack.top().fDeferredCount == -1);
|
SkASSERT(fStack.count() > 1);
|
fStack.pop();
|
}
|
}
|
|
void clipRect(const SkMatrix& ctm, const SkRect& rect, SkClipOp op, bool aa) {
|
this->writable_rc().op(rect, ctm, fRootBounds, (SkRegion::Op)op, aa);
|
this->trimIfExpanding(op);
|
this->validate();
|
}
|
|
void clipRRect(const SkMatrix& ctm, const SkRRect& rrect, SkClipOp op, bool aa) {
|
this->writable_rc().op(rrect, ctm, fRootBounds, (SkRegion::Op)op, aa);
|
this->trimIfExpanding(op);
|
this->validate();
|
}
|
|
void clipPath(const SkMatrix& ctm, const SkPath& path, SkClipOp op, bool aa) {
|
this->writable_rc().op(path, ctm, fRootBounds, (SkRegion::Op)op, aa);
|
this->trimIfExpanding(op);
|
this->validate();
|
}
|
|
void clipRegion(const SkRegion& rgn, SkClipOp op) {
|
this->writable_rc().op(rgn, (SkRegion::Op)op);
|
this->trimIfExpanding(op);
|
this->validate();
|
}
|
|
void setDeviceClipRestriction(SkIRect* mutableClipRestriction) {
|
this->writable_rc().setDeviceClipRestriction(mutableClipRestriction);
|
}
|
|
void validate() const {
|
#ifdef SK_DEBUG
|
const SkRasterClip& clip = this->rc();
|
if (fRootBounds.isEmpty()) {
|
SkASSERT(clip.isEmpty());
|
} else if (!clip.isEmpty()) {
|
SkASSERT(fRootBounds.contains(clip.getBounds()));
|
}
|
#endif
|
}
|
|
private:
|
struct Rec {
|
SkRasterClip fRC;
|
int fDeferredCount; // 0 for a "normal" entry
|
};
|
|
enum {
|
ELEM_COUNT = 16,
|
PTR_COUNT = ELEM_COUNT * sizeof(Rec) / sizeof(void*)
|
};
|
void* fStorage[PTR_COUNT];
|
SkTStack<Rec> fStack;
|
SkIRect fRootBounds;
|
|
SkRasterClip& writable_rc() {
|
SkASSERT(fStack.top().fDeferredCount >= 0);
|
if (fStack.top().fDeferredCount > 0) {
|
fStack.top().fDeferredCount -= 1;
|
fStack.push(fStack.top());
|
fStack.top().fDeferredCount = 0;
|
}
|
return fStack.top().fRC;
|
}
|
|
void trimIfExpanding(SkClipOp op) {
|
if ((int)op > (int)SkClipOp::kIntersect) {
|
Rec& rec = fStack.top();
|
SkASSERT(rec.fDeferredCount == 0);
|
rec.fRC.op(fRootBounds, SkRegion::kIntersect_Op);
|
}
|
}
|
};
|
|
#endif
|