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