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