aboutsummaryrefslogtreecommitdiffstats
path: root/src/gpu/GrStencil.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/gpu/GrStencil.h')
-rw-r--r--src/gpu/GrStencil.h323
1 files changed, 323 insertions, 0 deletions
diff --git a/src/gpu/GrStencil.h b/src/gpu/GrStencil.h
new file mode 100644
index 0000000..ae81840
--- /dev/null
+++ b/src/gpu/GrStencil.h
@@ -0,0 +1,323 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+#ifndef GrStencil_DEFINED
+#define GrStencil_DEFINED
+
+#include "GrTypes.h"
+/**
+ * Gr uses the stencil buffer to implement complex clipping inside the
+ * GrDrawTarget class. The GrDrawTarget makes a subset of the stencil buffer
+ * bits available for other uses by external code (clients). Client code can
+ * modify these bits. GrDrawTarget will ignore ref, mask, and writemask bits
+ * provided by clients that overlap the bits used to implement clipping.
+ *
+ * When code outside the GrDrawTarget class uses the stencil buffer the contract
+ * is as follows:
+ *
+ * > Normal stencil funcs allow the client to pass / fail regardless of the
+ * reserved clip bits.
+ * > Additional functions allow a test against the clip along with a limited
+ * set of tests against the client bits.
+ * > Client can assume all client bits are zero initially.
+ * > Client must ensure that after all its passes are finished it has only
+ * written to the color buffer in the region inside the clip. Furthermore, it
+ * must zero all client bits that were modifed (both inside and outside the
+ * clip).
+ */
+
+/**
+ * Determines which pixels pass / fail the stencil test.
+ * Stencil test passes if (ref & mask) FUNC (stencil & mask) is true
+ */
+enum GrStencilFunc {
+ kAlways_StencilFunc = 0,
+ kNever_StencilFunc,
+ kGreater_StencilFunc,
+ kGEqual_StencilFunc,
+ kLess_StencilFunc,
+ kLEqual_StencilFunc,
+ kEqual_StencilFunc,
+ kNotEqual_StencilFunc,
+
+ // Gr stores the current clip in the
+ // stencil buffer in the high bits that
+ // are not directly accessible modifiable
+ // via the GrDrawTarget interface. The below
+ // stencil funcs test against the current
+ // clip in addition to the GrDrawTarget
+ // client's stencil bits.
+
+ // pass if inside the clip
+ kAlwaysIfInClip_StencilFunc,
+ kEqualIfInClip_StencilFunc,
+ kLessIfInClip_StencilFunc,
+ kLEqualIfInClip_StencilFunc,
+ kNonZeroIfInClip_StencilFunc, // this one forces the ref to be 0
+
+ // counts
+ kStencilFuncCount,
+ kClipStencilFuncCount = kNonZeroIfInClip_StencilFunc -
+ kAlwaysIfInClip_StencilFunc + 1,
+ kBasicStencilFuncCount = kStencilFuncCount - kClipStencilFuncCount
+};
+
+/**
+ * Operations to perform based on whether stencil test passed failed.
+ */
+enum GrStencilOp {
+ kKeep_StencilOp = 0, // preserve existing stencil value
+ kReplace_StencilOp, // replace with reference value from stencl test
+ kIncWrap_StencilOp, // increment and wrap at max
+ kIncClamp_StencilOp, // increment and clamp at max
+ kDecWrap_StencilOp, // decrement and wrap at 0
+ kDecClamp_StencilOp, // decrement and clamp at 0
+ kZero_StencilOp, // zero stencil bits
+ kInvert_StencilOp, // invert stencil bits
+
+ kStencilOpCount
+};
+
+/**
+ * GrStencilState needs to be a class with accessors and setters so that it
+ * can maintain flags related to its current state. However, we also want to
+ * be able to declare pre-made stencil settings at compile time (without
+ * inserting static initializer code). So all the data members are in this
+ * struct. A macro defined after the class can be used to jam an instance of
+ * this struct that is created from an initializer list into a
+ * GrStencilSettings. (We hang our heads in shame.)
+ */
+struct GrStencilSettingsStruct {
+ GrStencilOp fFrontPassOp : 8; // op to perform when front faces pass
+ GrStencilOp fBackPassOp : 8; // op to perform when back faces pass
+ GrStencilOp fFrontFailOp : 8; // op to perform when front faces fail
+ GrStencilOp fBackFailOp : 8; // op to perform when back faces fail
+ GrStencilFunc fFrontFunc : 8; // test function for front faces
+ GrStencilFunc fBackFunc : 8; // test function for back faces
+ int fPad0 : 8;
+ int fPad1 : 8;
+ unsigned short fFrontFuncMask; // mask for front face test
+ unsigned short fBackFuncMask; // mask for back face test
+ unsigned short fFrontFuncRef; // reference value for front face test
+ unsigned short fBackFuncRef; // reference value for back face test
+ unsigned short fFrontWriteMask; // stencil write mask for front faces
+ unsigned short fBackWriteMask; // stencil write mask for back faces
+ mutable uint32_t fFlags;
+};
+// We rely on this being packed and aligned (memcmp'ed and memcpy'ed)
+GR_STATIC_ASSERT(sizeof(GrStencilSettingsStruct) % 4 == 0);
+GR_STATIC_ASSERT(sizeof(GrStencilSettingsStruct) ==
+ 4*sizeof(uint8_t) + // ops
+ 2*sizeof(uint8_t) + // funcs
+ 2*sizeof(uint8_t) + // pads
+ 2*sizeof(unsigned short) + // func masks
+ 2*sizeof(unsigned short) + // ref values
+ 2*sizeof(unsigned short) + // write masks
+ sizeof(uint32_t)); // flags
+
+/**
+ * Class representing stencil state.
+ */
+class GrStencilSettings : private GrStencilSettingsStruct {
+
+public:
+ GrStencilSettings() {
+ fPad0 = fPad1 = 0;
+ this->setDisabled();
+ }
+
+ GrStencilOp frontPassOp() const { return fFrontPassOp; }
+ GrStencilOp backPassOp() const { return fBackPassOp; }
+ GrStencilOp frontFailOp() const { return fFrontFailOp; }
+ GrStencilOp backFailOp() const { return fBackFailOp; }
+ GrStencilFunc frontFunc() const { return fFrontFunc; }
+ GrStencilFunc backFunc() const { return fBackFunc; }
+ unsigned short frontFuncMask() const { return fFrontFuncMask; }
+ unsigned short backFuncMask() const { return fBackFuncMask; }
+ unsigned short frontFuncRef() const { return fFrontFuncRef; }
+ unsigned short backFuncRef() const { return fBackFuncRef; }
+ unsigned short frontWriteMask() const {return fFrontWriteMask; }
+ unsigned short backWriteMask() const { return fBackWriteMask; }
+
+ void setFrontPassOp(GrStencilOp op) { fFrontPassOp = op; fFlags = 0;}
+ void setBackPassOp(GrStencilOp op) { fBackPassOp = op; fFlags = 0;}
+ void setFrontFailOp(GrStencilOp op) {fFrontFailOp = op; fFlags = 0;}
+ void setBackFailOp(GrStencilOp op) { fBackFailOp = op; fFlags = 0;}
+ void setFrontFunc(GrStencilFunc func) { fFrontFunc = func; fFlags = 0;}
+ void setBackFunc(GrStencilFunc func) { fBackFunc = func; fFlags = 0;}
+ void setFrontFuncMask(unsigned short mask) { fFrontFuncMask = mask; }
+ void setBackFuncMask(unsigned short mask) { fBackFuncMask = mask; }
+ void setFrontFuncRef(unsigned short ref) { fFrontFuncRef = ref; }
+ void setBackFuncRef(unsigned short ref) { fBackFuncRef = ref; }
+ void setFrontWriteMask(unsigned short writeMask) { fFrontWriteMask = writeMask; }
+ void setBackWriteMask(unsigned short writeMask) { fBackWriteMask = writeMask; }
+
+ void setSame(GrStencilOp passOp,
+ GrStencilOp failOp,
+ GrStencilFunc func,
+ unsigned short funcMask,
+ unsigned short funcRef,
+ unsigned short writeMask) {
+ fFrontPassOp = passOp;
+ fBackPassOp = passOp;
+ fFrontFailOp = failOp;
+ fBackFailOp = failOp;
+ fFrontFunc = func;
+ fBackFunc = func;
+ fFrontFuncMask = funcMask;
+ fBackFuncMask = funcMask;
+ fFrontFuncRef = funcRef;
+ fBackFuncRef = funcRef;
+ fFrontWriteMask = writeMask;
+ fBackWriteMask = writeMask;
+ fFlags = 0;
+ }
+
+ void setDisabled() {
+ memset(this, 0, sizeof(*this));
+ GR_STATIC_ASSERT(0 == kKeep_StencilOp);
+ GR_STATIC_ASSERT(0 == kAlways_StencilFunc);
+ fFlags = kIsDisabled_Flag | kDoesNotWrite_Flag;
+ }
+
+ bool isDisabled() const {
+ if (fFlags & kIsDisabled_Flag) {
+ return true;
+ }
+ if (fFlags & kNotDisabled_Flag) {
+ return false;
+ }
+ bool disabled = kKeep_StencilOp == fFrontPassOp &&
+ kKeep_StencilOp == fBackPassOp &&
+ kKeep_StencilOp == fFrontFailOp &&
+ kKeep_StencilOp == fBackFailOp &&
+ kAlways_StencilFunc == fFrontFunc &&
+ kAlways_StencilFunc == fBackFunc;
+ fFlags |= disabled ? kIsDisabled_Flag : kNotDisabled_Flag;
+ return disabled;
+ }
+
+ bool doesWrite() const {
+ if (fFlags & kDoesWrite_Flag) {
+ return true;
+ }
+ if (fFlags & kDoesNotWrite_Flag) {
+ return false;
+ }
+ bool writes = !((kNever_StencilFunc == fFrontFunc ||
+ kKeep_StencilOp == fFrontPassOp) &&
+ (kNever_StencilFunc == fBackFunc ||
+ kKeep_StencilOp == fBackPassOp) &&
+ (kAlways_StencilFunc == fFrontFunc ||
+ kKeep_StencilOp == fFrontFailOp) &&
+ (kAlways_StencilFunc == fBackFunc ||
+ kKeep_StencilOp == fBackFailOp));
+ fFlags |= writes ? kDoesWrite_Flag : kDoesNotWrite_Flag;
+ return writes;
+ }
+
+ void invalidate() {
+ // write an illegal value to the first member
+ fFrontPassOp = (GrStencilOp)(uint8_t)-1;
+ fFlags = 0;
+ }
+
+ bool operator == (const GrStencilSettings& s) const {
+ static const size_t gCompareSize = sizeof(GrStencilSettings) -
+ sizeof(fFlags);
+ GrAssert((const char*)&fFlags + sizeof(fFlags) ==
+ (const char*)this + sizeof(GrStencilSettings));
+ if (this->isDisabled() & s.isDisabled()) { // using & not &&
+ return true;
+ }
+ return 0 == memcmp(this, &s, gCompareSize);
+ }
+
+ bool operator != (const GrStencilSettings& s) const {
+ return !(*this == s);
+ }
+
+ GrStencilSettings& operator =(const GrStencilSettings& s) {
+ memcpy(this, &s, sizeof(GrStencilSettings));
+ return *this;
+ }
+
+private:
+ friend class GrGpu;
+ enum {
+ kIsDisabled_Flag = 0x1,
+ kNotDisabled_Flag = 0x2,
+ kDoesWrite_Flag = 0x4,
+ kDoesNotWrite_Flag = 0x8,
+ };
+
+ enum {
+ kMaxStencilClipPasses = 2 // maximum number of passes to add a clip
+ // element to the stencil buffer.
+ };
+
+ /**
+ * Given a thing to draw into the stencil clip, a fill type, and a set op
+ * this function determines:
+ * 1. Whether the thing can be draw directly to the stencil clip or
+ * needs to be drawn to the client portion of the stencil first.
+ * 2. How many passes are needed.
+ * 3. What those passes are.
+ * 4. The fill rule that should actually be used to render (will
+ * always be non-inverted).
+ *
+ * @param op the set op to combine this element with the
+ * existing clip
+ * @param stencilClipMask mask with just the stencil bit used for clipping
+ * enabled.
+ * @param invertedFill is this path inverted
+ * @param numPasses out: the number of passes needed to add the
+ * element to the clip.
+ * @param settings out: the stencil settings to use for each pass
+ *
+ * @return true if the clip element's geometry can be drawn directly to the
+ * stencil clip bit. Will only be true if canBeDirect is true.
+ * numPasses will be 1 if return value is true.
+ */
+ static bool GetClipPasses(GrSetOp op,
+ bool canBeDirect,
+ unsigned int stencilClipMask,
+ bool invertedFill,
+ int* numPasses,
+ GrStencilSettings settings[kMaxStencilClipPasses]);
+};
+
+GR_STATIC_ASSERT(sizeof(GrStencilSettingsStruct) == sizeof(GrStencilSettings));
+
+#define GR_STATIC_CONST_STENCIL(NAME, \
+ FRONT_PASS_OP, BACK_PASS_OP, \
+ FRONT_FAIL_OP, BACK_FAIL_OP, \
+ FRONT_FUNC, BACK_FUNC, \
+ FRONT_MASK, BACK_MASK, \
+ FRONT_REF, BACK_REF, \
+ FRONT_WRITE_MASK, BACK_WRITE_MASK) \
+ static const GrStencilSettingsStruct NAME ## _STRUCT = { \
+ (FRONT_PASS_OP), (BACK_PASS_OP), \
+ (FRONT_FAIL_OP), (BACK_FAIL_OP), \
+ (FRONT_FUNC), (BACK_FUNC), \
+ (0), (0), \
+ (FRONT_MASK), (BACK_MASK), \
+ (FRONT_REF), (BACK_REF), \
+ (FRONT_WRITE_MASK), (BACK_WRITE_MASK), \
+ 0 \
+ }; \
+ static const GrStencilSettings& NAME = \
+ *reinterpret_cast<const GrStencilSettings*>(&(NAME ## _STRUCT))
+#endif
+
+#define GR_STATIC_CONST_SAME_STENCIL(NAME, \
+ PASS_OP, FAIL_OP, FUNC, MASK, REF, WRITE_MASK) \
+ GR_STATIC_CONST_STENCIL(NAME, (PASS_OP), (PASS_OP), (FAIL_OP), \
+ (FAIL_OP), (FUNC), (FUNC), (MASK), (MASK), (REF), (REF), (WRITE_MASK), \
+ (WRITE_MASK))