mirror of
https://github.com/reactos/reactos.git
synced 2024-08-26 05:08:39 +00:00
![Jérôme Gardou](/assets/img/avatar_default.png)
With this commit, we now use a forked version of MESA which only supports OpenGL 1.1, like the windows implementation does. It exposes : - The same pixel formats - The same set of extensions - Nothing more All of this without taking 10% of your build time. If you need a more modern option, look at the MESA package from Rapps, which is (and must be) maintained outside of this code tree. CORE-7499
1023 lines
23 KiB
C
1023 lines
23 KiB
C
/* $Id: stencil.c,v 1.8 1998/01/01 00:52:11 brianp Exp $ */
|
|
|
|
/*
|
|
* Mesa 3-D graphics library
|
|
* Version: 2.6
|
|
* Copyright (C) 1995-1997 Brian Paul
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Library General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Library General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Library General Public
|
|
* License along with this library; if not, write to the Free
|
|
* Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
*/
|
|
|
|
|
|
/*
|
|
* $Log: stencil.c,v $
|
|
* Revision 1.8 1998/01/01 00:52:11 brianp
|
|
* added some tests to prevent crashing if Driver.DepthTestPixels not defined
|
|
*
|
|
* Revision 1.7 1997/07/24 01:21:56 brianp
|
|
* changed precompiled header symbol from PCH to PC_HEADER
|
|
*
|
|
* Revision 1.6 1997/05/28 03:26:29 brianp
|
|
* added precompiled header (PCH) support
|
|
*
|
|
* Revision 1.5 1997/04/29 01:26:37 brianp
|
|
* fixed a few return statements which were missing values
|
|
*
|
|
* Revision 1.4 1997/04/20 20:29:11 brianp
|
|
* replaced abort() with gl_problem()
|
|
*
|
|
* Revision 1.3 1997/02/27 19:58:35 brianp
|
|
* don't try to clear stencil buffer if there isn't one
|
|
*
|
|
* Revision 1.2 1996/09/15 14:18:55 brianp
|
|
* now use GLframebuffer and GLvisual
|
|
*
|
|
* Revision 1.1 1996/09/13 01:38:16 brianp
|
|
* Initial revision
|
|
*
|
|
*/
|
|
|
|
|
|
#ifdef PC_HEADER
|
|
#include "all.h"
|
|
#else
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include "context.h"
|
|
#include "dlist.h"
|
|
#include "macros.h"
|
|
#include "pb.h"
|
|
#include "stencil.h"
|
|
#include "types.h"
|
|
#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 )
|
|
{
|
|
if (INSIDE_BEGIN_END(ctx)) {
|
|
gl_error( ctx, GL_INVALID_OPERATION, "glClearStencil" );
|
|
return;
|
|
}
|
|
ctx->Stencil.Clear = (GLstencil) s;
|
|
}
|
|
|
|
|
|
|
|
void gl_StencilFunc( GLcontext *ctx, GLenum func, GLint ref, GLuint mask )
|
|
{
|
|
GLint maxref;
|
|
|
|
if (INSIDE_BEGIN_END(ctx)) {
|
|
gl_error( ctx, GL_INVALID_OPERATION, "glStencilFunc" );
|
|
return;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
|
|
|
|
void gl_StencilMask( GLcontext *ctx, GLuint mask )
|
|
{
|
|
if (INSIDE_BEGIN_END(ctx)) {
|
|
gl_error( ctx, GL_INVALID_OPERATION, "glStencilMask" );
|
|
return;
|
|
}
|
|
ctx->Stencil.WriteMask = (GLstencil) mask;
|
|
}
|
|
|
|
|
|
|
|
void gl_StencilOp( GLcontext *ctx, GLenum fail, GLenum zfail, GLenum zpass )
|
|
{
|
|
if (INSIDE_BEGIN_END(ctx)) {
|
|
gl_error( ctx, GL_INVALID_OPERATION, "glStencilOp" );
|
|
return;
|
|
}
|
|
switch (fail) {
|
|
case GL_KEEP:
|
|
case GL_ZERO:
|
|
case GL_REPLACE:
|
|
case GL_INCR:
|
|
case GL_DECR:
|
|
case GL_INVERT:
|
|
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:
|
|
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:
|
|
ctx->Stencil.ZPassFunc = zpass;
|
|
break;
|
|
default:
|
|
gl_error( ctx, GL_INVALID_ENUM, "glStencilOp" );
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/* 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[] )
|
|
{
|
|
GLint i;
|
|
GLstencil s, ref;
|
|
GLstencil wrtmask, invmask;
|
|
GLstencil *stencil;
|
|
|
|
wrtmask = ctx->Stencil.WriteMask;
|
|
invmask = ~ctx->Stencil.WriteMask;
|
|
ref = ctx->Stencil.Ref;
|
|
stencil = STENCIL_ADDRESS( x, y );
|
|
|
|
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]) {
|
|
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]) {
|
|
s = stencil[i];
|
|
if (s<0xff) {
|
|
stencil[i] = s+1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
for (i=0;i<n;i++) {
|
|
if (mask[i]) {
|
|
/* VERIFY logic of adding 1 to a write-masked value */
|
|
s = stencil[i];
|
|
if (s<0xff) {
|
|
stencil[i] = (invmask & s) | (wrtmask & (s+1));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case GL_DECR:
|
|
if (invmask==0) {
|
|
for (i=0;i<n;i++) {
|
|
if (mask[i]) {
|
|
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 */
|
|
s = stencil[i];
|
|
if (s>0) {
|
|
stencil[i] = (invmask & s) | (wrtmask & (s-1));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case GL_INVERT:
|
|
if (invmask==0) {
|
|
for (i=0;i<n;i++) {
|
|
if (mask[i]) {
|
|
s = stencil[i];
|
|
stencil[i] = ~s;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
for (i=0;i<n;i++) {
|
|
if (mask[i]) {
|
|
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[] )
|
|
{
|
|
GLint 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 < 0xff) {
|
|
*sptr = *sptr + 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
for (i=0;i<n;i++) {
|
|
if (mask[i]) {
|
|
GLstencil *sptr = STENCIL_ADDRESS( x[i], y[i] );
|
|
if (*sptr<0xff) {
|
|
*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_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, GLubyte stencil[] )
|
|
{
|
|
GLstencil *s;
|
|
|
|
if (ctx->Buffer->Stencil) {
|
|
s = STENCIL_ADDRESS( x, y );
|
|
MEMCPY( stencil, s, n * sizeof(GLubyte) );
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* 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 GLubyte stencil[] )
|
|
{
|
|
GLstencil *s;
|
|
|
|
if (ctx->Buffer->Stencil) {
|
|
s = STENCIL_ADDRESS( x, y );
|
|
MEMCPY( s, stencil, n * sizeof(GLubyte) );
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* 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 );
|
|
MEMSET( ptr, ctx->Stencil.Clear, width * sizeof(GLstencil) );
|
|
}
|
|
}
|
|
else {
|
|
/* clear whole stencil buffer */
|
|
MEMSET( ctx->Buffer->Stencil, ctx->Stencil.Clear,
|
|
ctx->Buffer->Width * ctx->Buffer->Height * sizeof(GLstencil) );
|
|
}
|
|
}
|