michael@0: michael@0: /* michael@0: * Copyright 2011 Google Inc. michael@0: * michael@0: * Use of this source code is governed by a BSD-style license that can be michael@0: * found in the LICENSE file. michael@0: */ michael@0: michael@0: michael@0: #ifndef GrStencil_DEFINED michael@0: #define GrStencil_DEFINED michael@0: michael@0: #include "GrTypes.h" michael@0: #include "SkRegion.h" michael@0: michael@0: /** michael@0: * Gr uses the stencil buffer to implement complex clipping inside the michael@0: * GrDrawTarget class. The GrDrawTarget makes a subset of the stencil buffer michael@0: * bits available for other uses by external code (clients). Client code can michael@0: * modify these bits. GrDrawTarget will ignore ref, mask, and writemask bits michael@0: * provided by clients that overlap the bits used to implement clipping. michael@0: * michael@0: * When code outside the GrDrawTarget class uses the stencil buffer the contract michael@0: * is as follows: michael@0: * michael@0: * > Normal stencil funcs allow the client to pass / fail regardless of the michael@0: * reserved clip bits. michael@0: * > Additional functions allow a test against the clip along with a limited michael@0: * set of tests against the client bits. michael@0: * > Client can assume all client bits are zero initially. michael@0: * > Client must ensure that after all its passes are finished it has only michael@0: * written to the color buffer in the region inside the clip. Furthermore, it michael@0: * must zero all client bits that were modifed (both inside and outside the michael@0: * clip). michael@0: */ michael@0: michael@0: /** michael@0: * Determines which pixels pass / fail the stencil test. michael@0: * Stencil test passes if (ref & mask) FUNC (stencil & mask) is true michael@0: */ michael@0: enum GrStencilFunc { michael@0: kAlways_StencilFunc = 0, michael@0: kNever_StencilFunc, michael@0: kGreater_StencilFunc, michael@0: kGEqual_StencilFunc, michael@0: kLess_StencilFunc, michael@0: kLEqual_StencilFunc, michael@0: kEqual_StencilFunc, michael@0: kNotEqual_StencilFunc, michael@0: michael@0: // Gr stores the current clip in the michael@0: // stencil buffer in the high bits that michael@0: // are not directly accessible modifiable michael@0: // via the GrDrawTarget interface. The below michael@0: // stencil funcs test against the current michael@0: // clip in addition to the GrDrawTarget michael@0: // client's stencil bits. michael@0: michael@0: // pass if inside the clip michael@0: kAlwaysIfInClip_StencilFunc, michael@0: kEqualIfInClip_StencilFunc, michael@0: kLessIfInClip_StencilFunc, michael@0: kLEqualIfInClip_StencilFunc, michael@0: kNonZeroIfInClip_StencilFunc, // this one forces the ref to be 0 michael@0: michael@0: // counts michael@0: kStencilFuncCount, michael@0: kClipStencilFuncCount = kNonZeroIfInClip_StencilFunc - michael@0: kAlwaysIfInClip_StencilFunc + 1, michael@0: kBasicStencilFuncCount = kStencilFuncCount - kClipStencilFuncCount michael@0: }; michael@0: michael@0: /** michael@0: * Operations to perform based on whether stencil test passed failed. michael@0: */ michael@0: enum GrStencilOp { michael@0: kKeep_StencilOp = 0, // preserve existing stencil value michael@0: kReplace_StencilOp, // replace with reference value from stencl test michael@0: kIncWrap_StencilOp, // increment and wrap at max michael@0: kIncClamp_StencilOp, // increment and clamp at max michael@0: kDecWrap_StencilOp, // decrement and wrap at 0 michael@0: kDecClamp_StencilOp, // decrement and clamp at 0 michael@0: kZero_StencilOp, // zero stencil bits michael@0: kInvert_StencilOp, // invert stencil bits michael@0: michael@0: kStencilOpCount michael@0: }; michael@0: michael@0: enum GrStencilFlags { michael@0: kIsDisabled_StencilFlag = 0x1, michael@0: kNotDisabled_StencilFlag = 0x2, michael@0: kDoesWrite_StencilFlag = 0x4, michael@0: kDoesNotWrite_StencilFlag = 0x8, michael@0: }; michael@0: michael@0: /** michael@0: * GrStencilState needs to be a class with accessors and setters so that it michael@0: * can maintain flags related to its current state. However, we also want to michael@0: * be able to declare pre-made stencil settings at compile time (without michael@0: * inserting static initializer code). So all the data members are in this michael@0: * struct. A macro defined after the class can be used to jam an instance of michael@0: * this struct that is created from an initializer list into a michael@0: * GrStencilSettings. (We hang our heads in shame.) michael@0: */ michael@0: struct GrStencilSettingsStruct { michael@0: uint8_t fPassOps[2]; // op to perform when faces pass (GrStencilOp) michael@0: uint8_t fFailOps[2]; // op to perform when faces fail (GrStencilOp) michael@0: uint8_t fFuncs[2]; // test function for faces (GrStencilFunc) michael@0: uint8_t fPad0; michael@0: uint8_t fPad1; michael@0: uint16_t fFuncMasks[2]; // mask for face tests michael@0: uint16_t fFuncRefs[2]; // reference values for face tests michael@0: uint16_t fWriteMasks[2]; // stencil write masks michael@0: mutable uint32_t fFlags; michael@0: }; michael@0: // We rely on this being packed and aligned (memcmp'ed and memcpy'ed) michael@0: GR_STATIC_ASSERT(sizeof(GrStencilSettingsStruct) % 4 == 0); michael@0: GR_STATIC_ASSERT(sizeof(GrStencilSettingsStruct) == michael@0: 4*sizeof(uint8_t) + // ops michael@0: 2*sizeof(uint8_t) + // funcs michael@0: 2*sizeof(uint8_t) + // pads michael@0: 2*sizeof(uint16_t) + // func masks michael@0: 2*sizeof(uint16_t) + // ref values michael@0: 2*sizeof(uint16_t) + // write masks michael@0: sizeof(uint32_t)); // flags michael@0: michael@0: // This macro is used to compute the GrStencilSettingsStructs flags michael@0: // associated to disabling. It is used both to define constant structure michael@0: // initializers and inside GrStencilSettings::isDisabled() michael@0: // michael@0: #define GR_STENCIL_SETTINGS_IS_DISABLED( \ michael@0: FRONT_PASS_OP, BACK_PASS_OP, \ michael@0: FRONT_FAIL_OP, BACK_FAIL_OP, \ michael@0: FRONT_FUNC, BACK_FUNC) \ michael@0: ((FRONT_PASS_OP) == kKeep_StencilOp && \ michael@0: (BACK_PASS_OP) == kKeep_StencilOp && \ michael@0: (FRONT_FAIL_OP) == kKeep_StencilOp && \ michael@0: (BACK_FAIL_OP) == kKeep_StencilOp && \ michael@0: (FRONT_FUNC) == kAlways_StencilFunc && \ michael@0: (BACK_FUNC) == kAlways_StencilFunc) michael@0: michael@0: #define GR_STENCIL_SETTINGS_DOES_WRITE( \ michael@0: FRONT_PASS_OP, BACK_PASS_OP, \ michael@0: FRONT_FAIL_OP, BACK_FAIL_OP, \ michael@0: FRONT_FUNC, BACK_FUNC) \ michael@0: (!(((FRONT_FUNC) == kNever_StencilFunc || \ michael@0: (FRONT_PASS_OP) == kKeep_StencilOp) && \ michael@0: ((BACK_FUNC) == kNever_StencilFunc || \ michael@0: (BACK_PASS_OP) == kKeep_StencilOp) && \ michael@0: ((FRONT_FUNC) == kAlways_StencilFunc || \ michael@0: (FRONT_FAIL_OP) == kKeep_StencilOp) && \ michael@0: ((BACK_FUNC) == kAlways_StencilFunc || \ michael@0: (BACK_FAIL_OP) == kKeep_StencilOp))) michael@0: michael@0: #define GR_STENCIL_SETTINGS_DEFAULT_FLAGS( \ michael@0: FRONT_PASS_OP, BACK_PASS_OP, \ michael@0: FRONT_FAIL_OP, BACK_FAIL_OP, \ michael@0: FRONT_FUNC, BACK_FUNC) \ michael@0: ((GR_STENCIL_SETTINGS_IS_DISABLED(FRONT_PASS_OP,BACK_PASS_OP, \ michael@0: FRONT_FAIL_OP,BACK_FAIL_OP,FRONT_FUNC,BACK_FUNC) ? \ michael@0: kIsDisabled_StencilFlag : kNotDisabled_StencilFlag) | \ michael@0: (GR_STENCIL_SETTINGS_DOES_WRITE(FRONT_PASS_OP,BACK_PASS_OP, \ michael@0: FRONT_FAIL_OP,BACK_FAIL_OP,FRONT_FUNC,BACK_FUNC) ? \ michael@0: kDoesWrite_StencilFlag : kDoesNotWrite_StencilFlag)) michael@0: michael@0: /** michael@0: * Class representing stencil state. michael@0: */ michael@0: class GrStencilSettings : private GrStencilSettingsStruct { michael@0: michael@0: public: michael@0: enum Face { michael@0: kFront_Face = 0, michael@0: kBack_Face = 1, michael@0: }; michael@0: michael@0: GrStencilSettings() { michael@0: fPad0 = fPad1 = 0; michael@0: this->setDisabled(); michael@0: } michael@0: michael@0: GrStencilOp passOp(Face f) const { return static_cast(fPassOps[f]); } michael@0: GrStencilOp failOp(Face f) const { return static_cast(fFailOps[f]); } michael@0: GrStencilFunc func(Face f) const { return static_cast(fFuncs[f]); } michael@0: uint16_t funcMask(Face f) const { return fFuncMasks[f]; } michael@0: uint16_t funcRef(Face f) const { return fFuncRefs[f]; } michael@0: uint16_t writeMask(Face f) const { return fWriteMasks[f]; } michael@0: michael@0: void setPassOp(Face f, GrStencilOp op) { fPassOps[f] = op; fFlags = 0;} michael@0: void setFailOp(Face f, GrStencilOp op) { fFailOps[f] = op; fFlags = 0;} michael@0: void setFunc(Face f, GrStencilFunc func) { fFuncs[f] = func; fFlags = 0;} michael@0: void setFuncMask(Face f, unsigned short mask) { fFuncMasks[f] = mask; } michael@0: void setFuncRef(Face f, unsigned short ref) { fFuncRefs[f] = ref; } michael@0: void setWriteMask(Face f, unsigned short writeMask) { fWriteMasks[f] = writeMask; } michael@0: michael@0: void copyFrontSettingsToBack() { michael@0: fPassOps[kBack_Face] = fPassOps[kFront_Face]; michael@0: fFailOps[kBack_Face] = fFailOps[kFront_Face]; michael@0: fFuncs[kBack_Face] = fFuncs[kFront_Face]; michael@0: fFuncMasks[kBack_Face] = fFuncMasks[kFront_Face]; michael@0: fFuncRefs[kBack_Face] = fFuncRefs[kFront_Face]; michael@0: fWriteMasks[kBack_Face] = fWriteMasks[kFront_Face]; michael@0: fFlags = 0; michael@0: } michael@0: michael@0: void setSame(GrStencilOp passOp, michael@0: GrStencilOp failOp, michael@0: GrStencilFunc func, michael@0: unsigned short funcMask, michael@0: unsigned short funcRef, michael@0: unsigned short writeMask) { michael@0: fPassOps[kFront_Face] = fPassOps[kBack_Face] = passOp; michael@0: fFailOps[kFront_Face] = fFailOps[kBack_Face] = failOp; michael@0: fFuncs[kFront_Face] = fFuncs[kBack_Face] = func; michael@0: fFuncMasks[kFront_Face] = fFuncMasks[kBack_Face] = funcMask; michael@0: fFuncRefs[kFront_Face] = fFuncRefs[kBack_Face] = funcRef; michael@0: fWriteMasks[kFront_Face] = fWriteMasks[kBack_Face] = writeMask; michael@0: fFlags = 0; michael@0: } michael@0: michael@0: void setDisabled() { michael@0: memset(this, 0, sizeof(*this)); michael@0: GR_STATIC_ASSERT(0 == kKeep_StencilOp); michael@0: GR_STATIC_ASSERT(0 == kAlways_StencilFunc); michael@0: fFlags = kIsDisabled_StencilFlag | kDoesNotWrite_StencilFlag; michael@0: } michael@0: michael@0: bool isTwoSided() const { michael@0: return fPassOps[kFront_Face] != fPassOps[kBack_Face] || michael@0: fFailOps[kFront_Face] != fFailOps[kBack_Face] || michael@0: fFuncs[kFront_Face] != fFuncs[kBack_Face] || michael@0: fFuncMasks[kFront_Face] != fFuncMasks[kBack_Face] || michael@0: fFuncRefs[kFront_Face] != fFuncRefs[kBack_Face] || michael@0: fWriteMasks[kFront_Face] != fWriteMasks[kBack_Face]; michael@0: } michael@0: michael@0: bool usesWrapOp() const { michael@0: return kIncWrap_StencilOp == fPassOps[kFront_Face] || michael@0: kDecWrap_StencilOp == fPassOps[kFront_Face] || michael@0: kIncWrap_StencilOp == fPassOps[kBack_Face] || michael@0: kDecWrap_StencilOp == fPassOps[kBack_Face] || michael@0: kIncWrap_StencilOp == fFailOps[kFront_Face] || michael@0: kDecWrap_StencilOp == fFailOps[kFront_Face] || michael@0: kIncWrap_StencilOp == fFailOps[kBack_Face] || michael@0: kDecWrap_StencilOp == fFailOps[kBack_Face]; michael@0: } michael@0: michael@0: bool isDisabled() const { michael@0: if (fFlags & kIsDisabled_StencilFlag) { michael@0: return true; michael@0: } michael@0: if (fFlags & kNotDisabled_StencilFlag) { michael@0: return false; michael@0: } michael@0: bool disabled = GR_STENCIL_SETTINGS_IS_DISABLED( michael@0: fPassOps[kFront_Face], fPassOps[kBack_Face], michael@0: fFailOps[kFront_Face], fFailOps[kBack_Face], michael@0: fFuncs[kFront_Face], fFuncs[kBack_Face]); michael@0: fFlags |= disabled ? kIsDisabled_StencilFlag : kNotDisabled_StencilFlag; michael@0: return disabled; michael@0: } michael@0: michael@0: bool doesWrite() const { michael@0: if (fFlags & kDoesWrite_StencilFlag) { michael@0: return true; michael@0: } michael@0: if (fFlags & kDoesNotWrite_StencilFlag) { michael@0: return false; michael@0: } michael@0: bool writes = GR_STENCIL_SETTINGS_DOES_WRITE( michael@0: fPassOps[kFront_Face], fPassOps[kBack_Face], michael@0: fFailOps[kFront_Face], fFailOps[kBack_Face], michael@0: fFuncs[kFront_Face], fFuncs[kBack_Face]); michael@0: fFlags |= writes ? kDoesWrite_StencilFlag : kDoesNotWrite_StencilFlag; michael@0: return writes; michael@0: } michael@0: michael@0: void invalidate() { michael@0: // write an illegal value to the first member michael@0: fPassOps[0] = (GrStencilOp)(uint8_t)-1; michael@0: fFlags = 0; michael@0: } michael@0: michael@0: bool operator == (const GrStencilSettings& s) const { michael@0: static const size_t gCompareSize = sizeof(GrStencilSettings) - michael@0: sizeof(fFlags); michael@0: SkASSERT((const char*)&fFlags + sizeof(fFlags) == michael@0: (const char*)this + sizeof(GrStencilSettings)); michael@0: if (this->isDisabled() & s.isDisabled()) { // using & not && michael@0: return true; michael@0: } michael@0: return 0 == memcmp(this, &s, gCompareSize); michael@0: } michael@0: michael@0: bool operator != (const GrStencilSettings& s) const { michael@0: return !(*this == s); michael@0: } michael@0: michael@0: GrStencilSettings& operator =(const GrStencilSettings& s) { michael@0: memcpy(this, &s, sizeof(GrStencilSettings)); michael@0: return *this; michael@0: } michael@0: michael@0: private: michael@0: friend class GrClipMaskManager; michael@0: michael@0: enum { michael@0: kMaxStencilClipPasses = 2 // maximum number of passes to add a clip michael@0: // element to the stencil buffer. michael@0: }; michael@0: michael@0: /** michael@0: * Given a thing to draw into the stencil clip, a fill type, and a set op michael@0: * this function determines: michael@0: * 1. Whether the thing can be draw directly to the stencil clip or michael@0: * needs to be drawn to the client portion of the stencil first. michael@0: * 2. How many passes are needed. michael@0: * 3. What those passes are. michael@0: * 4. The fill rule that should actually be used to render (will michael@0: * always be non-inverted). michael@0: * michael@0: * @param op the set op to combine this element with the michael@0: * existing clip michael@0: * @param stencilClipMask mask with just the stencil bit used for clipping michael@0: * enabled. michael@0: * @param invertedFill is this path inverted michael@0: * @param numPasses out: the number of passes needed to add the michael@0: * element to the clip. michael@0: * @param settings out: the stencil settings to use for each pass michael@0: * michael@0: * @return true if the clip element's geometry can be drawn directly to the michael@0: * stencil clip bit. Will only be true if canBeDirect is true. michael@0: * numPasses will be 1 if return value is true. michael@0: */ michael@0: static bool GetClipPasses(SkRegion::Op op, michael@0: bool canBeDirect, michael@0: unsigned int stencilClipMask, michael@0: bool invertedFill, michael@0: int* numPasses, michael@0: GrStencilSettings settings[kMaxStencilClipPasses]); michael@0: }; michael@0: michael@0: GR_STATIC_ASSERT(sizeof(GrStencilSettingsStruct) == sizeof(GrStencilSettings)); michael@0: michael@0: #define GR_STATIC_CONST_STENCIL_STRUCT(STRUCT_NAME, \ michael@0: FRONT_PASS_OP, BACK_PASS_OP, \ michael@0: FRONT_FAIL_OP, BACK_FAIL_OP, \ michael@0: FRONT_FUNC, BACK_FUNC, \ michael@0: FRONT_MASK, BACK_MASK, \ michael@0: FRONT_REF, BACK_REF, \ michael@0: FRONT_WRITE_MASK, BACK_WRITE_MASK) \ michael@0: static const GrStencilSettingsStruct STRUCT_NAME = { \ michael@0: {(FRONT_PASS_OP), (BACK_PASS_OP) }, \ michael@0: {(FRONT_FAIL_OP), (BACK_FAIL_OP) }, \ michael@0: {(FRONT_FUNC), (BACK_FUNC) }, \ michael@0: (0), (0), \ michael@0: {(FRONT_MASK), (BACK_MASK) }, \ michael@0: {(FRONT_REF), (BACK_REF) }, \ michael@0: {(FRONT_WRITE_MASK), (BACK_WRITE_MASK)}, \ michael@0: GR_STENCIL_SETTINGS_DEFAULT_FLAGS( \ michael@0: FRONT_PASS_OP, BACK_PASS_OP, FRONT_FAIL_OP, BACK_FAIL_OP, \ michael@0: FRONT_FUNC, BACK_FUNC) \ michael@0: }; michael@0: michael@0: #define GR_CONST_STENCIL_SETTINGS_PTR_FROM_STRUCT_PTR(STRUCT_PTR) \ michael@0: reinterpret_cast(STRUCT_PTR) michael@0: michael@0: #define GR_STATIC_CONST_SAME_STENCIL_STRUCT(STRUCT_NAME, \ michael@0: PASS_OP, FAIL_OP, FUNC, MASK, REF, WRITE_MASK) \ michael@0: GR_STATIC_CONST_STENCIL_STRUCT(STRUCT_NAME, (PASS_OP), (PASS_OP), \ michael@0: (FAIL_OP),(FAIL_OP), (FUNC), (FUNC), (MASK), (MASK), (REF), (REF), \ michael@0: (WRITE_MASK),(WRITE_MASK)) michael@0: michael@0: #define GR_STATIC_CONST_STENCIL(NAME, \ michael@0: FRONT_PASS_OP, BACK_PASS_OP, \ michael@0: FRONT_FAIL_OP, BACK_FAIL_OP, \ michael@0: FRONT_FUNC, BACK_FUNC, \ michael@0: FRONT_MASK, BACK_MASK, \ michael@0: FRONT_REF, BACK_REF, \ michael@0: FRONT_WRITE_MASK, BACK_WRITE_MASK) \ michael@0: GR_STATIC_CONST_STENCIL_STRUCT(NAME ## _STRUCT, \ michael@0: (FRONT_PASS_OP),(BACK_PASS_OP),(FRONT_FAIL_OP),(BACK_FAIL_OP), \ michael@0: (FRONT_FUNC),(BACK_FUNC),(FRONT_MASK),(BACK_MASK), \ michael@0: (FRONT_REF),(BACK_REF),(FRONT_WRITE_MASK),(BACK_WRITE_MASK)) \ michael@0: static const GrStencilSettings& NAME = \ michael@0: *GR_CONST_STENCIL_SETTINGS_PTR_FROM_STRUCT_PTR(&(NAME ## _STRUCT)); michael@0: michael@0: michael@0: #define GR_STATIC_CONST_SAME_STENCIL(NAME, \ michael@0: PASS_OP, FAIL_OP, FUNC, MASK, REF, WRITE_MASK) \ michael@0: GR_STATIC_CONST_STENCIL(NAME, (PASS_OP), (PASS_OP), (FAIL_OP), \ michael@0: (FAIL_OP), (FUNC), (FUNC), (MASK), (MASK), (REF), (REF), (WRITE_MASK), \ michael@0: (WRITE_MASK)) michael@0: michael@0: #endif