1 /* $Id: stencil.c,v 1.10 1999/11/24 18:48:31 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.
42 # define STENCIL_MAX 0xff
43 #elif STENCIL_BITS==16
44 # define STENCIL_MAX 0xffff
46 illegal number of stencil bits
52 _mesa_ClearStencil( GLint s
)
54 GET_CURRENT_CONTEXT(ctx
);
55 ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx
, "glClearStencil");
56 ctx
->Stencil
.Clear
= (GLstencil
) s
;
58 if (ctx
->Driver
.ClearStencil
) {
59 (*ctx
->Driver
.ClearStencil
)( ctx
, s
);
66 _mesa_StencilFunc( GLenum func
, GLint ref
, GLuint mask
)
68 GET_CURRENT_CONTEXT(ctx
);
71 ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx
, "glStencilFunc");
82 ctx
->Stencil
.Function
= func
;
85 gl_error( ctx
, GL_INVALID_ENUM
, "glStencilFunc" );
89 maxref
= (1 << STENCIL_BITS
) - 1;
90 ctx
->Stencil
.Ref
= (GLstencil
) CLAMP( ref
, 0, maxref
);
91 ctx
->Stencil
.ValueMask
= (GLstencil
) mask
;
93 if (ctx
->Driver
.StencilFunc
) {
94 (*ctx
->Driver
.StencilFunc
)( ctx
, func
, ctx
->Stencil
.Ref
, mask
);
101 _mesa_StencilMask( GLuint mask
)
103 GET_CURRENT_CONTEXT(ctx
);
104 ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx
, "glStencilMask");
105 ctx
->Stencil
.WriteMask
= (GLstencil
) mask
;
107 if (ctx
->Driver
.StencilMask
) {
108 (*ctx
->Driver
.StencilMask
)( ctx
, mask
);
115 _mesa_StencilOp( GLenum fail
, GLenum zfail
, GLenum zpass
)
117 GET_CURRENT_CONTEXT(ctx
);
118 ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx
, "glStencilOp");
126 case GL_INCR_WRAP_EXT
:
127 case GL_DECR_WRAP_EXT
:
128 ctx
->Stencil
.FailFunc
= fail
;
131 gl_error( ctx
, GL_INVALID_ENUM
, "glStencilOp" );
141 case GL_INCR_WRAP_EXT
:
142 case GL_DECR_WRAP_EXT
:
143 ctx
->Stencil
.ZFailFunc
= zfail
;
146 gl_error( ctx
, GL_INVALID_ENUM
, "glStencilOp" );
156 case GL_INCR_WRAP_EXT
:
157 case GL_DECR_WRAP_EXT
:
158 ctx
->Stencil
.ZPassFunc
= zpass
;
161 gl_error( ctx
, GL_INVALID_ENUM
, "glStencilOp" );
165 if (ctx
->Driver
.StencilOp
) {
166 (*ctx
->Driver
.StencilOp
)( ctx
, fail
, zfail
, zpass
);
174 IF stencil test fails THEN
175 Don't write the pixel (RGBA,Z)
183 IF depth test passes OR no depth buffer THEN
195 * Return the address of a stencil buffer value given the window coords:
197 #define STENCIL_ADDRESS(X,Y) \
198 (ctx->DrawBuffer->Stencil + ctx->DrawBuffer->Width * (Y) + (X))
203 * Apply the given stencil operator for each pixel in the span whose
205 * Input: n - number of pixels in the span
206 * x, y - location of leftmost pixel in the span
207 * oper - the stencil buffer operator
208 * mask - array [n] of flag: 1=apply operator, 0=don't apply operator
210 static void apply_stencil_op_to_span( GLcontext
*ctx
,
211 GLuint n
, GLint x
, GLint y
,
212 GLenum oper
, GLubyte mask
[] )
214 const GLstencil ref
= ctx
->Stencil
.Ref
;
215 const GLstencil wrtmask
= ctx
->Stencil
.WriteMask
;
216 const GLstencil invmask
= (GLstencil
) (~ctx
->Stencil
.WriteMask
);
217 GLstencil
*stencil
= STENCIL_ADDRESS( x
, y
);
235 stencil
[i
] = (GLstencil
) (stencil
[i
] & invmask
);
251 GLstencil s
= stencil
[i
];
252 stencil
[i
] = (GLstencil
) ((invmask
& s
) | (wrtmask
& ref
));
261 GLstencil s
= stencil
[i
];
262 if (s
< STENCIL_MAX
) {
263 stencil
[i
] = (GLstencil
) (s
+1);
271 /* VERIFY logic of adding 1 to a write-masked value */
272 GLstencil s
= stencil
[i
];
273 if (s
< STENCIL_MAX
) {
274 stencil
[i
] = (GLstencil
) ((invmask
& s
) | (wrtmask
& (s
+1)));
284 GLstencil s
= stencil
[i
];
286 stencil
[i
] = (GLstencil
) (s
-1);
294 /* VERIFY logic of subtracting 1 to a write-masked value */
295 GLstencil s
= stencil
[i
];
297 stencil
[i
] = (GLstencil
) ((invmask
& s
) | (wrtmask
& (s
-1)));
303 case GL_INCR_WRAP_EXT
:
314 GLstencil s
= stencil
[i
];
315 stencil
[i
] = (GLstencil
) ((invmask
& s
) | (wrtmask
& (stencil
[i
]+1)));
320 case GL_DECR_WRAP_EXT
:
331 GLstencil s
= stencil
[i
];
332 stencil
[i
] = (GLstencil
) ((invmask
& s
) | (wrtmask
& (stencil
[i
]-1)));
341 GLstencil s
= stencil
[i
];
342 stencil
[i
] = (GLstencil
) ~s
;
349 GLstencil s
= stencil
[i
];
350 stencil
[i
] = (GLstencil
) ((invmask
& s
) | (wrtmask
& ~s
));
356 gl_problem(ctx
, "Bad stencilop in apply_stencil_op_to_span");
364 * Apply stencil test to a span of pixels before depth buffering.
365 * Input: n - number of pixels in the span
366 * x, y - coordinate of left-most pixel in the span
367 * mask - array [n] of flag: 0=skip the pixel, 1=stencil the pixel
368 * Output: mask - pixels which fail the stencil test will have their
369 * mask flag set to 0.
370 * Return: 0 = all pixels failed, 1 = zero or more pixels passed.
372 GLint
gl_stencil_span( GLcontext
*ctx
,
373 GLuint n
, GLint x
, GLint y
, GLubyte mask
[] )
375 GLubyte fail
[MAX_WIDTH
];
381 stencil
= STENCIL_ADDRESS( x
, y
);
384 * Perform stencil test. The results of this operation are stored
385 * in the fail[] array:
386 * IF fail[i] is non-zero THEN
387 * the stencil fail operator is to be applied
389 * the stencil fail operator is not to be applied
392 switch (ctx
->Stencil
.Function
) {
407 r
= (GLstencil
) (ctx
->Stencil
.Ref
& ctx
->Stencil
.ValueMask
);
410 s
= (GLstencil
) (stencil
[i
] & ctx
->Stencil
.ValueMask
);
426 r
= (GLstencil
) (ctx
->Stencil
.Ref
& ctx
->Stencil
.ValueMask
);
429 s
= (GLstencil
) (stencil
[i
] & ctx
->Stencil
.ValueMask
);
445 r
= (GLstencil
) (ctx
->Stencil
.Ref
& ctx
->Stencil
.ValueMask
);
448 s
= (GLstencil
) (stencil
[i
] & ctx
->Stencil
.ValueMask
);
464 r
= (GLstencil
) (ctx
->Stencil
.Ref
& ctx
->Stencil
.ValueMask
);
467 s
= (GLstencil
) (stencil
[i
] & ctx
->Stencil
.ValueMask
);
483 r
= (GLstencil
) (ctx
->Stencil
.Ref
& ctx
->Stencil
.ValueMask
);
486 s
= (GLstencil
) (stencil
[i
] & ctx
->Stencil
.ValueMask
);
502 r
= (GLstencil
) (ctx
->Stencil
.Ref
& ctx
->Stencil
.ValueMask
);
505 s
= (GLstencil
) (stencil
[i
] & ctx
->Stencil
.ValueMask
);
527 gl_problem(ctx
, "Bad stencil func in gl_stencil_span");
531 apply_stencil_op_to_span( ctx
, n
, x
, y
, ctx
->Stencil
.FailFunc
, fail
);
533 return (allfail
) ? 0 : 1;
540 * Apply the combination depth-buffer/stencil operator to a span of pixels.
541 * Input: n - number of pixels in the span
542 * x, y - location of leftmost pixel in span
543 * z - array [n] of z values
544 * Input: mask - array [n] of flags (1=test this pixel, 0=skip the pixel)
545 * Output: mask - array [n] of flags (1=depth test passed, 0=failed)
547 void gl_depth_stencil_span( GLcontext
*ctx
,
548 GLuint n
, GLint x
, GLint y
, const GLdepth z
[],
551 if (ctx
->Depth
.Test
==GL_FALSE
) {
553 * No depth buffer, just apply zpass stencil function to active pixels.
555 apply_stencil_op_to_span( ctx
, n
, x
, y
, ctx
->Stencil
.ZPassFunc
, mask
);
559 * Perform depth buffering, then apply zpass or zfail stencil function.
561 GLubyte passmask
[MAX_WIDTH
], failmask
[MAX_WIDTH
], oldmask
[MAX_WIDTH
];
564 /* init pass and fail masks to zero, copy mask[] to oldmask[] */
566 passmask
[i
] = failmask
[i
] = 0;
567 oldmask
[i
] = mask
[i
];
570 /* apply the depth test */
571 if (ctx
->Driver
.DepthTestSpan
)
572 (*ctx
->Driver
.DepthTestSpan
)( ctx
, n
, x
, y
, z
, mask
);
574 /* set the stencil pass/fail flags according to result of depth test */
586 /* apply the pass and fail operations */
587 apply_stencil_op_to_span( ctx
, n
, x
, y
, ctx
->Stencil
.ZFailFunc
, failmask
);
588 apply_stencil_op_to_span( ctx
, n
, x
, y
, ctx
->Stencil
.ZPassFunc
, passmask
);
596 * Apply the given stencil operator for each pixel in the array whose
598 * Input: n - number of pixels in the span
599 * x, y - array of [n] pixels
600 * operator - the stencil buffer operator
601 * mask - array [n] of flag: 1=apply operator, 0=don't apply operator
603 static void apply_stencil_op_to_pixels( GLcontext
*ctx
,
604 GLuint n
, const GLint x
[],
606 GLenum oper
, GLubyte mask
[] )
610 GLstencil wrtmask
, invmask
;
612 wrtmask
= ctx
->Stencil
.WriteMask
;
613 invmask
= (GLstencil
) (~ctx
->Stencil
.WriteMask
);
615 ref
= ctx
->Stencil
.Ref
;
625 GLstencil
*sptr
= STENCIL_ADDRESS( x
[i
], y
[i
] );
633 GLstencil
*sptr
= STENCIL_ADDRESS( x
[i
], y
[i
] );
634 *sptr
= (GLstencil
) (invmask
& *sptr
);
643 GLstencil
*sptr
= STENCIL_ADDRESS( x
[i
], y
[i
] );
651 GLstencil
*sptr
= STENCIL_ADDRESS( x
[i
], y
[i
] );
652 *sptr
= (GLstencil
) ((invmask
& *sptr
) | (wrtmask
& ref
));
661 GLstencil
*sptr
= STENCIL_ADDRESS( x
[i
], y
[i
] );
662 if (*sptr
< STENCIL_MAX
) {
663 *sptr
= (GLstencil
) (*sptr
+ 1);
671 GLstencil
*sptr
= STENCIL_ADDRESS( x
[i
], y
[i
] );
672 if (*sptr
< STENCIL_MAX
) {
673 *sptr
= (GLstencil
) ((invmask
& *sptr
) | (wrtmask
& (*sptr
+1)));
683 GLstencil
*sptr
= STENCIL_ADDRESS( x
[i
], y
[i
] );
685 *sptr
= (GLstencil
) (*sptr
- 1);
693 GLstencil
*sptr
= STENCIL_ADDRESS( x
[i
], y
[i
] );
695 *sptr
= (GLstencil
) ((invmask
& *sptr
) | (wrtmask
& (*sptr
-1)));
701 case GL_INCR_WRAP_EXT
:
705 GLstencil
*sptr
= STENCIL_ADDRESS( x
[i
], y
[i
] );
706 *sptr
= (GLstencil
) (*sptr
+ 1);
713 GLstencil
*sptr
= STENCIL_ADDRESS( x
[i
], y
[i
] );
714 *sptr
= (GLstencil
) ((invmask
& *sptr
) | (wrtmask
& (*sptr
+1)));
719 case GL_DECR_WRAP_EXT
:
723 GLstencil
*sptr
= STENCIL_ADDRESS( x
[i
], y
[i
] );
724 *sptr
= (GLstencil
) (*sptr
- 1);
731 GLstencil
*sptr
= STENCIL_ADDRESS( x
[i
], y
[i
] );
732 *sptr
= (GLstencil
) ((invmask
& *sptr
) | (wrtmask
& (*sptr
-1)));
741 GLstencil
*sptr
= STENCIL_ADDRESS( x
[i
], y
[i
] );
742 *sptr
= (GLstencil
) (~*sptr
);
749 GLstencil
*sptr
= STENCIL_ADDRESS( x
[i
], y
[i
] );
750 *sptr
= (GLstencil
) ((invmask
& *sptr
) | (wrtmask
& ~*sptr
));
756 gl_problem(ctx
, "Bad stencilop in apply_stencil_op_to_pixels");
763 * Apply stencil test to an array of pixels before depth buffering.
764 * Input: n - number of pixels in the span
765 * x, y - array of [n] pixels to stencil
766 * mask - array [n] of flag: 0=skip the pixel, 1=stencil the pixel
767 * Output: mask - pixels which fail the stencil test will have their
768 * mask flag set to 0.
769 * Return: 0 = all pixels failed, 1 = zero or more pixels passed.
771 GLint
gl_stencil_pixels( GLcontext
*ctx
,
772 GLuint n
, const GLint x
[], const GLint y
[],
775 GLubyte fail
[PB_SIZE
];
781 * Perform stencil test. The results of this operation are stored
782 * in the fail[] array:
783 * IF fail[i] is non-zero THEN
784 * the stencil fail operator is to be applied
786 * the stencil fail operator is not to be applied
790 switch (ctx
->Stencil
.Function
) {
805 r
= (GLstencil
) (ctx
->Stencil
.Ref
& ctx
->Stencil
.ValueMask
);
808 GLstencil
*sptr
= STENCIL_ADDRESS(x
[i
],y
[i
]);
809 s
= (GLstencil
) (*sptr
& ctx
->Stencil
.ValueMask
);
825 r
= (GLstencil
) (ctx
->Stencil
.Ref
& ctx
->Stencil
.ValueMask
);
828 GLstencil
*sptr
= STENCIL_ADDRESS(x
[i
],y
[i
]);
829 s
= (GLstencil
) (*sptr
& ctx
->Stencil
.ValueMask
);
845 r
= (GLstencil
) (ctx
->Stencil
.Ref
& ctx
->Stencil
.ValueMask
);
848 GLstencil
*sptr
= STENCIL_ADDRESS(x
[i
],y
[i
]);
849 s
= (GLstencil
) (*sptr
& ctx
->Stencil
.ValueMask
);
865 r
= (GLstencil
) (ctx
->Stencil
.Ref
& ctx
->Stencil
.ValueMask
);
868 GLstencil
*sptr
= STENCIL_ADDRESS(x
[i
],y
[i
]);
869 s
= (GLstencil
) (*sptr
& ctx
->Stencil
.ValueMask
);
885 r
= (GLstencil
) (ctx
->Stencil
.Ref
& ctx
->Stencil
.ValueMask
);
888 GLstencil
*sptr
= STENCIL_ADDRESS(x
[i
],y
[i
]);
889 s
= (GLstencil
) (*sptr
& ctx
->Stencil
.ValueMask
);
905 r
= (GLstencil
) (ctx
->Stencil
.Ref
& ctx
->Stencil
.ValueMask
);
908 GLstencil
*sptr
= STENCIL_ADDRESS(x
[i
],y
[i
]);
909 s
= (GLstencil
) (*sptr
& ctx
->Stencil
.ValueMask
);
931 gl_problem(ctx
, "Bad stencil func in gl_stencil_pixels");
935 apply_stencil_op_to_pixels( ctx
, n
, x
, y
, ctx
->Stencil
.FailFunc
, fail
);
937 return (allfail
) ? 0 : 1;
944 * Apply the combination depth-buffer/stencil operator to a span of pixels.
945 * Input: n - number of pixels in the span
946 * x, y - array of [n] pixels to stencil
947 * z - array [n] of z values
948 * Input: mask - array [n] of flags (1=test this pixel, 0=skip the pixel)
949 * Output: mask - array [n] of flags (1=depth test passed, 0=failed)
951 void gl_depth_stencil_pixels( GLcontext
*ctx
,
952 GLuint n
, const GLint x
[], const GLint y
[],
953 const GLdepth z
[], GLubyte mask
[] )
955 if (ctx
->Depth
.Test
==GL_FALSE
) {
957 * No depth buffer, just apply zpass stencil function to active pixels.
959 apply_stencil_op_to_pixels( ctx
, n
, x
, y
, ctx
->Stencil
.ZPassFunc
, mask
);
963 * Perform depth buffering, then apply zpass or zfail stencil function.
965 GLubyte passmask
[PB_SIZE
], failmask
[PB_SIZE
], oldmask
[PB_SIZE
];
968 /* init pass and fail masks to zero */
970 passmask
[i
] = failmask
[i
] = 0;
971 oldmask
[i
] = mask
[i
];
974 /* apply the depth test */
975 if (ctx
->Driver
.DepthTestPixels
)
976 (*ctx
->Driver
.DepthTestPixels
)( ctx
, n
, x
, y
, z
, mask
);
978 /* set the stencil pass/fail flags according to result of depth test */
990 /* apply the pass and fail operations */
991 apply_stencil_op_to_pixels( ctx
, n
, x
, y
,
992 ctx
->Stencil
.ZFailFunc
, failmask
);
993 apply_stencil_op_to_pixels( ctx
, n
, x
, y
,
994 ctx
->Stencil
.ZPassFunc
, passmask
);
1002 * Return a span of stencil values from the stencil buffer.
1003 * Input: n - how many pixels
1004 * x,y - location of first pixel
1005 * Output: stencil - the array of stencil values
1007 void gl_read_stencil_span( GLcontext
*ctx
,
1008 GLuint n
, GLint x
, GLint y
, GLstencil stencil
[] )
1010 if (ctx
->DrawBuffer
->Stencil
) {
1011 const GLstencil
*s
= STENCIL_ADDRESS( x
, y
);
1012 #if STENCIL_BITS == 8
1013 MEMCPY( stencil
, s
, n
* sizeof(GLstencil
) );
1025 * Write a span of stencil values to the stencil buffer.
1026 * Input: n - how many pixels
1027 * x,y - location of first pixel
1028 * stencil - the array of stencil values
1030 void gl_write_stencil_span( GLcontext
*ctx
,
1031 GLuint n
, GLint x
, GLint y
,
1032 const GLstencil stencil
[] )
1034 if (ctx
->DrawBuffer
->Stencil
) {
1035 GLstencil
*s
= STENCIL_ADDRESS( x
, y
);
1036 #if STENCIL_BITS == 8
1037 MEMCPY( s
, stencil
, n
* sizeof(GLstencil
) );
1049 * Allocate a new stencil buffer. If there's an old one it will be
1050 * deallocated first. The new stencil buffer will be uninitialized.
1052 void gl_alloc_stencil_buffer( GLcontext
*ctx
)
1054 GLuint buffersize
= ctx
->DrawBuffer
->Width
* ctx
->DrawBuffer
->Height
;
1056 /* deallocate current stencil buffer if present */
1057 if (ctx
->DrawBuffer
->Stencil
) {
1058 FREE(ctx
->DrawBuffer
->Stencil
);
1059 ctx
->DrawBuffer
->Stencil
= NULL
;
1062 /* allocate new stencil buffer */
1063 ctx
->DrawBuffer
->Stencil
= (GLstencil
*) MALLOC(buffersize
* sizeof(GLstencil
));
1064 if (!ctx
->DrawBuffer
->Stencil
) {
1066 _mesa_set_enable( ctx
, GL_STENCIL_TEST
, GL_FALSE
);
1067 gl_error( ctx
, GL_OUT_OF_MEMORY
, "gl_alloc_stencil_buffer" );
1075 * Clear the stencil buffer. If the stencil buffer doesn't exist yet we'll
1078 void gl_clear_stencil_buffer( GLcontext
*ctx
)
1080 if (ctx
->Visual
->StencilBits
==0 || !ctx
->DrawBuffer
->Stencil
) {
1081 /* no stencil buffer */
1085 if (ctx
->Scissor
.Enabled
) {
1086 /* clear scissor region only */
1088 GLint width
= ctx
->DrawBuffer
->Xmax
- ctx
->DrawBuffer
->Xmin
+ 1;
1089 for (y
=ctx
->DrawBuffer
->Ymin
; y
<=ctx
->DrawBuffer
->Ymax
; y
++) {
1090 GLstencil
*ptr
= STENCIL_ADDRESS( ctx
->DrawBuffer
->Xmin
, y
);
1092 MEMSET( ptr
, ctx
->Stencil
.Clear
, width
* sizeof(GLstencil
) );
1095 for (x
= 0; x
< width
; x
++)
1096 ptr
[x
] = ctx
->Stencil
.Clear
;
1101 /* clear whole stencil buffer */
1103 MEMSET( ctx
->DrawBuffer
->Stencil
, ctx
->Stencil
.Clear
,
1104 ctx
->DrawBuffer
->Width
* ctx
->DrawBuffer
->Height
* sizeof(GLstencil
) );
1107 GLuint pixels
= ctx
->DrawBuffer
->Width
* ctx
->DrawBuffer
->Height
;
1108 GLstencil
*buffer
= ctx
->DrawBuffer
->Stencil
;
1109 for (i
= 0; i
< pixels
; i
++)
1110 ptr
[i
] = ctx
->Stencil
.Clear
;