summaryrefslogtreecommitdiffstats
path: root/src/mesa/main/stencil.c
diff options
context:
space:
mode:
authorjtg <jtg>1999-08-19 00:55:39 +0000
committerjtg <jtg>1999-08-19 00:55:39 +0000
commitafb833d4e89c312460a4ab9ed6a7a8ca4ebbfe1c (patch)
tree59d65b4da12fb5379224cf5f6b808fde91523c7f /src/mesa/main/stencil.c
parentf2544d4920ce168bec9cd94d774b7ea5103a3d74 (diff)
Initial revision
Diffstat (limited to 'src/mesa/main/stencil.c')
-rw-r--r--src/mesa/main/stencil.c1107
1 files changed, 1107 insertions, 0 deletions
diff --git a/src/mesa/main/stencil.c b/src/mesa/main/stencil.c
new file mode 100644
index 00000000000..69bd70188b1
--- /dev/null
+++ b/src/mesa/main/stencil.c
@@ -0,0 +1,1107 @@
+/* $Id: stencil.c,v 1.1 1999/08/19 00:55:41 jtg Exp $ */
+
+/*
+ * Mesa 3-D graphics library
+ * Version: 3.1
+ *
+ * Copyright (C) 1999 Brian Paul All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+
+#ifdef PC_HEADER
+#include "all.h"
+#else
+#include <stdlib.h>
+#include <string.h>
+#include "context.h"
+#include "macros.h"
+#include "pb.h"
+#include "stencil.h"
+#include "types.h"
+#ifdef XFree86Server
+#include "GL/xf86glx.h"
+#endif
+#endif
+
+
+#if STENCIL_BITS==8
+# define STENCIL_MAX 0xff
+#elif STENCIL_BITS==16
+# define STENCIL_MAX 0xffff
+#else
+ illegal number of stencil bits
+#endif
+
+
+
+/*
+ * Return the address of a stencil buffer value given the window coords:
+ */
+#define STENCIL_ADDRESS(X,Y) (ctx->Buffer->Stencil + ctx->Buffer->Width * (Y) + (X))
+
+
+void gl_ClearStencil( GLcontext *ctx, GLint s )
+{
+ ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx, "glClearStencil");
+ ctx->Stencil.Clear = (GLstencil) s;
+
+ if (ctx->Driver.ClearStencil) {
+ (*ctx->Driver.ClearStencil)( ctx, s );
+ }
+}
+
+
+
+void gl_StencilFunc( GLcontext *ctx, GLenum func, GLint ref, GLuint mask )
+{
+ GLint maxref;
+
+ ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx, "glStencilFunc");
+
+ switch (func) {
+ case GL_NEVER:
+ case GL_LESS:
+ case GL_LEQUAL:
+ case GL_GREATER:
+ case GL_GEQUAL:
+ case GL_EQUAL:
+ case GL_NOTEQUAL:
+ case GL_ALWAYS:
+ ctx->Stencil.Function = func;
+ break;
+ default:
+ gl_error( ctx, GL_INVALID_ENUM, "glStencilFunc" );
+ return;
+ }
+
+ maxref = (1 << STENCIL_BITS) - 1;
+ ctx->Stencil.Ref = CLAMP( ref, 0, maxref );
+ ctx->Stencil.ValueMask = mask;
+
+ if (ctx->Driver.StencilFunc) {
+ (*ctx->Driver.StencilFunc)( ctx, func, ctx->Stencil.Ref, mask );
+ }
+}
+
+
+
+void gl_StencilMask( GLcontext *ctx, GLuint mask )
+{
+ ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx, "glStencilMask");
+ ctx->Stencil.WriteMask = (GLstencil) mask;
+
+ if (ctx->Driver.StencilMask) {
+ (*ctx->Driver.StencilMask)( ctx, mask );
+ }
+}
+
+
+
+void gl_StencilOp( GLcontext *ctx, GLenum fail, GLenum zfail, GLenum zpass )
+{
+ ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx, "glStencilOp");
+ switch (fail) {
+ case GL_KEEP:
+ case GL_ZERO:
+ case GL_REPLACE:
+ case GL_INCR:
+ case GL_DECR:
+ case GL_INVERT:
+ case GL_INCR_WRAP_EXT:
+ case GL_DECR_WRAP_EXT:
+ ctx->Stencil.FailFunc = fail;
+ break;
+ default:
+ gl_error( ctx, GL_INVALID_ENUM, "glStencilOp" );
+ return;
+ }
+ switch (zfail) {
+ case GL_KEEP:
+ case GL_ZERO:
+ case GL_REPLACE:
+ case GL_INCR:
+ case GL_DECR:
+ case GL_INVERT:
+ case GL_INCR_WRAP_EXT:
+ case GL_DECR_WRAP_EXT:
+ ctx->Stencil.ZFailFunc = zfail;
+ break;
+ default:
+ gl_error( ctx, GL_INVALID_ENUM, "glStencilOp" );
+ return;
+ }
+ switch (zpass) {
+ case GL_KEEP:
+ case GL_ZERO:
+ case GL_REPLACE:
+ case GL_INCR:
+ case GL_DECR:
+ case GL_INVERT:
+ case GL_INCR_WRAP_EXT:
+ case GL_DECR_WRAP_EXT:
+ ctx->Stencil.ZPassFunc = zpass;
+ break;
+ default:
+ gl_error( ctx, GL_INVALID_ENUM, "glStencilOp" );
+ return;
+ }
+
+ if (ctx->Driver.StencilOp) {
+ (*ctx->Driver.StencilOp)( ctx, fail, zfail, zpass );
+ }
+}
+
+
+
+/* Stencil Logic:
+
+IF stencil test fails THEN
+ Don't write the pixel (RGBA,Z)
+ Execute FailOp
+ELSE
+ Write the pixel
+ENDIF
+
+Perform Depth Test
+
+IF depth test passes OR no depth buffer THEN
+ Execute ZPass
+ Write the pixel
+ELSE
+ Execute ZFail
+ENDIF
+
+*/
+
+
+
+
+/*
+ * Apply the given stencil operator for each pixel in the span whose
+ * mask flag is set.
+ * Input: n - number of pixels in the span
+ * x, y - location of leftmost pixel in the span
+ * oper - the stencil buffer operator
+ * mask - array [n] of flag: 1=apply operator, 0=don't apply operator
+ */
+static void apply_stencil_op_to_span( GLcontext *ctx,
+ GLuint n, GLint x, GLint y,
+ GLenum oper, GLubyte mask[] )
+{
+ const GLstencil ref = ctx->Stencil.Ref;
+ const GLstencil wrtmask = ctx->Stencil.WriteMask;
+ const GLstencil invmask = ~ctx->Stencil.WriteMask;
+ GLstencil *stencil = STENCIL_ADDRESS( x, y );
+ GLuint i;
+
+ switch (oper) {
+ case GL_KEEP:
+ /* do nothing */
+ break;
+ case GL_ZERO:
+ if (invmask==0) {
+ for (i=0;i<n;i++) {
+ if (mask[i]) {
+ stencil[i] = 0;
+ }
+ }
+ }
+ else {
+ for (i=0;i<n;i++) {
+ if (mask[i]) {
+ stencil[i] = stencil[i] & invmask;
+ }
+ }
+ }
+ break;
+ case GL_REPLACE:
+ if (invmask==0) {
+ for (i=0;i<n;i++) {
+ if (mask[i]) {
+ stencil[i] = ref;
+ }
+ }
+ }
+ else {
+ for (i=0;i<n;i++) {
+ if (mask[i]) {
+ GLstencil s = stencil[i];
+ stencil[i] = (invmask & s ) | (wrtmask & ref);
+ }
+ }
+ }
+ break;
+ case GL_INCR:
+ if (invmask==0) {
+ for (i=0;i<n;i++) {
+ if (mask[i]) {
+ GLstencil s = stencil[i];
+ if (s < STENCIL_MAX) {
+ stencil[i] = s+1;
+ }
+ }
+ }
+ }
+ else {
+ for (i=0;i<n;i++) {
+ if (mask[i]) {
+ /* VERIFY logic of adding 1 to a write-masked value */
+ GLstencil s = stencil[i];
+ if (s < STENCIL_MAX) {
+ stencil[i] = (invmask & s) | (wrtmask & (s+1));
+ }
+ }
+ }
+ }
+ break;
+ case GL_DECR:
+ if (invmask==0) {
+ for (i=0;i<n;i++) {
+ if (mask[i]) {
+ GLstencil s = stencil[i];
+ if (s>0) {
+ stencil[i] = s-1;
+ }
+ }
+ }
+ }
+ else {
+ for (i=0;i<n;i++) {
+ if (mask[i]) {
+ /* VERIFY logic of subtracting 1 to a write-masked value */
+ GLstencil s = stencil[i];
+ if (s>0) {
+ stencil[i] = (invmask & s) | (wrtmask & (s-1));
+ }
+ }
+ }
+ }
+ break;
+ case GL_INCR_WRAP_EXT:
+ if (invmask==0) {
+ for (i=0;i<n;i++) {
+ if (mask[i]) {
+ stencil[i]++;
+ }
+ }
+ }
+ else {
+ for (i=0;i<n;i++) {
+ if (mask[i]) {
+ GLstencil s = stencil[i];
+ stencil[i] = (invmask & s) | (wrtmask & (stencil[i]+1));
+ }
+ }
+ }
+ break;
+ case GL_DECR_WRAP_EXT:
+ if (invmask==0) {
+ for (i=0;i<n;i++) {
+ if (mask[i]) {
+ stencil[i]--;
+ }
+ }
+ }
+ else {
+ for (i=0;i<n;i++) {
+ if (mask[i]) {
+ GLstencil s = stencil[i];
+ stencil[i] = (invmask & s) | (wrtmask & (stencil[i]-1));
+ }
+ }
+ }
+ break;
+ case GL_INVERT:
+ if (invmask==0) {
+ for (i=0;i<n;i++) {
+ if (mask[i]) {
+ GLstencil s = stencil[i];
+ stencil[i] = ~s;
+ }
+ }
+ }
+ else {
+ for (i=0;i<n;i++) {
+ if (mask[i]) {
+ GLstencil s = stencil[i];
+ stencil[i] = (invmask & s) | (wrtmask & ~s);
+ }
+ }
+ }
+ break;
+ default:
+ gl_problem(ctx, "Bad stencilop in apply_stencil_op_to_span");
+ }
+}
+
+
+
+
+/*
+ * Apply stencil test to a span of pixels before depth buffering.
+ * Input: n - number of pixels in the span
+ * x, y - coordinate of left-most pixel in the span
+ * mask - array [n] of flag: 0=skip the pixel, 1=stencil the pixel
+ * Output: mask - pixels which fail the stencil test will have their
+ * mask flag set to 0.
+ * Return: 0 = all pixels failed, 1 = zero or more pixels passed.
+ */
+GLint gl_stencil_span( GLcontext *ctx,
+ GLuint n, GLint x, GLint y, GLubyte mask[] )
+{
+ GLubyte fail[MAX_WIDTH];
+ GLint allfail = 0;
+ GLuint i;
+ GLstencil r, s;
+ GLstencil *stencil;
+
+ stencil = STENCIL_ADDRESS( x, y );
+
+ /*
+ * Perform stencil test. The results of this operation are stored
+ * in the fail[] array:
+ * IF fail[i] is non-zero THEN
+ * the stencil fail operator is to be applied
+ * ELSE
+ * the stencil fail operator is not to be applied
+ * ENDIF
+ */
+ switch (ctx->Stencil.Function) {
+ case GL_NEVER:
+ /* always fail */
+ for (i=0;i<n;i++) {
+ if (mask[i]) {
+ mask[i] = 0;
+ fail[i] = 1;
+ }
+ else {
+ fail[i] = 0;
+ }
+ }
+ allfail = 1;
+ break;
+ case GL_LESS:
+ r = ctx->Stencil.Ref & ctx->Stencil.ValueMask;
+ for (i=0;i<n;i++) {
+ if (mask[i]) {
+ s = stencil[i] & ctx->Stencil.ValueMask;
+ if (r < s) {
+ /* passed */
+ fail[i] = 0;
+ }
+ else {
+ fail[i] = 1;
+ mask[i] = 0;
+ }
+ }
+ else {
+ fail[i] = 0;
+ }
+ }
+ break;
+ case GL_LEQUAL:
+ r = ctx->Stencil.Ref & ctx->Stencil.ValueMask;
+ for (i=0;i<n;i++) {
+ if (mask[i]) {
+ s = stencil[i] & ctx->Stencil.ValueMask;
+ if (r <= s) {
+ /* pass */
+ fail[i] = 0;
+ }
+ else {
+ fail[i] = 1;
+ mask[i] = 0;
+ }
+ }
+ else {
+ fail[i] = 0;
+ }
+ }
+ break;
+ case GL_GREATER:
+ r = ctx->Stencil.Ref & ctx->Stencil.ValueMask;
+ for (i=0;i<n;i++) {
+ if (mask[i]) {
+ s = stencil[i] & ctx->Stencil.ValueMask;
+ if (r > s) {
+ /* passed */
+ fail[i] = 0;
+ }
+ else {
+ fail[i] = 1;
+ mask[i] = 0;
+ }
+ }
+ else {
+ fail[i] = 0;
+ }
+ }
+ break;
+ case GL_GEQUAL:
+ r = ctx->Stencil.Ref & ctx->Stencil.ValueMask;
+ for (i=0;i<n;i++) {
+ if (mask[i]) {
+ s = stencil[i] & ctx->Stencil.ValueMask;
+ if (r >= s) {
+ /* passed */
+ fail[i] = 0;
+ }
+ else {
+ fail[i] = 1;
+ mask[i] = 0;
+ }
+ }
+ else {
+ fail[i] = 0;
+ }
+ }
+ break;
+ case GL_EQUAL:
+ r = ctx->Stencil.Ref & ctx->Stencil.ValueMask;
+ for (i=0;i<n;i++) {
+ if (mask[i]) {
+ s = stencil[i] & ctx->Stencil.ValueMask;
+ if (r == s) {
+ /* passed */
+ fail[i] = 0;
+ }
+ else {
+ fail[i] = 1;
+ mask[i] = 0;
+ }
+ }
+ else {
+ fail[i] = 0;
+ }
+ }
+ break;
+ case GL_NOTEQUAL:
+ r = ctx->Stencil.Ref & ctx->Stencil.ValueMask;
+ for (i=0;i<n;i++) {
+ if (mask[i]) {
+ s = stencil[i] & ctx->Stencil.ValueMask;
+ if (r != s) {
+ /* passed */
+ fail[i] = 0;
+ }
+ else {
+ fail[i] = 1;
+ mask[i] = 0;
+ }
+ }
+ else {
+ fail[i] = 0;
+ }
+ }
+ break;
+ case GL_ALWAYS:
+ /* always pass */
+ for (i=0;i<n;i++) {
+ fail[i] = 0;
+ }
+ break;
+ default:
+ gl_problem(ctx, "Bad stencil func in gl_stencil_span");
+ return 0;
+ }
+
+ apply_stencil_op_to_span( ctx, n, x, y, ctx->Stencil.FailFunc, fail );
+
+ return (allfail) ? 0 : 1;
+}
+
+
+
+
+/*
+ * Apply the combination depth-buffer/stencil operator to a span of pixels.
+ * Input: n - number of pixels in the span
+ * x, y - location of leftmost pixel in span
+ * z - array [n] of z values
+ * Input: mask - array [n] of flags (1=test this pixel, 0=skip the pixel)
+ * Output: mask - array [n] of flags (1=depth test passed, 0=failed)
+ */
+void gl_depth_stencil_span( GLcontext *ctx,
+ GLuint n, GLint x, GLint y, const GLdepth z[],
+ GLubyte mask[] )
+{
+ if (ctx->Depth.Test==GL_FALSE) {
+ /*
+ * No depth buffer, just apply zpass stencil function to active pixels.
+ */
+ apply_stencil_op_to_span( ctx, n, x, y, ctx->Stencil.ZPassFunc, mask );
+ }
+ else {
+ /*
+ * Perform depth buffering, then apply zpass or zfail stencil function.
+ */
+ GLubyte passmask[MAX_WIDTH], failmask[MAX_WIDTH], oldmask[MAX_WIDTH];
+ GLuint i;
+
+ /* init pass and fail masks to zero, copy mask[] to oldmask[] */
+ for (i=0;i<n;i++) {
+ passmask[i] = failmask[i] = 0;
+ oldmask[i] = mask[i];
+ }
+
+ /* apply the depth test */
+ if (ctx->Driver.DepthTestSpan)
+ (*ctx->Driver.DepthTestSpan)( ctx, n, x, y, z, mask );
+
+ /* set the stencil pass/fail flags according to result of depth test */
+ for (i=0;i<n;i++) {
+ if (oldmask[i]) {
+ if (mask[i]) {
+ passmask[i] = 1;
+ }
+ else {
+ failmask[i] = 1;
+ }
+ }
+ }
+
+ /* apply the pass and fail operations */
+ apply_stencil_op_to_span( ctx, n, x, y, ctx->Stencil.ZFailFunc, failmask );
+ apply_stencil_op_to_span( ctx, n, x, y, ctx->Stencil.ZPassFunc, passmask );
+ }
+}
+
+
+
+
+/*
+ * Apply the given stencil operator for each pixel in the array whose
+ * mask flag is set.
+ * Input: n - number of pixels in the span
+ * x, y - array of [n] pixels
+ * operator - the stencil buffer operator
+ * mask - array [n] of flag: 1=apply operator, 0=don't apply operator
+ */
+static void apply_stencil_op_to_pixels( GLcontext *ctx,
+ GLuint n, const GLint x[],
+ const GLint y[],
+ GLenum oper, GLubyte mask[] )
+{
+ GLuint i;
+ GLstencil ref;
+ GLstencil wrtmask, invmask;
+
+ wrtmask = ctx->Stencil.WriteMask;
+ invmask = ~ctx->Stencil.WriteMask;
+
+ ref = ctx->Stencil.Ref;
+
+ switch (oper) {
+ case GL_KEEP:
+ /* do nothing */
+ break;
+ case GL_ZERO:
+ if (invmask==0) {
+ for (i=0;i<n;i++) {
+ if (mask[i]) {
+ GLstencil *sptr = STENCIL_ADDRESS( x[i], y[i] );
+ *sptr = 0;
+ }
+ }
+ }
+ else {
+ for (i=0;i<n;i++) {
+ if (mask[i]) {
+ GLstencil *sptr = STENCIL_ADDRESS( x[i], y[i] );
+ *sptr = invmask & *sptr;
+ }
+ }
+ }
+ break;
+ case GL_REPLACE:
+ if (invmask==0) {
+ for (i=0;i<n;i++) {
+ if (mask[i]) {
+ GLstencil *sptr = STENCIL_ADDRESS( x[i], y[i] );
+ *sptr = ref;
+ }
+ }
+ }
+ else {
+ for (i=0;i<n;i++) {
+ if (mask[i]) {
+ GLstencil *sptr = STENCIL_ADDRESS( x[i], y[i] );
+ *sptr = (invmask & *sptr ) | (wrtmask & ref);
+ }
+ }
+ }
+ break;
+ case GL_INCR:
+ if (invmask==0) {
+ for (i=0;i<n;i++) {
+ if (mask[i]) {
+ GLstencil *sptr = STENCIL_ADDRESS( x[i], y[i] );
+ if (*sptr < STENCIL_MAX) {
+ *sptr = *sptr + 1;
+ }
+ }
+ }
+ }
+ else {
+ for (i=0;i<n;i++) {
+ if (mask[i]) {
+ GLstencil *sptr = STENCIL_ADDRESS( x[i], y[i] );
+ if (*sptr < STENCIL_MAX) {
+ *sptr = (invmask & *sptr) | (wrtmask & (*sptr+1));
+ }
+ }
+ }
+ }
+ break;
+ case GL_DECR:
+ if (invmask==0) {
+ for (i=0;i<n;i++) {
+ if (mask[i]) {
+ GLstencil *sptr = STENCIL_ADDRESS( x[i], y[i] );
+ if (*sptr>0) {
+ *sptr = *sptr - 1;
+ }
+ }
+ }
+ }
+ else {
+ for (i=0;i<n;i++) {
+ if (mask[i]) {
+ GLstencil *sptr = STENCIL_ADDRESS( x[i], y[i] );
+ if (*sptr>0) {
+ *sptr = (invmask & *sptr) | (wrtmask & (*sptr-1));
+ }
+ }
+ }
+ }
+ break;
+ case GL_INCR_WRAP_EXT:
+ if (invmask==0) {
+ for (i=0;i<n;i++) {
+ if (mask[i]) {
+ GLstencil *sptr = STENCIL_ADDRESS( x[i], y[i] );
+ *sptr = *sptr + 1;
+ }
+ }
+ }
+ else {
+ for (i=0;i<n;i++) {
+ if (mask[i]) {
+ GLstencil *sptr = STENCIL_ADDRESS( x[i], y[i] );
+ *sptr = (invmask & *sptr) | (wrtmask & (*sptr+1));
+ }
+ }
+ }
+ break;
+ case GL_DECR_WRAP_EXT:
+ if (invmask==0) {
+ for (i=0;i<n;i++) {
+ if (mask[i]) {
+ GLstencil *sptr = STENCIL_ADDRESS( x[i], y[i] );
+ *sptr = *sptr - 1;
+ }
+ }
+ }
+ else {
+ for (i=0;i<n;i++) {
+ if (mask[i]) {
+ GLstencil *sptr = STENCIL_ADDRESS( x[i], y[i] );
+ *sptr = (invmask & *sptr) | (wrtmask & (*sptr-1));
+ }
+ }
+ }
+ break;
+ case GL_INVERT:
+ if (invmask==0) {
+ for (i=0;i<n;i++) {
+ if (mask[i]) {
+ GLstencil *sptr = STENCIL_ADDRESS( x[i], y[i] );
+ *sptr = ~*sptr;
+ }
+ }
+ }
+ else {
+ for (i=0;i<n;i++) {
+ if (mask[i]) {
+ GLstencil *sptr = STENCIL_ADDRESS( x[i], y[i] );
+ *sptr = (invmask & *sptr) | (wrtmask & ~*sptr);
+ }
+ }
+ }
+ break;
+ default:
+ gl_problem(ctx, "Bad stencilop in apply_stencil_op_to_pixels");
+ }
+}
+
+
+
+/*
+ * Apply stencil test to an array of pixels before depth buffering.
+ * Input: n - number of pixels in the span
+ * x, y - array of [n] pixels to stencil
+ * mask - array [n] of flag: 0=skip the pixel, 1=stencil the pixel
+ * Output: mask - pixels which fail the stencil test will have their
+ * mask flag set to 0.
+ * Return: 0 = all pixels failed, 1 = zero or more pixels passed.
+ */
+GLint gl_stencil_pixels( GLcontext *ctx,
+ GLuint n, const GLint x[], const GLint y[],
+ GLubyte mask[] )
+{
+ GLubyte fail[PB_SIZE];
+ GLstencil r, s;
+ GLuint i;
+ GLint allfail = 0;
+
+ /*
+ * Perform stencil test. The results of this operation are stored
+ * in the fail[] array:
+ * IF fail[i] is non-zero THEN
+ * the stencil fail operator is to be applied
+ * ELSE
+ * the stencil fail operator is not to be applied
+ * ENDIF
+ */
+
+ switch (ctx->Stencil.Function) {
+ case GL_NEVER:
+ /* always fail */
+ for (i=0;i<n;i++) {
+ if (mask[i]) {
+ mask[i] = 0;
+ fail[i] = 1;
+ }
+ else {
+ fail[i] = 0;
+ }
+ }
+ allfail = 1;
+ break;
+ case GL_LESS:
+ r = ctx->Stencil.Ref & ctx->Stencil.ValueMask;
+ for (i=0;i<n;i++) {
+ if (mask[i]) {
+ GLstencil *sptr = STENCIL_ADDRESS(x[i],y[i]);
+ s = *sptr & ctx->Stencil.ValueMask;
+ if (r < s) {
+ /* passed */
+ fail[i] = 0;
+ }
+ else {
+ fail[i] = 1;
+ mask[i] = 0;
+ }
+ }
+ else {
+ fail[i] = 0;
+ }
+ }
+ break;
+ case GL_LEQUAL:
+ r = ctx->Stencil.Ref & ctx->Stencil.ValueMask;
+ for (i=0;i<n;i++) {
+ if (mask[i]) {
+ GLstencil *sptr = STENCIL_ADDRESS(x[i],y[i]);
+ s = *sptr & ctx->Stencil.ValueMask;
+ if (r <= s) {
+ /* pass */
+ fail[i] = 0;
+ }
+ else {
+ fail[i] = 1;
+ mask[i] = 0;
+ }
+ }
+ else {
+ fail[i] = 0;
+ }
+ }
+ break;
+ case GL_GREATER:
+ r = ctx->Stencil.Ref & ctx->Stencil.ValueMask;
+ for (i=0;i<n;i++) {
+ if (mask[i]) {
+ GLstencil *sptr = STENCIL_ADDRESS(x[i],y[i]);
+ s = *sptr & ctx->Stencil.ValueMask;
+ if (r > s) {
+ /* passed */
+ fail[i] = 0;
+ }
+ else {
+ fail[i] = 1;
+ mask[i] = 0;
+ }
+ }
+ else {
+ fail[i] = 0;
+ }
+ }
+ break;
+ case GL_GEQUAL:
+ r = ctx->Stencil.Ref & ctx->Stencil.ValueMask;
+ for (i=0;i<n;i++) {
+ if (mask[i]) {
+ GLstencil *sptr = STENCIL_ADDRESS(x[i],y[i]);
+ s = *sptr & ctx->Stencil.ValueMask;
+ if (r >= s) {
+ /* passed */
+ fail[i] = 0;
+ }
+ else {
+ fail[i] = 1;
+ mask[i] = 0;
+ }
+ }
+ else {
+ fail[i] = 0;
+ }
+ }
+ break;
+ case GL_EQUAL:
+ r = ctx->Stencil.Ref & ctx->Stencil.ValueMask;
+ for (i=0;i<n;i++) {
+ if (mask[i]) {
+ GLstencil *sptr = STENCIL_ADDRESS(x[i],y[i]);
+ s = *sptr & ctx->Stencil.ValueMask;
+ if (r == s) {
+ /* passed */
+ fail[i] = 0;
+ }
+ else {
+ fail[i] = 1;
+ mask[i] = 0;
+ }
+ }
+ else {
+ fail[i] = 0;
+ }
+ }
+ break;
+ case GL_NOTEQUAL:
+ r = ctx->Stencil.Ref & ctx->Stencil.ValueMask;
+ for (i=0;i<n;i++) {
+ if (mask[i]) {
+ GLstencil *sptr = STENCIL_ADDRESS(x[i],y[i]);
+ s = *sptr & ctx->Stencil.ValueMask;
+ if (r != s) {
+ /* passed */
+ fail[i] = 0;
+ }
+ else {
+ fail[i] = 1;
+ mask[i] = 0;
+ }
+ }
+ else {
+ fail[i] = 0;
+ }
+ }
+ break;
+ case GL_ALWAYS:
+ /* always pass */
+ for (i=0;i<n;i++) {
+ fail[i] = 0;
+ }
+ break;
+ default:
+ gl_problem(ctx, "Bad stencil func in gl_stencil_pixels");
+ return 0;
+ }
+
+ apply_stencil_op_to_pixels( ctx, n, x, y, ctx->Stencil.FailFunc, fail );
+
+ return (allfail) ? 0 : 1;
+}
+
+
+
+
+/*
+ * Apply the combination depth-buffer/stencil operator to a span of pixels.
+ * Input: n - number of pixels in the span
+ * x, y - array of [n] pixels to stencil
+ * z - array [n] of z values
+ * Input: mask - array [n] of flags (1=test this pixel, 0=skip the pixel)
+ * Output: mask - array [n] of flags (1=depth test passed, 0=failed)
+ */
+void gl_depth_stencil_pixels( GLcontext *ctx,
+ GLuint n, const GLint x[], const GLint y[],
+ const GLdepth z[], GLubyte mask[] )
+{
+ if (ctx->Depth.Test==GL_FALSE) {
+ /*
+ * No depth buffer, just apply zpass stencil function to active pixels.
+ */
+ apply_stencil_op_to_pixels( ctx, n, x, y, ctx->Stencil.ZPassFunc, mask );
+ }
+ else {
+ /*
+ * Perform depth buffering, then apply zpass or zfail stencil function.
+ */
+ GLubyte passmask[PB_SIZE], failmask[PB_SIZE], oldmask[PB_SIZE];
+ GLuint i;
+
+ /* init pass and fail masks to zero */
+ for (i=0;i<n;i++) {
+ passmask[i] = failmask[i] = 0;
+ oldmask[i] = mask[i];
+ }
+
+ /* apply the depth test */
+ if (ctx->Driver.DepthTestPixels)
+ (*ctx->Driver.DepthTestPixels)( ctx, n, x, y, z, mask );
+
+ /* set the stencil pass/fail flags according to result of depth test */
+ for (i=0;i<n;i++) {
+ if (oldmask[i]) {
+ if (mask[i]) {
+ passmask[i] = 1;
+ }
+ else {
+ failmask[i] = 1;
+ }
+ }
+ }
+
+ /* apply the pass and fail operations */
+ apply_stencil_op_to_pixels( ctx, n, x, y,
+ ctx->Stencil.ZFailFunc, failmask );
+ apply_stencil_op_to_pixels( ctx, n, x, y,
+ ctx->Stencil.ZPassFunc, passmask );
+ }
+
+}
+
+
+
+/*
+ * Return a span of stencil values from the stencil buffer.
+ * Input: n - how many pixels
+ * x,y - location of first pixel
+ * Output: stencil - the array of stencil values
+ */
+void gl_read_stencil_span( GLcontext *ctx,
+ GLuint n, GLint x, GLint y, GLstencil stencil[] )
+{
+ if (ctx->Buffer->Stencil) {
+ const GLstencil *s = STENCIL_ADDRESS( x, y );
+#if STENCIL_BITS == 8
+ MEMCPY( stencil, s, n * sizeof(GLstencil) );
+#else
+ GLuint i;
+ for (i=0;i<n;i++)
+ stencil[i] = s[i];
+#endif
+ }
+}
+
+
+
+/*
+ * Write a span of stencil values to the stencil buffer.
+ * Input: n - how many pixels
+ * x,y - location of first pixel
+ * stencil - the array of stencil values
+ */
+void gl_write_stencil_span( GLcontext *ctx,
+ GLuint n, GLint x, GLint y,
+ const GLstencil stencil[] )
+{
+ if (ctx->Buffer->Stencil) {
+ GLstencil *s = STENCIL_ADDRESS( x, y );
+#if STENCIL_BITS == 8
+ MEMCPY( s, stencil, n * sizeof(GLstencil) );
+#else
+ GLuint i;
+ for (i=0;i<n;i++)
+ s[i] = stencil[i];
+#endif
+ }
+}
+
+
+
+/*
+ * Allocate a new stencil buffer. If there's an old one it will be
+ * deallocated first. The new stencil buffer will be uninitialized.
+ */
+void gl_alloc_stencil_buffer( GLcontext *ctx )
+{
+ GLuint buffersize = ctx->Buffer->Width * ctx->Buffer->Height;
+
+ /* deallocate current stencil buffer if present */
+ if (ctx->Buffer->Stencil) {
+ free(ctx->Buffer->Stencil);
+ ctx->Buffer->Stencil = NULL;
+ }
+
+ /* allocate new stencil buffer */
+ ctx->Buffer->Stencil = (GLstencil *) malloc(buffersize * sizeof(GLstencil));
+ if (!ctx->Buffer->Stencil) {
+ /* out of memory */
+ ctx->Stencil.Enabled = GL_FALSE;
+ gl_error( ctx, GL_OUT_OF_MEMORY, "gl_alloc_stencil_buffer" );
+ }
+}
+
+
+
+
+/*
+ * Clear the stencil buffer. If the stencil buffer doesn't exist yet we'll
+ * allocate it now.
+ */
+void gl_clear_stencil_buffer( GLcontext *ctx )
+{
+ if (ctx->Visual->StencilBits==0 || !ctx->Buffer->Stencil) {
+ /* no stencil buffer */
+ return;
+ }
+
+ if (ctx->Scissor.Enabled) {
+ /* clear scissor region only */
+ GLint y;
+ GLint width = ctx->Buffer->Xmax - ctx->Buffer->Xmin + 1;
+ for (y=ctx->Buffer->Ymin; y<=ctx->Buffer->Ymax; y++) {
+ GLstencil *ptr = STENCIL_ADDRESS( ctx->Buffer->Xmin, y );
+#if STENCIL_BITS==8
+ MEMSET( ptr, ctx->Stencil.Clear, width * sizeof(GLstencil) );
+#else
+ GLint x;
+ for (x = 0; x < width; x++)
+ ptr[x] = ctx->Stencil.Clear;
+#endif
+ }
+ }
+ else {
+ /* clear whole stencil buffer */
+#if STENCIL_BITS==8
+ MEMSET( ctx->Buffer->Stencil, ctx->Stencil.Clear,
+ ctx->Buffer->Width * ctx->Buffer->Height * sizeof(GLstencil) );
+#else
+ GLuint i;
+ GLuint pixels = ctx->Buffer->Width * ctx->Buffer->Height;
+ GLstencil *buffer = ctx->Buffer->Stencil;
+ for (i = 0; i < pixels; i++)
+ ptr[i] = ctx->Stencil.Clear;
+#endif
+ }
+}