
/*
 * Copyright 2011 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */


#ifndef GrStencil_DEFINED
#define GrStencil_DEFINED

#include "GrTypes.h"
/**
 * Gr uses the stencil buffer to implement complex clipping inside the
 * GrDrawTarget class. The GrDrawTarget makes a subset of the stencil buffer
 * bits available for other uses by external code (clients). Client code can
 * modify these bits. GrDrawTarget will ignore ref, mask, and writemask bits
 * provided by clients that overlap the bits used to implement clipping.
 *
 * When code outside the GrDrawTarget class uses the stencil buffer the contract
 * is as follows:
 *
 * > Normal stencil funcs allow the client to pass / fail regardless of the
 *   reserved clip bits.
 * > Additional functions allow a test against the clip along with a limited
 *   set of tests against the client bits.
 * > Client can assume all client bits are zero initially.
 * > Client must ensure that after all its passes are finished it has only
 *   written to the color buffer in the region inside the clip. Furthermore, it
 *   must zero all client bits that were modifed (both inside and outside the
 *   clip).
 */

/**
 * Determines which pixels pass / fail the stencil test.
 * Stencil test passes if (ref & mask) FUNC (stencil & mask) is true
 */
enum GrStencilFunc {
    kAlways_StencilFunc = 0,
    kNever_StencilFunc,
    kGreater_StencilFunc,
    kGEqual_StencilFunc,
    kLess_StencilFunc,
    kLEqual_StencilFunc,
    kEqual_StencilFunc,
    kNotEqual_StencilFunc,

    // Gr stores the current clip in the
    // stencil buffer in the high bits that
    // are not directly accessible modifiable
    // via the GrDrawTarget interface. The below
    // stencil funcs test against the current
    // clip in addition to the GrDrawTarget
    // client's stencil bits.

    // pass if inside the clip
    kAlwaysIfInClip_StencilFunc,
    kEqualIfInClip_StencilFunc,
    kLessIfInClip_StencilFunc,
    kLEqualIfInClip_StencilFunc,
    kNonZeroIfInClip_StencilFunc, // this one forces the ref to be 0

    // counts
    kStencilFuncCount,
    kClipStencilFuncCount = kNonZeroIfInClip_StencilFunc -
                            kAlwaysIfInClip_StencilFunc + 1,
    kBasicStencilFuncCount = kStencilFuncCount - kClipStencilFuncCount
};

/**
 * Operations to perform based on whether stencil test passed failed.
 */
enum GrStencilOp {
    kKeep_StencilOp = 0,    // preserve existing stencil value
    kReplace_StencilOp,     // replace with reference value from stencl test
    kIncWrap_StencilOp,     // increment and wrap at max
    kIncClamp_StencilOp,    // increment and clamp at max
    kDecWrap_StencilOp,     // decrement and wrap at 0
    kDecClamp_StencilOp,    // decrement and clamp at 0
    kZero_StencilOp,        // zero stencil bits
    kInvert_StencilOp,      // invert stencil bits

    kStencilOpCount
};

/**
 * Struct representing stencil state.
 */
struct GrStencilSettings {
    GrStencilOp   fFrontPassOp;     // op to perform when front faces pass
    GrStencilOp   fBackPassOp;      // op to perform when back faces pass
    GrStencilOp   fFrontFailOp;     // op to perform when front faces fail
    GrStencilOp   fBackFailOp;      // op to perform when back faces fail
    GrStencilFunc fFrontFunc;       // test function for front faces
    GrStencilFunc fBackFunc;        // test function for back faces
    unsigned int fFrontFuncMask;    // mask for front face test
    unsigned int fBackFuncMask;     // mask for back face test
    unsigned int fFrontFuncRef;     // reference value for front face test
    unsigned int fBackFuncRef;      // reference value for back face test
    unsigned int fFrontWriteMask;   // stencil write mask for front faces
    unsigned int fBackWriteMask;    // stencil write mask for back faces

