/*
* 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