blob: f885240f4b70d138d069222d000d26e26b4bbf8a [file] [log] [blame]
/*
* Copyright 2011 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "SkClipStack.h"
#include "SkPath.h"
#include <new>
struct SkClipStack::Rec {
enum State {
kEmpty_State,
kRect_State,
kPath_State
};
SkPath fPath;
SkRect fRect;
int fSaveCount;
SkRegion::Op fOp;
State fState;
bool fDoAA;
Rec(int saveCount, const SkRect& rect, SkRegion::Op op, bool doAA) : fRect(rect) {
fSaveCount = saveCount;
fOp = op;
fState = kRect_State;
fDoAA = doAA;
}
Rec(int saveCount, const SkPath& path, SkRegion::Op op, bool doAA) : fPath(path) {
fRect.setEmpty();
fSaveCount = saveCount;
fOp = op;
fState = kPath_State;
fDoAA = doAA;
}
bool operator==(const Rec& b) const {
if (fSaveCount != b.fSaveCount || fOp != b.fOp || fState != b.fState ||
fDoAA != b.fDoAA) {
return false;
}
switch (fState) {
case kEmpty_State:
return true;
case kRect_State:
return fRect == b.fRect;
case kPath_State:
return fPath == b.fPath;
}
return false; // Silence the compiler.
}
bool operator!=(const Rec& b) const {
return !(*this == b);
}
/**
* Returns true if this Rec can be intersected in place with a new clip
*/
bool canBeIntersected(int saveCount, SkRegion::Op op) const {
if (kEmpty_State == fState && (
SkRegion::kDifference_Op == op ||
SkRegion::kIntersect_Op == op)) {
return true;
}
return fSaveCount == saveCount &&
SkRegion::kIntersect_Op == fOp &&
SkRegion::kIntersect_Op == op;
}
};
SkClipStack::SkClipStack() : fDeque(sizeof(Rec)) {
fSaveCount = 0;
}
SkClipStack::SkClipStack(const SkClipStack& b) : fDeque(sizeof(Rec)) {
*this = b;
}
SkClipStack& SkClipStack::operator=(const SkClipStack& b) {
if (this == &b) {
return *this;
}
reset();
fSaveCount = b.fSaveCount;
SkDeque::F2BIter recIter(b.fDeque);
for (const Rec* rec = (const Rec*)recIter.next();
rec != NULL;
rec = (const Rec*)recIter.next()) {
new (fDeque.push_back()) Rec(*rec);
}
return *this;
}
bool SkClipStack::operator==(const SkClipStack& b) const {
if (fSaveCount != b.fSaveCount || fDeque.count() != b.fDeque.count()) {
return false;
}
SkDeque::F2BIter myIter(fDeque);
SkDeque::F2BIter bIter(b.fDeque);
const Rec* myRec = (const Rec*)myIter.next();
const Rec* bRec = (const Rec*)bIter.next();
while (myRec != NULL && bRec != NULL) {
if (*myRec != *bRec) {
return false;
}
myRec = (const Rec*)myIter.next();
bRec = (const Rec*)bIter.next();
}
return myRec == NULL && bRec == NULL;
}
void SkClipStack::reset() {
// don't have a reset() on SkDeque, so fake it here
fDeque.~SkDeque();
new (&fDeque) SkDeque(sizeof(Rec));
fSaveCount = 0;
}
void SkClipStack::save() {
fSaveCount += 1;
}
void SkClipStack::restore() {
fSaveCount -= 1;
while (!fDeque.empty()) {
Rec* rec = (Rec*)fDeque.back();
if (rec->fSaveCount <= fSaveCount) {
break;
}
rec->~Rec();
fDeque.pop_back();
}
}
void SkClipStack::clipDevRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
Rec* rec = (Rec*)fDeque.back();
if (rec && rec->canBeIntersected(fSaveCount, op)) {
switch (rec->fState) {
case Rec::kEmpty_State:
return;
case Rec::kRect_State:
if (!rec->fRect.intersect(rect)) {
rec->fState = Rec::kEmpty_State;
}
return;
case Rec::kPath_State:
if (!SkRect::Intersects(rec->fPath.getBounds(), rect)) {
rec->fState = Rec::kEmpty_State;
return;
}
break;
}
}
new (fDeque.push_back()) Rec(fSaveCount, rect, op, doAA);
}
void SkClipStack::clipDevPath(const SkPath& path, SkRegion::Op op, bool doAA) {
Rec* rec = (Rec*)fDeque.back();
if (rec && rec->canBeIntersected(fSaveCount, op)) {
const SkRect& pathBounds = path.getBounds();
switch (rec->fState) {
case Rec::kEmpty_State:
return;
case Rec::kRect_State:
if (!SkRect::Intersects(rec->fRect, pathBounds)) {
rec->fState = Rec::kEmpty_State;
return;
}
break;
case Rec::kPath_State:
if (!SkRect::Intersects(rec->fPath.getBounds(), pathBounds)) {
rec->fState = Rec::kEmpty_State;
return;
}
break;
}
}
new (fDeque.push_back()) Rec(fSaveCount, path, op, doAA);
}
///////////////////////////////////////////////////////////////////////////////
SkClipStack::B2FIter::B2FIter() {
}
bool operator==(const SkClipStack::B2FIter::Clip& a,
const SkClipStack::B2FIter::Clip& b) {
return a.fOp == b.fOp && a.fDoAA == b.fDoAA &&
((a.fRect == NULL && b.fRect == NULL) ||
(a.fRect != NULL && b.fRect != NULL && *a.fRect == *b.fRect)) &&
((a.fPath == NULL && b.fPath == NULL) ||
(a.fPath != NULL && b.fPath != NULL && *a.fPath == *b.fPath));
}
bool operator!=(const SkClipStack::B2FIter::Clip& a,
const SkClipStack::B2FIter::Clip& b) {
return !(a == b);
}
SkClipStack::B2FIter::B2FIter(const SkClipStack& stack) {
this->reset(stack);
}
const SkClipStack::B2FIter::Clip* SkClipStack::B2FIter::next() {
const SkClipStack::Rec* rec = (const SkClipStack::Rec*)fIter.next();
if (NULL == rec) {
return NULL;
}
switch (rec->fState) {
case SkClipStack::Rec::kEmpty_State:
fClip.fRect = NULL;
fClip.fPath = NULL;
break;
case SkClipStack::Rec::kRect_State:
fClip.fRect = &rec->fRect;
fClip.fPath = NULL;
break;
case SkClipStack::Rec::kPath_State:
fClip.fRect = NULL;
fClip.fPath = &rec->fPath;
break;
}
fClip.fOp = rec->fOp;
fClip.fDoAA = rec->fDoAA;
return &fClip;
}
void SkClipStack::B2FIter::reset(const SkClipStack& stack) {
fIter.reset(stack.fDeque);
}