| /* | |
| * 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 "GrStencil.h" | |
| //////////////////////////////////////////////////////////////////////////////// | |
| // Stencil Rules for Merging user stencil space into clip | |
| // We can't include the clip bit in the ref or mask values because the division | |
| // between user and clip bits in the stencil depends on the number of stencil | |
| // bits in the runtime. Comments below indicate what the code should do to | |
| // incorporate the clip bit into these settings. | |
| /////// | |
| // Replace | |
| // set the ref to be the clip bit, but mask it out for the test | |
| GR_STATIC_CONST_SAME_STENCIL(gUserToClipReplace, | |
| kReplace_StencilOp, | |
| kZero_StencilOp, | |
| kLess_StencilFunc, | |
| 0xffff, // unset clip bit | |
| 0x0000, // set clip bit | |
| 0xffff); | |
| GR_STATIC_CONST_SAME_STENCIL(gInvUserToClipReplace, | |
| kReplace_StencilOp, | |
| kZero_StencilOp, | |
| kEqual_StencilFunc, | |
| 0xffff, // unset clip bit | |
| 0x0000, // set clip bit | |
| 0xffff); | |
| /////// | |
| // Intersect | |
| GR_STATIC_CONST_SAME_STENCIL(gUserToClipIsect, | |
| kReplace_StencilOp, | |
| kZero_StencilOp, | |
| kLess_StencilFunc, | |
| 0xffff, | |
| 0x0000, // set clip bit | |
| 0xffff); | |
| GR_STATIC_CONST_SAME_STENCIL(gInvUserToClipIsect, | |
| kReplace_StencilOp, | |
| kZero_StencilOp, | |
| kEqual_StencilFunc, | |
| 0xffff, | |
| 0x0000, // set clip bit | |
| 0xffff); | |
| /////// | |
| // Difference | |
| GR_STATIC_CONST_SAME_STENCIL(gUserToClipDiff, | |
| kReplace_StencilOp, | |
| kZero_StencilOp, | |
| kEqual_StencilFunc, | |
| 0xffff, | |
| 0x0000, // set clip bit | |
| 0xffff); | |
| GR_STATIC_CONST_SAME_STENCIL(gInvUserToClipDiff, | |
| kReplace_StencilOp, | |
| kZero_StencilOp, | |
| kLess_StencilFunc, | |
| 0xffff, | |
| 0x0000, // set clip bit | |
| 0xffff); | |
| /////// | |
| // Union | |
| // first pass makes all the passing cases >= just clip bit set. | |
| GR_STATIC_CONST_SAME_STENCIL(gUserToClipUnionPass0, | |
| kReplace_StencilOp, | |
| kKeep_StencilOp, | |
| kLEqual_StencilFunc, | |
| 0xffff, | |
| 0x0001, // set clip bit | |
| 0xffff); | |
| // second pass allows anything greater than just clip bit set to pass | |
| GR_STATIC_CONST_SAME_STENCIL(gUserToClipUnionPass1, | |
| kReplace_StencilOp, | |
| kZero_StencilOp, | |
| kLEqual_StencilFunc, | |
| 0xffff, | |
| 0x0000, // set clip bit | |
| 0xffff); | |
| // first pass finds zeros in the user bits and if found sets | |
| // the clip bit to 1 | |
| GR_STATIC_CONST_SAME_STENCIL(gInvUserToClipUnionPass0, | |
| kReplace_StencilOp, | |
| kKeep_StencilOp, | |
| kEqual_StencilFunc, | |
| 0xffff, | |
| 0x0000, // set clip bit | |
| 0x0000 // set clip bit | |
| ); | |
| // second pass zeros the user bits | |
| GR_STATIC_CONST_SAME_STENCIL(gInvUserToClipUnionPass1, | |
| kZero_StencilOp, | |
| kZero_StencilOp, | |
| kLess_StencilFunc, | |
| 0xffff, | |
| 0x0000, | |
| 0xffff // unset clip bit | |
| ); | |
| /////// | |
| // Xor | |
| GR_STATIC_CONST_SAME_STENCIL(gUserToClipXorPass0, | |
| kInvert_StencilOp, | |
| kKeep_StencilOp, | |
| kEqual_StencilFunc, | |
| 0xffff, // unset clip bit | |
| 0x0000, | |
| 0xffff); | |
| GR_STATIC_CONST_SAME_STENCIL(gUserToClipXorPass1, | |
| kReplace_StencilOp, | |
| kZero_StencilOp, | |
| kGreater_StencilFunc, | |
| 0xffff, | |
| 0x0000, // set clip bit | |
| 0xffff); | |
| GR_STATIC_CONST_SAME_STENCIL(gInvUserToClipXorPass0, | |
| kInvert_StencilOp, | |
| kKeep_StencilOp, | |
| kEqual_StencilFunc, | |
| 0xffff, // unset clip bit | |
| 0x0000, | |
| 0xffff); | |
| GR_STATIC_CONST_SAME_STENCIL(gInvUserToClipXorPass1, | |
| kReplace_StencilOp, | |
| kZero_StencilOp, | |
| kLess_StencilFunc, | |
| 0xffff, | |
| 0x0000, // set clip bit | |
| 0xffff); | |
| /////// | |
| // Reverse Diff | |
| GR_STATIC_CONST_SAME_STENCIL(gUserToClipRDiffPass0, | |
| kInvert_StencilOp, | |
| kZero_StencilOp, | |
| kLess_StencilFunc, | |
| 0xffff, // unset clip bit | |
| 0x0000, // set clip bit | |
| 0xffff); | |
| GR_STATIC_CONST_SAME_STENCIL(gUserToClipRDiffPass1, | |
| kReplace_StencilOp, | |
| kZero_StencilOp, | |
| kEqual_StencilFunc, | |
| 0x0000, // set clip bit | |
| 0x0000, // set clip bit | |
| 0xffff); | |
| // We are looking for stencil values that are all zero. The first pass sets the | |
| // clip bit if the stencil is all zeros. The second pass clears the user bits. | |
| GR_STATIC_CONST_SAME_STENCIL(gInvUserToClipRDiffPass0, | |
| kInvert_StencilOp, | |
| kZero_StencilOp, | |
| kEqual_StencilFunc, | |
| 0xffff, | |
| 0x0000, | |
| 0x0000 // set clip bit | |
| ); | |
| GR_STATIC_CONST_SAME_STENCIL(gInvUserToClipRDiffPass1, | |
| kZero_StencilOp, | |
| kZero_StencilOp, | |
| kAlways_StencilFunc, | |
| 0xffff, | |
| 0x0000, | |
| 0xffff // unset clip bit | |
| ); | |
| /////// | |
| // Direct to Stencil | |
| // We can render a clip element directly without first writing to the client | |
| // portion of the clip when the fill is not inverse and the set operation will | |
| // only modify the in/out status of samples covered by the clip element. | |
| // this one only works if used right after stencil clip was cleared. | |
| // Our clip mask creation code doesn't allow midstream replace ops. | |
| GR_STATIC_CONST_SAME_STENCIL(gReplaceClip, | |
| kReplace_StencilOp, | |
| kReplace_StencilOp, | |
| kAlways_StencilFunc, | |
| 0xffff, | |
| 0x0000, // set clip bit | |
| 0x0000 // set clipBit | |
| ); | |
| GR_STATIC_CONST_SAME_STENCIL(gUnionClip, | |
| kReplace_StencilOp, | |
| kReplace_StencilOp, | |
| kAlways_StencilFunc, | |
| 0xffff, | |
| 0x0000, // set clip bit | |
| 0x0000 // set clip bit | |
| ); | |
| GR_STATIC_CONST_SAME_STENCIL(gXorClip, | |
| kInvert_StencilOp, | |
| kInvert_StencilOp, | |
| kAlways_StencilFunc, | |
| 0xffff, | |
| 0x0000, | |
| 0x0000 // set clip bit | |
| ); | |
| GR_STATIC_CONST_SAME_STENCIL(gDiffClip, | |
| kZero_StencilOp, | |
| kZero_StencilOp, | |
| kAlways_StencilFunc, | |
| 0xffff, | |
| 0x0000, | |
| 0x0000 // set clip bit | |
| ); | |
| bool GrStencilSettings::GetClipPasses( | |
| SkRegion::Op op, | |
| bool canBeDirect, | |
| unsigned int stencilClipMask, | |
| bool invertedFill, | |
| int* numPasses, | |
| GrStencilSettings settings[kMaxStencilClipPasses]) { | |
| if (canBeDirect && !invertedFill) { | |
| *numPasses = 0; | |
| switch (op) { | |
| case SkRegion::kReplace_Op: | |
| *numPasses = 1; | |
| settings[0] = gReplaceClip; | |
| break; | |
| case SkRegion::kUnion_Op: | |
| *numPasses = 1; | |
| settings[0] = gUnionClip; | |
| break; | |
| case SkRegion::kXOR_Op: | |
| *numPasses = 1; | |
| settings[0] = gXorClip; | |
| break; | |
| case SkRegion::kDifference_Op: | |
| *numPasses = 1; | |
| settings[0] = gDiffClip; | |
| break; | |
| default: // suppress warning | |
| break; | |
| } | |
| if (1 == *numPasses) { | |
| settings[0].fFuncRefs[kFront_Face] |= stencilClipMask; | |
| settings[0].fWriteMasks[kFront_Face] |= stencilClipMask; | |
| settings[0].fFuncRefs[kBack_Face] = | |
| settings[0].fFuncRefs[kFront_Face]; | |
| settings[0].fWriteMasks[kBack_Face] = | |
| settings[0].fWriteMasks[kFront_Face]; | |
| return true; | |
| } | |
| } | |
| switch (op) { | |
| // if we make the path renderer go to stencil we always give it a | |
| // non-inverted fill and we use the stencil rules on the client->clipbit | |
| // pass to select either the zeros or nonzeros. | |
| case SkRegion::kReplace_Op: | |
| *numPasses= 1; | |
| settings[0] = invertedFill ? gInvUserToClipReplace : | |
| gUserToClipReplace; | |
| settings[0].fFuncMasks[kFront_Face] &= ~stencilClipMask; | |
| settings[0].fFuncRefs[kFront_Face] |= stencilClipMask; | |
| settings[0].fFuncMasks[kBack_Face] = | |
| settings[0].fFuncMasks[kFront_Face]; | |
| settings[0].fFuncRefs[kBack_Face] = | |
| settings[0].fFuncRefs[kFront_Face]; | |
| break; | |
| case SkRegion::kIntersect_Op: | |
| *numPasses = 1; | |
| settings[0] = invertedFill ? gInvUserToClipIsect : gUserToClipIsect; | |
| settings[0].fFuncRefs[kFront_Face] = stencilClipMask; | |
| settings[0].fFuncRefs[kBack_Face] = | |
| settings[0].fFuncRefs[kFront_Face]; | |
| break; | |
| case SkRegion::kUnion_Op: | |
| *numPasses = 2; | |
| if (invertedFill) { | |
| settings[0] = gInvUserToClipUnionPass0; | |
| settings[0].fFuncMasks[kFront_Face] &= ~stencilClipMask; | |
| settings[0].fFuncMasks[kBack_Face] = | |
| settings[0].fFuncMasks[kFront_Face]; | |
| settings[0].fFuncRefs[kFront_Face] |= stencilClipMask; | |
| settings[0].fFuncRefs[kBack_Face] = | |
| settings[0].fFuncRefs[kFront_Face]; | |
| settings[0].fWriteMasks[kFront_Face] |= stencilClipMask; | |
| settings[0].fWriteMasks[kBack_Face] = | |
| settings[0].fWriteMasks[kFront_Face]; | |
| settings[1] = gInvUserToClipUnionPass1; | |
| settings[1].fWriteMasks[kFront_Face] &= ~stencilClipMask; | |
| settings[1].fWriteMasks[kBack_Face] &= | |
| settings[1].fWriteMasks[kFront_Face]; | |
| } else { | |
| settings[0] = gUserToClipUnionPass0; | |
| settings[0].fFuncMasks[kFront_Face] &= ~stencilClipMask; | |
| settings[0].fFuncRefs[kFront_Face] |= stencilClipMask; | |
| settings[0].fFuncMasks[kBack_Face] = | |
| settings[0].fFuncMasks[kFront_Face]; | |
| settings[0].fFuncRefs[kBack_Face] = | |
| settings[0].fFuncRefs[kFront_Face]; | |
| settings[1] = gUserToClipUnionPass1; | |
| settings[1].fFuncRefs[kFront_Face] |= stencilClipMask; | |
| settings[1].fFuncRefs[kBack_Face] = | |
| settings[1].fFuncRefs[kFront_Face]; | |
| } | |
| break; | |
| case SkRegion::kXOR_Op: | |
| *numPasses = 2; | |
| if (invertedFill) { | |
| settings[0] = gInvUserToClipXorPass0; | |
| settings[0].fFuncMasks[kFront_Face] &= ~stencilClipMask; | |
| settings[0].fFuncMasks[kBack_Face] = | |
| settings[0].fFuncMasks[kFront_Face]; | |
| settings[1] = gInvUserToClipXorPass1; | |
| settings[1].fFuncRefs[kFront_Face] |= stencilClipMask; | |
| settings[1].fFuncRefs[kBack_Face] = | |
| settings[1].fFuncRefs[kFront_Face]; | |
| } else { | |
| settings[0] = gUserToClipXorPass0; | |
| settings[0].fFuncMasks[kFront_Face] &= ~stencilClipMask; | |
| settings[0].fFuncMasks[kBack_Face] = | |
| settings[0].fFuncMasks[kFront_Face]; | |
| settings[1] = gUserToClipXorPass1; | |
| settings[1].fFuncRefs[kFront_Face] |= stencilClipMask; | |
| settings[1].fFuncRefs[kBack_Face] = | |
| settings[1].fFuncRefs[kFront_Face]; | |
| } | |
| break; | |
| case SkRegion::kDifference_Op: | |
| *numPasses = 1; | |
| settings[0] = invertedFill ? gInvUserToClipDiff : gUserToClipDiff; | |
| settings[0].fFuncRefs[kFront_Face] |= stencilClipMask; | |
| settings[0].fFuncRefs[kBack_Face] = | |
| settings[0].fFuncRefs[kFront_Face]; | |
| break; | |
| case SkRegion::kReverseDifference_Op: | |
| if (invertedFill) { | |
| *numPasses = 2; | |
| settings[0] = gInvUserToClipRDiffPass0; | |
| settings[0].fWriteMasks[kFront_Face] |= stencilClipMask; | |
| settings[0].fWriteMasks[kBack_Face] = | |
| settings[0].fWriteMasks[kFront_Face]; | |
| settings[1] = gInvUserToClipRDiffPass1; | |
| settings[1].fWriteMasks[kFront_Face] &= ~stencilClipMask; | |
| settings[1].fWriteMasks[kBack_Face] = | |
| settings[1].fWriteMasks[kFront_Face]; | |
| } else { | |
| *numPasses = 2; | |
| settings[0] = gUserToClipRDiffPass0; | |
| settings[0].fFuncMasks[kFront_Face] &= ~stencilClipMask; | |
| settings[0].fFuncMasks[kBack_Face] = | |
| settings[0].fFuncMasks[kFront_Face]; | |
| settings[0].fFuncRefs[kFront_Face] |= stencilClipMask; | |
| settings[0].fFuncRefs[kBack_Face] = | |
| settings[0].fFuncRefs[kFront_Face]; | |
| settings[1] = gUserToClipRDiffPass1; | |
| settings[1].fFuncMasks[kFront_Face] |= stencilClipMask; | |
| settings[1].fFuncRefs[kFront_Face] |= stencilClipMask; | |
| settings[1].fFuncMasks[kBack_Face] = | |
| settings[1].fFuncMasks[kFront_Face]; | |
| settings[1].fFuncRefs[kBack_Face] = | |
| settings[1].fFuncRefs[kFront_Face]; | |
| } | |
| break; | |
| default: | |
| SkFAIL("Unknown set op"); | |
| } | |
| return false; | |
| } |