    bool operator == (const GrStencilSettings& s) const {
        // make sure this is tightly packed.
        GR_STATIC_ASSERT(0 == sizeof(GrStencilOp)%4);
        GR_STATIC_ASSERT(0 == sizeof(GrStencilFunc)%4);
        GR_STATIC_ASSERT(sizeof(GrStencilSettings) ==
                        4*sizeof(GrStencilOp) +
                        2*sizeof(GrStencilFunc) +
                        6*sizeof(unsigned int));
        return 0 == memcmp(this, &s, sizeof(GrStencilSettings));
    }

    bool operator != (const GrStencilSettings& s) const {
        return !(*this == s);
    }

    GrStencilSettings& operator =(const GrStencilSettings& s) {
        memcpy(this, &s, sizeof(GrStencilSettings));
        return *this;
    }

    void setSame(GrStencilOp passOp,
                 GrStencilOp failOp,
                 GrStencilFunc func,
                 unsigned int funcMask,
                 unsigned int funcRef,
                 unsigned int writeMask) {
        fFrontPassOp        = passOp;
        fBackPassOp         = passOp;
        fFrontFailOp        = failOp;
        fBackFailOp         = failOp;
        fFrontFunc          = func;
        fBackFunc           = func;
        fFrontFuncMask      = funcMask;
        fBackFuncMask       = funcMask;
        fFrontFuncRef       = funcRef;
        fBackFuncRef        = funcRef;
        fFrontWriteMask     = writeMask;
        fBackWriteMask      = writeMask;
    }

    // canonical value for disabled stenciling
    static const GrStencilSettings gDisabled;
    void setDisabled() {
        *this = gDisabled;
    }
    bool isDisabled() const {
        return kKeep_StencilOp == fFrontPassOp   &&
               kKeep_StencilOp == fBackPassOp    &&
               kKeep_StencilOp == fFrontFailOp   &&
               kKeep_StencilOp == fBackFailOp   &&
               kAlways_StencilFunc == fFrontFunc &&
               kAlways_StencilFunc == fBackFunc;
    }
    bool doesWrite() const {
        return !((kNever_StencilFunc == fFrontFunc ||
                  kKeep_StencilOp == fFrontPassOp)  &&
                 (kNever_StencilFunc == fBackFunc ||
                  kKeep_StencilOp == fBackPassOp)    &&
                 (kAlways_StencilFunc == fFrontFunc ||
                  kKeep_StencilOp == fFrontFailOp)  &&
                 (kAlways_StencilFunc == fBackFunc ||
                  kKeep_StencilOp == fBackFailOp));
    }
    void invalidate()  {
        // just write an illegal value to the first member
        fFrontPassOp = (GrStencilOp)-1;
    }

private:
    friend class GrGpu;

    enum {
        kMaxStencilClipPasses = 2  // maximum number of passes to add a clip 
                                   // element to the stencil buffer.
    };

    /**
     * Given a thing to draw into the stencil clip, a fill type, and a set op
     * this function determines:
     *      1. Whether the thing can be draw directly to the stencil clip or
     *      needs to be drawn to the client portion of the stencil first.
     *      2. How many passes are needed.
     *      3. What those passes are.
     *      4. The fill rule that should actually be used to render (will 
     *         always be non-inverted).
     *
     * @param op                the set op to combine this element with the 
     *                          existing clip
     * @param stencilClipMask   mask with just the stencil bit used for clipping
     *                          enabled.
     * @param invertedFill      is this path inverted
     * @param numPasses         out: the number of passes needed to add the 
     *                               element to the clip.
     * @param settings          out: the stencil settings to use for each pass
     *
     * @return true if the clip element's geometry can be drawn directly to the
     *         stencil clip bit. Will only be true if canBeDirect is true.
     *         numPasses will be 1 if return value is true.
     */
    static bool GetClipPasses(GrSetOp op, 
                              bool canBeDirect,
                              unsigned int stencilClipMask,
                              bool invertedFill,
                              int* numPasses,
                              GrStencilSettings settings[kMaxStencilClipPasses]);
};

#endif
