1 /* $Id: stencil.c,v 1.9 1999/11/11 01:22:27 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 * Return the address of a stencil buffer value given the window coords:
54 #define STENCIL_ADDRESS(X,Y) (ctx->Buffer->Stencil + ctx->Buffer->Width * (Y) + (X))
58 _mesa_ClearStencil( GLint s
)
60 GET_CURRENT_CONTEXT(ctx
);
61 ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx
, "glClearStencil");
62 ctx
->Stencil
.Clear
= (GLstencil
) s
;
64 if (ctx
->Driver
.ClearStencil
) {
65 (*ctx
->Driver
.ClearStencil
)( ctx
, s
);
72 _mesa_StencilFunc( GLenum func
, GLint ref
, GLuint mask
)
74 GET_CURRENT_CONTEXT(ctx
);
77 ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx
, "glStencilFunc");
88 ctx
->Stencil
.Function
= func
;
91 gl_error( ctx
, GL_INVALID_ENUM
, "glStencilFunc" );
95 maxref
= (1 << STENCIL_BITS
) - 1;
96 ctx
->Stencil
.Ref
= (GLstencil
) CLAMP( ref
, 0, maxref
);
97 ctx
->Stencil
.ValueMask
= (GLstencil
) mask
;
99 if (ctx
->Driver
.StencilFunc
) {
100 (*ctx
->Driver
.StencilFunc
)( ctx
, func
, ctx
->Stencil
.Ref
, mask
);
107 _mesa_StencilMask( GLuint mask
)
109 GET_CURRENT_CONTEXT(ctx
);
110 ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx
, "glStencilMask");
111 ctx
->Stencil
.WriteMask
= (GLstencil
) mask
;
113 if (ctx
->Driver
.StencilMask
) {
114 (*ctx
->Driver
.StencilMask
)( ctx
, mask
);
121 _mesa_StencilOp( GLenum fail
, GLenum zfail
, GLenum zpass
)
123 GET_CURRENT_CONTEXT(ctx
);
124 ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx
, "glStencilOp");
132 case GL_INCR_WRAP_EXT
:
133 case GL_DECR_WRAP_EXT
:
134 ctx
->Stencil
.FailFunc
= fail
;
137 gl_error( ctx
, GL_INVALID_ENUM
, "glStencilOp" );
147 case GL_INCR_WRAP_EXT
:
148 case GL_DECR_WRAP_EXT
:
149 ctx
->Stencil
.ZFailFunc
= zfail
;
152 gl_error( ctx
, GL_INVALID_ENUM
, "glStencilOp" );
162 case GL_INCR_WRAP_EXT
:
163 case GL_DECR_WRAP_EXT
:
164 ctx
->Stencil
.ZPassFunc
= zpass
;
167 gl_error( ctx
, GL_INVALID_ENUM
, "glStencilOp" );
171 if (ctx
->Driver
.StencilOp
) {
172 (*ctx
->Driver
.StencilOp
)( ctx
, fail
, zfail
, zpass
);
180 IF stencil test fails THEN
181 Don't write the pixel (RGBA,Z)
189 IF depth test passes OR no depth buffer THEN
202 * Apply the given stencil operator for each pixel in the span whose
204 * Input: n - number of pixels in the span
205 * x, y - location of leftmost pixel in the span
206 * oper - the stencil buffer operator
207 * mask - array [n] of flag: 1=apply operator, 0=don't apply operator
209 static void apply_stencil_op_to_span( GLcontext
*ctx
,
210 GLuint n
, GLint x
, GLint y
,
211 GLenum oper
, GLubyte mask
[] )
213 const GLstencil ref
= ctx
->Stencil
.Ref
;
214 const GLstencil wrtmask
= ctx
->Stencil
.WriteMask
;
215 const GLstencil invmask
= (GLstencil
) (~ctx
->Stencil
.WriteMask
);
216 GLstencil
*stencil
= STENCIL_ADDRESS( x
, y
);
234 stencil
[i
] = (GLstencil
) (stencil
[i
] & invmask
);
250 GLstencil s
= stencil
[i
];
251 stencil
[i
] = (GLstencil
) ((invmask
& s
) | (wrtmask
& ref
));
260 GLstencil s
= stencil
[i
];
261 if (s
< STENCIL_MAX
) {
262 stencil
[i
] = (GLstencil
) (s
+1);
270 /* VERIFY logic of adding 1 to a write-masked value */
271 GLstencil s
= stencil
[i
];
272 if (s
< STENCIL_MAX
) {
273 stencil
[i
] = (GLstencil
) ((invmask
& s
) | (wrtmask
& (s
+1)));
283 GLstencil s
= stencil
[i
];
285 stencil
[i
] = (GLstencil
) (s
-1);
293 /* VERIFY logic of subtracting 1 to a write-masked value */
294 GLstencil s
= stencil
[i
];
296 stencil
[i
] = (GLstencil
) ((invmask
& s
) | (wrtmask
& (s
-1)));
302 case GL_INCR_WRAP_EXT
:
313 GLstencil s
= stencil
[i
];
314 stencil
[i
] = (GLstencil
) ((invmask
& s
) | (wrtmask
& (stencil
[i
]+1)));
319 case GL_DECR_WRAP_EXT
:
330 GLstencil s
= stencil
[i
];
331 stencil
[i
] = (GLstencil
) ((invmask
& s
) | (wrtmask
& (stencil
[i
]-1)));
340 GLstencil s
= stencil
[i
];
341 stencil
[i
] = (GLstencil
) ~s
;
348 GLstencil s
= stencil
[i
];
349 stencil
[i
] = (GLstencil
) ((invmask
& s
) | (wrtmask
& ~s
));
355 gl_problem(ctx
, "Bad stencilop in apply_stencil_op_to_span");
363 * Apply stencil test to a span of pixels before depth buffering.
364 * Input: n - number of pixels in the span
365 * x, y - coordinate of left-most pixel in the span
366 * mask - array [n] of flag: 0=skip the pixel, 1=stencil the pixel
367 * Output: mask - pixels which fail the stencil test will have their
368 * mask flag set to 0.
369 * Return: 0 = all pixels failed, 1 = zero or more pixels passed.
371 GLint
gl_stencil_span( GLcontext
*ctx
,
372 GLuint n
, GLint x
, GLint y
, GLubyte mask
[] )
374 GLubyte fail
[MAX_WIDTH
];
380 stencil
= STENCIL_ADDRESS( x
, y
);
383 * Perform stencil test. The results of this operation are stored
384 * in the fail[] array:
385 * IF fail[i] is non-zero THEN
386 * the stencil fail operator is to be applied
388 * the stencil fail operator is not to be applied
391 switch (ctx
->Stencil
.Function
) {
406 r
= (GLstencil
) (ctx
->Stencil
.Ref
& ctx
->Stencil
.ValueMask
);
409 s
= (GLstencil
) (stencil
[i
] & ctx
->Stencil
.ValueMask
);
425 r
= (GLstencil
) (ctx
->Stencil
.Ref
& ctx
->Stencil
.ValueMask
);
428 s
= (GLstencil
) (stencil
[i
] & ctx
->Stencil
.ValueMask
);
444 r
= (GLstencil
) (ctx
->Stencil
.Ref
& ctx
->Stencil
.ValueMask
);
447 s
= (GLstencil
) (stencil
[i
] & ctx
->Stencil
.ValueMask
);
463 r
= (GLstencil
) (ctx
->Stencil
.Ref
& ctx
->Stencil
.ValueMask
);
466 s
= (GLstencil
) (stencil
[i
] & ctx
->Stencil
.ValueMask
);
482 r
= (GLstencil
) (ctx
->Stencil
.Ref
& ctx
->Stencil
.ValueMask
);
485 s
= (GLstencil
) (stencil
[i
] & ctx
->Stencil
.ValueMask
);
501 r
= (GLstencil
) (ctx
->Stencil
.Ref
& ctx
->Stencil
.ValueMask
);
504 s
= (GLstencil
) (stencil
[i
] & ctx
->Stencil
.ValueMask
);
526 gl_problem(ctx
, "Bad stencil func in gl_stencil_span");
530 apply_stencil_op_to_span( ctx
, n
, x
, y
, ctx
->Stencil
.FailFunc
, fail
);
532 return (allfail
) ? 0 : 1;
539 * Apply the combination depth-buffer/stencil operator to a span of pixels.
540 * Input: n - number of pixels in the span
541 * x, y - location of leftmost pixel in span
542 * z - array [n] of z values
543 * Input: mask - array [n] of flags (1=test this pixel, 0=skip the pixel)
544 * Output: mask - array [n] of flags (1=depth test passed, 0=failed)
546 void gl_depth_stencil_span( GLcontext
*ctx
,
547 GLuint n
, GLint x
, GLint y
, const GLdepth z
[],
550 if (ctx
->Depth
.Test
==GL_FALSE
) {
552 * No depth buffer, just apply zpass stencil function to active pixels.
554 apply_stencil_op_to_span( ctx
, n
, x
, y
, ctx
->Stencil
.ZPassFunc
, mask
);
558 * Perform depth buffering, then apply zpass or zfail stencil function.
560 GLubyte passmask
[MAX_WIDTH
], failmask
[MAX_WIDTH
], oldmask
[MAX_WIDTH
];
563 /* init pass and fail masks to zero, copy mask[] to oldmask[] */
565 passmask
[i
] = failmask
[i
] = 0;
566 oldmask
[i
] = mask
[i
];
569 /* apply the depth test */
570 if (ctx
->Driver
.DepthTestSpan
)
571 (*ctx
->Driver
.DepthTestSpan
)( ctx
, n
, x
, y
, z
, mask
);
573 /* set the stencil pass/fail flags according to result of depth test */
585 /* apply the pass and fail operations */
586 apply_stencil_op_to_span( ctx
, n
, x
, y
, ctx
->Stencil
.ZFailFunc
, failmask
);
587 apply_stencil_op_to_span( ctx
, n
, x
, y
, ctx
->Stencil
.ZPassFunc
, passmask
);
595 * Apply the given stencil operator for each pixel in the array whose
597 * Input: n - number of pixels in the span
598 * x, y - array of [n] pixels
599 * operator - the stencil buffer operator
600 * mask - array [n] of flag: 1=apply operator, 0=don't apply operator
602 static void apply_stencil_op_to_pixels( GLcontext
*ctx
,
603 GLuint n
, const GLint x
[],
605 GLenum oper
, GLubyte mask
[] )
609 GLstencil wrtmask
, invmask
;
611 wrtmask
= ctx
->Stencil
.WriteMask
;
612 invmask
= (GLstencil
) (~ctx
->Stencil
.WriteMask
);
614 ref
= ctx
->Stencil
.Ref
;
624 GLstencil
*sptr
= STENCIL_ADDRESS( x
[i
], y
[i
] );
632 GLstencil
*sptr
= STENCIL_ADDRESS( x
[i
], y
[i
] );
633 *sptr
= (GLstencil
) (invmask
& *sptr
);
642 GLstencil
*sptr
= STENCIL_ADDRESS( x
[i
], y
[i
] );
650 GLstencil
*sptr
= STENCIL_ADDRESS( x
[i
], y
[i
] );
651 *sptr
= (GLstencil
) ((invmask
& *sptr
) | (wrtmask
& ref
));
660 GLstencil
*sptr
= STENCIL_ADDRESS( x
[i
], y
[i
] );
661 if (*sptr
< STENCIL_MAX
) {
662 *sptr
= (GLstencil
) (*sptr
+ 1);
670 GLstencil
*sptr
= STENCIL_ADDRESS( x
[i
], y
[i
] );
671 if (*sptr
< STENCIL_MAX
) {
672 *sptr
= (GLstencil
) ((invmask
& *sptr
) | (wrtmask
& (*sptr
+1)));
682 GLstencil
*sptr
= STENCIL_ADDRESS( x
[i
], y
[i
] );
684 *sptr
= (GLstencil
) (*sptr
- 1);
692 GLstencil
*sptr
= STENCIL_ADDRESS( x
[i
], y
[i
] );
694 *sptr
= (GLstencil
) ((invmask
& *sptr
) | (wrtmask
& (*sptr
-1)));
700 case GL_INCR_WRAP_EXT
:
704 GLstencil
*sptr
= STENCIL_ADDRESS( x
[i
], y
[i
] );
705 *sptr
= (GLstencil
) (*sptr
+ 1);
712 GLstencil
*sptr
= STENCIL_ADDRESS( x
[i
], y
[i
] );
713 *sptr
= (GLstencil
) ((invmask
& *sptr
) | (wrtmask
& (*sptr
+1)));
718 case GL_DECR_WRAP_EXT
:
722 GLstencil
*sptr
= STENCIL_ADDRESS( x
[i
], y
[i
] );
723 *sptr
= (GLstencil
) (*sptr
- 1);
730 GLstencil
*sptr
= STENCIL_ADDRESS( x
[i
], y
[i
] );
731 *sptr
= (GLstencil
) ((invmask
& *sptr
) | (wrtmask
& (*sptr
-1)));
740 GLstencil
*sptr
= STENCIL_ADDRESS( x
[i
], y
[i
] );
741 *sptr
= (GLstencil
) (~*sptr
);
748 GLstencil
*sptr
= STENCIL_ADDRESS( x
[i
], y
[i
] );
749 *sptr
= (GLstencil
) ((invmask
& *sptr
) | (wrtmask
& ~*sptr
));
755 gl_problem(ctx
, "Bad stencilop in apply_stencil_op_to_pixels");
762 * Apply stencil test to an array of pixels before depth buffering.
763 * Input: n - number of pixels in the span
764 * x, y - array of [n] pixels to stencil
765 * mask - array [n] of flag: 0=skip the pixel, 1=stencil the pixel
766 * Output: mask - pixels which fail the stencil test will have their
767 * mask flag set to 0.
768 * Return: 0 = all pixels failed, 1 = zero or more pixels passed.
770 GLint
gl_stencil_pixels( GLcontext
*ctx
,
771 GLuint n
, const GLint x
[], const GLint y
[],
774 GLubyte fail
[PB_SIZE
];
780 * Perform stencil test. The results of this operation are stored
781 * in the fail[] array:
782 * IF fail[i] is non-zero THEN
783 * the stencil fail operator is to be applied
785 * the stencil fail operator is not to be applied
789 switch (ctx
->Stencil
.Function
) {
804 r
= (GLstencil
) (ctx
->Stencil
.Ref
& ctx
->Stencil
.ValueMask
);
807 GLstencil
*sptr
= STENCIL_ADDRESS(x
[i
],y
[i
]);
808 s
= (GLstencil
) (*sptr
& ctx
->Stencil
.ValueMask
);
824 r
= (GLstencil
) (ctx
->Stencil
.Ref
& ctx
->Stencil
.ValueMask
);
827 GLstencil
*sptr
= STENCIL_ADDRESS(x
[i
],y
[i
]);
828 s
= (GLstencil
) (*sptr
& ctx
->Stencil
.ValueMask
);
844 r
= (GLstencil
) (ctx
->Stencil
.Ref
& ctx
->Stencil
.ValueMask
);
847 GLstencil
*sptr
= STENCIL_ADDRESS(x
[i
],y
[i
]);
848 s
= (GLstencil
) (*sptr
& ctx
->Stencil
.ValueMask
);
864 r
= (GLstencil
) (ctx
->Stencil
.Ref
& ctx
->Stencil
.ValueMask
);
867 GLstencil
*sptr
= STENCIL_ADDRESS(x
[i
],y
[i
]);
868 s
= (GLstencil
) (*sptr
& ctx
->Stencil
.ValueMask
);
884 r
= (GLstencil
) (ctx
->Stencil
.Ref
& ctx
->Stencil
.ValueMask
);
887 GLstencil
*sptr
= STENCIL_ADDRESS(x
[i
],y
[i
]);
888 s
= (GLstencil
) (*sptr
& ctx
->Stencil
.ValueMask
);
904 r
= (GLstencil
) (ctx
->Stencil
.Ref
& ctx
->Stencil
.ValueMask
);
907 GLstencil
*sptr
= STENCIL_ADDRESS(x
[i
],y
[i
]);
908 s
= (GLstencil
) (*sptr
& ctx
->Stencil
.ValueMask
);
930 gl_problem(ctx
, "Bad stencil func in gl_stencil_pixels");
934 apply_stencil_op_to_pixels( ctx
, n
, x
, y
, ctx
->Stencil
.FailFunc
, fail
);
936 return (allfail
) ? 0 : 1;
943 * Apply the combination depth-buffer/stencil operator to a span of pixels.
944 * Input: n - number of pixels in the span
945 * x, y - array of [n] pixels to stencil
946 * z - array [n] of z values
947 * Input: mask - array [n] of flags (1=test this pixel, 0=skip the pixel)
948 * Output: mask - array [n] of flags (1=depth test passed, 0=failed)
950 void gl_depth_stencil_pixels( GLcontext
*ctx
,
951 GLuint n
, const GLint x
[], const GLint y
[],
952 const GLdepth z
[], GLubyte mask
[] )
954 if (ctx
->Depth
.Test
==GL_FALSE
) {
956 * No depth buffer, just apply zpass stencil function to active pixels.
958 apply_stencil_op_to_pixels( ctx
, n
, x
, y
, ctx
->Stencil
.ZPassFunc
, mask
);
962 * Perform depth buffering, then apply zpass or zfail stencil function.
964 GLubyte passmask
[PB_SIZE
], failmask
[PB_SIZE
], oldmask
[PB_SIZE
];
967 /* init pass and fail masks to zero */
969 passmask
[i
] = failmask
[i
] = 0;
970 oldmask
[i
] = mask
[i
];
973 /* apply the depth test */
974 if (ctx
->Driver
.DepthTestPixels
)
975 (*ctx
->Driver
.DepthTestPixels
)( ctx
, n
, x
, y
, z
, mask
);
977 /* set the stencil pass/fail flags according to result of depth test */
989 /* apply the pass and fail operations */
990 apply_stencil_op_to_pixels( ctx
, n
, x
, y
,
991 ctx
->Stencil
.ZFailFunc
, failmask
);
992 apply_stencil_op_to_pixels( ctx
, n
, x
, y
,
993 ctx
->Stencil
.ZPassFunc
, passmask
);
1001 * Return a span of stencil values from the stencil buffer.
1002 * Input: n - how many pixels
1003 * x,y - location of first pixel
1004 * Output: stencil - the array of stencil values
1006 void gl_read_stencil_span( GLcontext
*ctx
,
1007 GLuint n
, GLint x
, GLint y
, GLstencil stencil
[] )
1009 if (ctx
->Buffer
->Stencil
) {
1010 const GLstencil
*s
= STENCIL_ADDRESS( x
, y
);
1011 #if STENCIL_BITS == 8
1012 MEMCPY( stencil
, s
, n
* sizeof(GLstencil
) );
1024 * Write a span of stencil values to the stencil buffer.
1025 * Input: n - how many pixels
1026 * x,y - location of first pixel
1027 * stencil - the array of stencil values
1029 void gl_write_stencil_span( GLcontext
*ctx
,
1030 GLuint n
, GLint x
, GLint y
,
1031 const GLstencil stencil
[] )
1033 if (ctx
->Buffer
->Stencil
) {
1034 GLstencil
*s
= STENCIL_ADDRESS( x
, y
);
1035 #if STENCIL_BITS == 8
1036 MEMCPY( s
, stencil
, n
* sizeof(GLstencil
) );
1048 * Allocate a new stencil buffer. If there's an old one it will be
1049 * deallocated first. The new stencil buffer will be uninitialized.
1051 void gl_alloc_stencil_buffer( GLcontext
*ctx
)
1053 GLuint buffersize
= ctx
->Buffer
->Width
* ctx
->Buffer
->Height
;
1055 /* deallocate current stencil buffer if present */
1056 if (ctx
->Buffer
->Stencil
) {
1057 FREE(ctx
->Buffer
->Stencil
);
1058 ctx
->Buffer
->Stencil
= NULL
;
1061 /* allocate new stencil buffer */
1062 ctx
->Buffer
->Stencil
= (GLstencil
*) MALLOC(buffersize
* sizeof(GLstencil
));
1063 if (!ctx
->Buffer
->Stencil
) {
1065 _mesa_set_enable( ctx
, GL_STENCIL_TEST
, GL_FALSE
);
1066 gl_error( ctx
, GL_OUT_OF_MEMORY
, "gl_alloc_stencil_buffer" );
1074 * Clear the stencil buffer. If the stencil buffer doesn't exist yet we'll
1077 void gl_clear_stencil_buffer( GLcontext
*ctx
)
1079 if (ctx
->Visual
->StencilBits
==0 || !ctx
->Buffer
->Stencil
) {
1080 /* no stencil buffer */
1084 if (ctx
->Scissor
.Enabled
) {
1085 /* clear scissor region only */
1087 GLint width
= ctx
->Buffer
->Xmax
- ctx
->Buffer
->Xmin
+ 1;
1088 for (y
=ctx
->Buffer
->Ymin
; y
<=ctx
->Buffer
->Ymax
; y
++) {
1089 GLstencil
*ptr
= STENCIL_ADDRESS( ctx
->Buffer
->Xmin
, y
);
1091 MEMSET( ptr
, ctx
->Stencil
.Clear
, width
* sizeof(GLstencil
) );
1094 for (x
= 0; x
< width
; x
++)
1095 ptr
[x
] = ctx
->Stencil
.Clear
;
1100 /* clear whole stencil buffer */
1102 MEMSET( ctx
->Buffer
->Stencil
, ctx
->Stencil
.Clear
,
1103 ctx
->Buffer
->Width
* ctx
->Buffer
->Height
* sizeof(GLstencil
) );
1106 GLuint pixels
= ctx
->Buffer
->Width
* ctx
->Buffer
->Height
;
1107 GLstencil
*buffer
= ctx
->Buffer
->Stencil
;
1108 for (i
= 0; i
< pixels
; i
++)
1109 ptr
[i
] = ctx
->Stencil
.Clear
;