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