1 /* $Id: stencil.c,v 1.7 1999/11/05 06:43:10 brianp Exp $ */
4 * Mesa 3-D graphics library
7 * Copyright (C) 1999 Brian Paul All Rights Reserved.
9 * Permission is hereby granted, free of charge, to any person obtaining a
10 * copy of this software and associated documentation files (the "Software"),
11 * to deal in the Software without restriction, including without limitation
12 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
13 * and/or sell copies of the Software, and to permit persons to whom the
14 * Software is furnished to do so, subject to the following conditions:
16 * The above copyright notice and this permission notice shall be included
17 * in all copies or substantial portions of the Software.
19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
20 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
22 * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
23 * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
24 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
28 /* $XFree86: xc/lib/GL/mesa/src/stencil.c,v 1.3 1999/04/04 00:20:32 dawes Exp $ */
37 #include "GL/xf86glx.h"
46 #include "GL/xf86glx.h"
52 # define STENCIL_MAX 0xff
53 #elif STENCIL_BITS==16
54 # define STENCIL_MAX 0xffff
56 illegal number of stencil bits
62 * Return the address of a stencil buffer value given the window coords:
64 #define STENCIL_ADDRESS(X,Y) (ctx->Buffer->Stencil + ctx->Buffer->Width * (Y) + (X))
67 void gl_ClearStencil( GLcontext
*ctx
, GLint s
)
69 ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx
, "glClearStencil");
70 ctx
->Stencil
.Clear
= (GLstencil
) s
;
72 if (ctx
->Driver
.ClearStencil
) {
73 (*ctx
->Driver
.ClearStencil
)( ctx
, s
);
79 void gl_StencilFunc( GLcontext
*ctx
, GLenum func
, GLint ref
, GLuint mask
)
83 ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx
, "glStencilFunc");
94 ctx
->Stencil
.Function
= func
;
97 gl_error( ctx
, GL_INVALID_ENUM
, "glStencilFunc" );
101 maxref
= (1 << STENCIL_BITS
) - 1;
102 ctx
->Stencil
.Ref
= (GLstencil
) CLAMP( ref
, 0, maxref
);
103 ctx
->Stencil
.ValueMask
= (GLstencil
) mask
;
105 if (ctx
->Driver
.StencilFunc
) {
106 (*ctx
->Driver
.StencilFunc
)( ctx
, func
, ctx
->Stencil
.Ref
, mask
);
112 void gl_StencilMask( GLcontext
*ctx
, GLuint mask
)
114 ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx
, "glStencilMask");
115 ctx
->Stencil
.WriteMask
= (GLstencil
) mask
;
117 if (ctx
->Driver
.StencilMask
) {
118 (*ctx
->Driver
.StencilMask
)( ctx
, mask
);
124 void gl_StencilOp( GLcontext
*ctx
, GLenum fail
, GLenum zfail
, GLenum zpass
)
126 ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx
, "glStencilOp");
134 case GL_INCR_WRAP_EXT
:
135 case GL_DECR_WRAP_EXT
:
136 ctx
->Stencil
.FailFunc
= fail
;
139 gl_error( ctx
, GL_INVALID_ENUM
, "glStencilOp" );
149 case GL_INCR_WRAP_EXT
:
150 case GL_DECR_WRAP_EXT
:
151 ctx
->Stencil
.ZFailFunc
= zfail
;
154 gl_error( ctx
, GL_INVALID_ENUM
, "glStencilOp" );
164 case GL_INCR_WRAP_EXT
:
165 case GL_DECR_WRAP_EXT
:
166 ctx
->Stencil
.ZPassFunc
= zpass
;
169 gl_error( ctx
, GL_INVALID_ENUM
, "glStencilOp" );
173 if (ctx
->Driver
.StencilOp
) {
174 (*ctx
->Driver
.StencilOp
)( ctx
, fail
, zfail
, zpass
);
182 IF stencil test fails THEN
183 Don't write the pixel (RGBA,Z)
191 IF depth test passes OR no depth buffer THEN
204 * Apply the given stencil operator for each pixel in the span whose
206 * Input: n - number of pixels in the span
207 * x, y - location of leftmost pixel in the span
208 * oper - the stencil buffer operator
209 * mask - array [n] of flag: 1=apply operator, 0=don't apply operator
211 static void apply_stencil_op_to_span( GLcontext
*ctx
,
212 GLuint n
, GLint x
, GLint y
,
213 GLenum oper
, GLubyte mask
[] )
215 const GLstencil ref
= ctx
->Stencil
.Ref
;
216 const GLstencil wrtmask
= ctx
->Stencil
.WriteMask
;
217 const GLstencil invmask
= (GLstencil
) (~ctx
->Stencil
.WriteMask
);
218 GLstencil
*stencil
= STENCIL_ADDRESS( x
, y
);
236 stencil
[i
] = (GLstencil
) (stencil
[i
] & invmask
);
252 GLstencil s
= stencil
[i
];
253 stencil
[i
] = (GLstencil
) ((invmask
& s
) | (wrtmask
& ref
));
262 GLstencil s
= stencil
[i
];
263 if (s
< STENCIL_MAX
) {
264 stencil
[i
] = (GLstencil
) (s
+1);
272 /* VERIFY logic of adding 1 to a write-masked value */
273 GLstencil s
= stencil
[i
];
274 if (s
< STENCIL_MAX
) {
275 stencil
[i
] = (GLstencil
) ((invmask
& s
) | (wrtmask
& (s
+1)));
285 GLstencil s
= stencil
[i
];
287 stencil
[i
] = (GLstencil
) (s
-1);
295 /* VERIFY logic of subtracting 1 to a write-masked value */
296 GLstencil s
= stencil
[i
];
298 stencil
[i
] = (GLstencil
) ((invmask
& s
) | (wrtmask
& (s
-1)));
304 case GL_INCR_WRAP_EXT
:
315 GLstencil s
= stencil
[i
];
316 stencil
[i
] = (GLstencil
) ((invmask
& s
) | (wrtmask
& (stencil
[i
]+1)));
321 case GL_DECR_WRAP_EXT
:
332 GLstencil s
= stencil
[i
];
333 stencil
[i
] = (GLstencil
) ((invmask
& s
) | (wrtmask
& (stencil
[i
]-1)));
342 GLstencil s
= stencil
[i
];
350 GLstencil s
= stencil
[i
];
351 stencil
[i
] = (GLstencil
) ((invmask
& s
) | (wrtmask
& ~s
));
357 gl_problem(ctx
, "Bad stencilop in apply_stencil_op_to_span");
365 * Apply stencil test to a span of pixels before depth buffering.
366 * Input: n - number of pixels in the span
367 * x, y - coordinate of left-most pixel in the span
368 * mask - array [n] of flag: 0=skip the pixel, 1=stencil the pixel
369 * Output: mask - pixels which fail the stencil test will have their
370 * mask flag set to 0.
371 * Return: 0 = all pixels failed, 1 = zero or more pixels passed.
373 GLint
gl_stencil_span( GLcontext
*ctx
,
374 GLuint n
, GLint x
, GLint y
, GLubyte mask
[] )
376 GLubyte fail
[MAX_WIDTH
];
382 stencil
= STENCIL_ADDRESS( x
, y
);
385 * Perform stencil test. The results of this operation are stored
386 * in the fail[] array:
387 * IF fail[i] is non-zero THEN
388 * the stencil fail operator is to be applied
390 * the stencil fail operator is not to be applied
393 switch (ctx
->Stencil
.Function
) {
408 r
= (GLstencil
) (ctx
->Stencil
.Ref
& ctx
->Stencil
.ValueMask
);
411 s
= (GLstencil
) (stencil
[i
] & ctx
->Stencil
.ValueMask
);
427 r
= (GLstencil
) (ctx
->Stencil
.Ref
& ctx
->Stencil
.ValueMask
);
430 s
= (GLstencil
) (stencil
[i
] & ctx
->Stencil
.ValueMask
);
446 r
= (GLstencil
) (ctx
->Stencil
.Ref
& ctx
->Stencil
.ValueMask
);
449 s
= (GLstencil
) (stencil
[i
] & ctx
->Stencil
.ValueMask
);
465 r
= (GLstencil
) (ctx
->Stencil
.Ref
& ctx
->Stencil
.ValueMask
);
468 s
= (GLstencil
) (stencil
[i
] & ctx
->Stencil
.ValueMask
);
484 r
= (GLstencil
) (ctx
->Stencil
.Ref
& ctx
->Stencil
.ValueMask
);
487 s
= (GLstencil
) (stencil
[i
] & ctx
->Stencil
.ValueMask
);
503 r
= ctx
->Stencil
.Ref
& ctx
->Stencil
.ValueMask
;
506 s
= (GLstencil
) (stencil
[i
] & ctx
->Stencil
.ValueMask
);
528 gl_problem(ctx
, "Bad stencil func in gl_stencil_span");
532 apply_stencil_op_to_span( ctx
, n
, x
, y
, ctx
->Stencil
.FailFunc
, fail
);
534 return (allfail
) ? 0 : 1;
541 * Apply the combination depth-buffer/stencil operator to a span of pixels.
542 * Input: n - number of pixels in the span
543 * x, y - location of leftmost pixel in span
544 * z - array [n] of z values
545 * Input: mask - array [n] of flags (1=test this pixel, 0=skip the pixel)
546 * Output: mask - array [n] of flags (1=depth test passed, 0=failed)
548 void gl_depth_stencil_span( GLcontext
*ctx
,
549 GLuint n
, GLint x
, GLint y
, const GLdepth z
[],
552 if (ctx
->Depth
.Test
==GL_FALSE
) {
554 * No depth buffer, just apply zpass stencil function to active pixels.
556 apply_stencil_op_to_span( ctx
, n
, x
, y
, ctx
->Stencil
.ZPassFunc
, mask
);
560 * Perform depth buffering, then apply zpass or zfail stencil function.
562 GLubyte passmask
[MAX_WIDTH
], failmask
[MAX_WIDTH
], oldmask
[MAX_WIDTH
];
565 /* init pass and fail masks to zero, copy mask[] to oldmask[] */
567 passmask
[i
] = failmask
[i
] = 0;
568 oldmask
[i
] = mask
[i
];
571 /* apply the depth test */
572 if (ctx
->Driver
.DepthTestSpan
)
573 (*ctx
->Driver
.DepthTestSpan
)( ctx
, n
, x
, y
, z
, mask
);
575 /* set the stencil pass/fail flags according to result of depth test */
587 /* apply the pass and fail operations */
588 apply_stencil_op_to_span( ctx
, n
, x
, y
, ctx
->Stencil
.ZFailFunc
, failmask
);
589 apply_stencil_op_to_span( ctx
, n
, x
, y
, ctx
->Stencil
.ZPassFunc
, passmask
);
597 * Apply the given stencil operator for each pixel in the array whose
599 * Input: n - number of pixels in the span
600 * x, y - array of [n] pixels
601 * operator - the stencil buffer operator
602 * mask - array [n] of flag: 1=apply operator, 0=don't apply operator
604 static void apply_stencil_op_to_pixels( GLcontext
*ctx
,
605 GLuint n
, const GLint x
[],
607 GLenum oper
, GLubyte mask
[] )
611 GLstencil wrtmask
, invmask
;
613 wrtmask
= ctx
->Stencil
.WriteMask
;
614 invmask
= (GLstencil
) (~ctx
->Stencil
.WriteMask
);
616 ref
= ctx
->Stencil
.Ref
;
626 GLstencil
*sptr
= STENCIL_ADDRESS( x
[i
], y
[i
] );
634 GLstencil
*sptr
= STENCIL_ADDRESS( x
[i
], y
[i
] );
635 *sptr
= (GLstencil
) (invmask
& *sptr
);
644 GLstencil
*sptr
= STENCIL_ADDRESS( x
[i
], y
[i
] );
652 GLstencil
*sptr
= STENCIL_ADDRESS( x
[i
], y
[i
] );
653 *sptr
= (GLstencil
) ((invmask
& *sptr
) | (wrtmask
& ref
));
662 GLstencil
*sptr
= STENCIL_ADDRESS( x
[i
], y
[i
] );
663 if (*sptr
< STENCIL_MAX
) {
664 *sptr
= (GLstencil
) (*sptr
+ 1);
672 GLstencil
*sptr
= STENCIL_ADDRESS( x
[i
], y
[i
] );
673 if (*sptr
< STENCIL_MAX
) {
674 *sptr
= (GLstencil
) ((invmask
& *sptr
) | (wrtmask
& (*sptr
+1)));
684 GLstencil
*sptr
= STENCIL_ADDRESS( x
[i
], y
[i
] );
686 *sptr
= (GLstencil
) (*sptr
- 1);
694 GLstencil
*sptr
= STENCIL_ADDRESS( x
[i
], y
[i
] );
696 *sptr
= (GLstencil
) ((invmask
& *sptr
) | (wrtmask
& (*sptr
-1)));
702 case GL_INCR_WRAP_EXT
:
706 GLstencil
*sptr
= STENCIL_ADDRESS( x
[i
], y
[i
] );
707 *sptr
= (GLstencil
) (*sptr
+ 1);
714 GLstencil
*sptr
= STENCIL_ADDRESS( x
[i
], y
[i
] );
715 *sptr
= (GLstencil
) ((invmask
& *sptr
) | (wrtmask
& (*sptr
+1)));
720 case GL_DECR_WRAP_EXT
:
724 GLstencil
*sptr
= STENCIL_ADDRESS( x
[i
], y
[i
] );
725 *sptr
= (GLstencil
) (*sptr
- 1);
732 GLstencil
*sptr
= STENCIL_ADDRESS( x
[i
], y
[i
] );
733 *sptr
= (GLstencil
) ((invmask
& *sptr
) | (wrtmask
& (*sptr
-1)));
742 GLstencil
*sptr
= STENCIL_ADDRESS( x
[i
], y
[i
] );
743 *sptr
= (GLstencil
) (~*sptr
);
750 GLstencil
*sptr
= STENCIL_ADDRESS( x
[i
], y
[i
] );
751 *sptr
= (GLstencil
) ((invmask
& *sptr
) | (wrtmask
& ~*sptr
));
757 gl_problem(ctx
, "Bad stencilop in apply_stencil_op_to_pixels");
764 * Apply stencil test to an array of pixels before depth buffering.
765 * Input: n - number of pixels in the span
766 * x, y - array of [n] pixels to stencil
767 * mask - array [n] of flag: 0=skip the pixel, 1=stencil the pixel
768 * Output: mask - pixels which fail the stencil test will have their
769 * mask flag set to 0.
770 * Return: 0 = all pixels failed, 1 = zero or more pixels passed.
772 GLint
gl_stencil_pixels( GLcontext
*ctx
,
773 GLuint n
, const GLint x
[], const GLint y
[],
776 GLubyte fail
[PB_SIZE
];
782 * Perform stencil test. The results of this operation are stored
783 * in the fail[] array:
784 * IF fail[i] is non-zero THEN
785 * the stencil fail operator is to be applied
787 * the stencil fail operator is not to be applied
791 switch (ctx
->Stencil
.Function
) {
806 r
= (GLstencil
) (ctx
->Stencil
.Ref
& ctx
->Stencil
.ValueMask
);
809 GLstencil
*sptr
= STENCIL_ADDRESS(x
[i
],y
[i
]);
810 s
= (GLstencil
) (*sptr
& ctx
->Stencil
.ValueMask
);
826 r
= (GLstencil
) (ctx
->Stencil
.Ref
& ctx
->Stencil
.ValueMask
);
829 GLstencil
*sptr
= STENCIL_ADDRESS(x
[i
],y
[i
]);
830 s
= (GLstencil
) (*sptr
& ctx
->Stencil
.ValueMask
);
846 r
= (GLstencil
) (ctx
->Stencil
.Ref
& ctx
->Stencil
.ValueMask
);
849 GLstencil
*sptr
= STENCIL_ADDRESS(x
[i
],y
[i
]);
850 s
= (GLstencil
) (*sptr
& ctx
->Stencil
.ValueMask
);
866 r
= (GLstencil
) (ctx
->Stencil
.Ref
& ctx
->Stencil
.ValueMask
);
869 GLstencil
*sptr
= STENCIL_ADDRESS(x
[i
],y
[i
]);
870 s
= (GLstencil
) (*sptr
& ctx
->Stencil
.ValueMask
);
886 r
= (GLstencil
) (ctx
->Stencil
.Ref
& ctx
->Stencil
.ValueMask
);
889 GLstencil
*sptr
= STENCIL_ADDRESS(x
[i
],y
[i
]);
890 s
= (GLstencil
) (*sptr
& ctx
->Stencil
.ValueMask
);
906 r
= (GLstencil
) (ctx
->Stencil
.Ref
& ctx
->Stencil
.ValueMask
);
909 GLstencil
*sptr
= STENCIL_ADDRESS(x
[i
],y
[i
]);
910 s
= (GLstencil
) (*sptr
& ctx
->Stencil
.ValueMask
);
932 gl_problem(ctx
, "Bad stencil func in gl_stencil_pixels");
936 apply_stencil_op_to_pixels( ctx
, n
, x
, y
, ctx
->Stencil
.FailFunc
, fail
);
938 return (allfail
) ? 0 : 1;
945 * Apply the combination depth-buffer/stencil operator to a span of pixels.
946 * Input: n - number of pixels in the span
947 * x, y - array of [n] pixels to stencil
948 * z - array [n] of z values
949 * Input: mask - array [n] of flags (1=test this pixel, 0=skip the pixel)
950 * Output: mask - array [n] of flags (1=depth test passed, 0=failed)
952 void gl_depth_stencil_pixels( GLcontext
*ctx
,
953 GLuint n
, const GLint x
[], const GLint y
[],
954 const GLdepth z
[], GLubyte mask
[] )
956 if (ctx
->Depth
.Test
==GL_FALSE
) {
958 * No depth buffer, just apply zpass stencil function to active pixels.
960 apply_stencil_op_to_pixels( ctx
, n
, x
, y
, ctx
->Stencil
.ZPassFunc
, mask
);
964 * Perform depth buffering, then apply zpass or zfail stencil function.
966 GLubyte passmask
[PB_SIZE
], failmask
[PB_SIZE
], oldmask
[PB_SIZE
];
969 /* init pass and fail masks to zero */
971 passmask
[i
] = failmask
[i
] = 0;
972 oldmask
[i
] = mask
[i
];
975 /* apply the depth test */
976 if (ctx
->Driver
.DepthTestPixels
)
977 (*ctx
->Driver
.DepthTestPixels
)( ctx
, n
, x
, y
, z
, mask
);
979 /* set the stencil pass/fail flags according to result of depth test */
991 /* apply the pass and fail operations */
992 apply_stencil_op_to_pixels( ctx
, n
, x
, y
,
993 ctx
->Stencil
.ZFailFunc
, failmask
);
994 apply_stencil_op_to_pixels( ctx
, n
, x
, y
,
995 ctx
->Stencil
.ZPassFunc
, passmask
);
1003 * Return a span of stencil values from the stencil buffer.
1004 * Input: n - how many pixels
1005 * x,y - location of first pixel
1006 * Output: stencil - the array of stencil values
1008 void gl_read_stencil_span( GLcontext
*ctx
,
1009 GLuint n
, GLint x
, GLint y
, GLstencil stencil
[] )
1011 if (ctx
->Buffer
->Stencil
) {
1012 const GLstencil
*s
= STENCIL_ADDRESS( x
, y
);
1013 #if STENCIL_BITS == 8
1014 MEMCPY( stencil
, s
, n
* sizeof(GLstencil
) );
1026 * Write a span of stencil values to the stencil buffer.
1027 * Input: n - how many pixels
1028 * x,y - location of first pixel
1029 * stencil - the array of stencil values
1031 void gl_write_stencil_span( GLcontext
*ctx
,
1032 GLuint n
, GLint x
, GLint y
,
1033 const GLstencil stencil
[] )
1035 if (ctx
->Buffer
->Stencil
) {
1036 GLstencil
*s
= STENCIL_ADDRESS( x
, y
);
1037 #if STENCIL_BITS == 8
1038 MEMCPY( s
, stencil
, n
* sizeof(GLstencil
) );
1050 * Allocate a new stencil buffer. If there's an old one it will be
1051 * deallocated first. The new stencil buffer will be uninitialized.
1053 void gl_alloc_stencil_buffer( GLcontext
*ctx
)
1055 GLuint buffersize
= ctx
->Buffer
->Width
* ctx
->Buffer
->Height
;
1057 /* deallocate current stencil buffer if present */
1058 if (ctx
->Buffer
->Stencil
) {
1059 FREE(ctx
->Buffer
->Stencil
);
1060 ctx
->Buffer
->Stencil
= NULL
;
1063 /* allocate new stencil buffer */
1064 ctx
->Buffer
->Stencil
= (GLstencil
*) MALLOC(buffersize
* sizeof(GLstencil
));
1065 if (!ctx
->Buffer
->Stencil
) {
1067 gl_set_enable( ctx
, GL_STENCIL_TEST
, GL_FALSE
);
1068 gl_error( ctx
, GL_OUT_OF_MEMORY
, "gl_alloc_stencil_buffer" );
1076 * Clear the stencil buffer. If the stencil buffer doesn't exist yet we'll
1079 void gl_clear_stencil_buffer( GLcontext
*ctx
)
1081 if (ctx
->Visual
->StencilBits
==0 || !ctx
->Buffer
->Stencil
) {
1082 /* no stencil buffer */
1086 if (ctx
->Scissor
.Enabled
) {
1087 /* clear scissor region only */
1089 GLint width
= ctx
->Buffer
->Xmax
- ctx
->Buffer
->Xmin
+ 1;
1090 for (y
=ctx
->Buffer
->Ymin
; y
<=ctx
->Buffer
->Ymax
; y
++) {
1091 GLstencil
*ptr
= STENCIL_ADDRESS( ctx
->Buffer
->Xmin
, y
);
1093 MEMSET( ptr
, ctx
->Stencil
.Clear
, width
* sizeof(GLstencil
) );
1096 for (x
= 0; x
< width
; x
++)
1097 ptr
[x
] = ctx
->Stencil
.Clear
;
1102 /* clear whole stencil buffer */
1104 MEMSET( ctx
->Buffer
->Stencil
, ctx
->Stencil
.Clear
,
1105 ctx
->Buffer
->Width
* ctx
->Buffer
->Height
* sizeof(GLstencil
) );
1108 GLuint pixels
= ctx
->Buffer
->Width
* ctx
->Buffer
->Height
;
1109 GLstencil
*buffer
= ctx
->Buffer
->Stencil
;
1110 for (i
= 0; i
< pixels
; i
++)
1111 ptr
[i
] = ctx
->Stencil
.Clear
;