From afb833d4e89c312460a4ab9ed6a7a8ca4ebbfe1c Mon Sep 17 00:00:00 2001 From: jtg Date: Thu, 19 Aug 1999 00:55:39 +0000 Subject: Initial revision --- src/mesa/main/stencil.c | 1107 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1107 insertions(+) create mode 100644 src/mesa/main/stencil.c (limited to 'src/mesa/main/stencil.c') diff --git a/src/mesa/main/stencil.c b/src/mesa/main/stencil.c new file mode 100644 index 0000000000..69bd70188b --- /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 +#include +#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;i0) { + stencil[i] = s-1; + } + } + } + } + else { + for (i=0;i0) { + stencil[i] = (invmask & s) | (wrtmask & (s-1)); + } + } + } + } + break; + case GL_INCR_WRAP_EXT: + if (invmask==0) { + for (i=0;iStencil.Function) { + case GL_NEVER: + /* always fail */ + for (i=0;iStencil.Ref & ctx->Stencil.ValueMask; + for (i=0;iStencil.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;iStencil.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;iStencil.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;iStencil.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;iStencil.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;iStencil.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;iStencil.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;iDriver.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;iStencil.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;i0) { + *sptr = *sptr - 1; + } + } + } + } + else { + for (i=0;i0) { + *sptr = (invmask & *sptr) | (wrtmask & (*sptr-1)); + } + } + } + } + break; + case GL_INCR_WRAP_EXT: + if (invmask==0) { + for (i=0;iStencil.Function) { + case GL_NEVER: + /* always fail */ + for (i=0;iStencil.Ref & ctx->Stencil.ValueMask; + for (i=0;iStencil.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;iStencil.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;iStencil.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;iStencil.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;iStencil.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;iStencil.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;iStencil.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;iDriver.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;iStencil.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;iBuffer->Stencil) { + GLstencil *s = STENCIL_ADDRESS( x, y ); +#if STENCIL_BITS == 8 + MEMCPY( s, stencil, n * sizeof(GLstencil) ); +#else + GLuint i; + for (i=0;iBuffer->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 + } +} -- cgit v1.2.3