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