1 /* $Id: stencil.c,v 1.3 1999/09/19 02:03:19 tjump 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.
40 #include "GL/xf86glx.h"
46 # define STENCIL_MAX 0xff
47 #elif STENCIL_BITS==16
48 # define STENCIL_MAX 0xffff
50 illegal number of stencil bits
56 * Return the address of a stencil buffer value given the window coords:
58 #define STENCIL_ADDRESS(X,Y) (ctx->Buffer->Stencil + ctx->Buffer->Width * (Y) + (X))
61 void gl_ClearStencil( GLcontext
*ctx
, GLint s
)
63 ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx
, "glClearStencil");
64 ctx
->Stencil
.Clear
= (GLstencil
) s
;
66 if (ctx
->Driver
.ClearStencil
) {
67 (*ctx
->Driver
.ClearStencil
)( ctx
, s
);
73 void gl_StencilFunc( GLcontext
*ctx
, GLenum func
, GLint ref
, GLuint mask
)
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
= CLAMP( ref
, 0, maxref
);
97 ctx
->Stencil
.ValueMask
= mask
;
99 if (ctx
->Driver
.StencilFunc
) {
100 (*ctx
->Driver
.StencilFunc
)( ctx
, func
, ctx
->Stencil
.Ref
, mask
);
106 void gl_StencilMask( GLcontext
*ctx
, GLuint mask
)
108 ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx
, "glStencilMask");
109 ctx
->Stencil
.WriteMask
= (GLstencil
) mask
;
111 if (ctx
->Driver
.StencilMask
) {
112 (*ctx
->Driver
.StencilMask
)( ctx
, mask
);
118 void gl_StencilOp( GLcontext
*ctx
, GLenum fail
, GLenum zfail
, GLenum zpass
)
120 ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx
, "glStencilOp");
128 case GL_INCR_WRAP_EXT
:
129 case GL_DECR_WRAP_EXT
:
130 ctx
->Stencil
.FailFunc
= fail
;
133 gl_error( ctx
, GL_INVALID_ENUM
, "glStencilOp" );
143 case GL_INCR_WRAP_EXT
:
144 case GL_DECR_WRAP_EXT
:
145 ctx
->Stencil
.ZFailFunc
= zfail
;
148 gl_error( ctx
, GL_INVALID_ENUM
, "glStencilOp" );
158 case GL_INCR_WRAP_EXT
:
159 case GL_DECR_WRAP_EXT
:
160 ctx
->Stencil
.ZPassFunc
= zpass
;
163 gl_error( ctx
, GL_INVALID_ENUM
, "glStencilOp" );
167 if (ctx
->Driver
.StencilOp
) {
168 (*ctx
->Driver
.StencilOp
)( ctx
, fail
, zfail
, zpass
);
176 IF stencil test fails THEN
177 Don't write the pixel (RGBA,Z)
185 IF depth test passes OR no depth buffer THEN
198 * Apply the given stencil operator for each pixel in the span whose
200 * Input: n - number of pixels in the span
201 * x, y - location of leftmost pixel in the span
202 * oper - the stencil buffer operator
203 * mask - array [n] of flag: 1=apply operator, 0=don't apply operator
205 static void apply_stencil_op_to_span( GLcontext
*ctx
,
206 GLuint n
, GLint x
, GLint y
,
207 GLenum oper
, GLubyte mask
[] )
209 const GLstencil ref
= ctx
->Stencil
.Ref
;
210 const GLstencil wrtmask
= ctx
->Stencil
.WriteMask
;
211 const GLstencil invmask
= ~ctx
->Stencil
.WriteMask
;
212 GLstencil
*stencil
= STENCIL_ADDRESS( x
, y
);
230 stencil
[i
] = stencil
[i
] & invmask
;
246 GLstencil s
= stencil
[i
];
247 stencil
[i
] = (invmask
& s
) | (wrtmask
& ref
);
256 GLstencil s
= stencil
[i
];
257 if (s
< STENCIL_MAX
) {
266 /* VERIFY logic of adding 1 to a write-masked value */
267 GLstencil s
= stencil
[i
];
268 if (s
< STENCIL_MAX
) {
269 stencil
[i
] = (invmask
& s
) | (wrtmask
& (s
+1));
279 GLstencil s
= stencil
[i
];
289 /* VERIFY logic of subtracting 1 to a write-masked value */
290 GLstencil s
= stencil
[i
];
292 stencil
[i
] = (invmask
& s
) | (wrtmask
& (s
-1));
298 case GL_INCR_WRAP_EXT
:
309 GLstencil s
= stencil
[i
];
310 stencil
[i
] = (invmask
& s
) | (wrtmask
& (stencil
[i
]+1));
315 case GL_DECR_WRAP_EXT
:
326 GLstencil s
= stencil
[i
];
327 stencil
[i
] = (invmask
& s
) | (wrtmask
& (stencil
[i
]-1));
336 GLstencil s
= stencil
[i
];
344 GLstencil s
= stencil
[i
];
345 stencil
[i
] = (invmask
& s
) | (wrtmask
& ~s
);
351 gl_problem(ctx
, "Bad stencilop in apply_stencil_op_to_span");
359 * Apply stencil test to a span of pixels before depth buffering.
360 * Input: n - number of pixels in the span
361 * x, y - coordinate of left-most pixel in the span
362 * mask - array [n] of flag: 0=skip the pixel, 1=stencil the pixel
363 * Output: mask - pixels which fail the stencil test will have their
364 * mask flag set to 0.
365 * Return: 0 = all pixels failed, 1 = zero or more pixels passed.
367 GLint
gl_stencil_span( GLcontext
*ctx
,
368 GLuint n
, GLint x
, GLint y
, GLubyte mask
[] )
370 GLubyte fail
[MAX_WIDTH
];
376 stencil
= STENCIL_ADDRESS( x
, y
);
379 * Perform stencil test. The results of this operation are stored
380 * in the fail[] array:
381 * IF fail[i] is non-zero THEN
382 * the stencil fail operator is to be applied
384 * the stencil fail operator is not to be applied
387 switch (ctx
->Stencil
.Function
) {
402 r
= ctx
->Stencil
.Ref
& ctx
->Stencil
.ValueMask
;
405 s
= stencil
[i
] & ctx
->Stencil
.ValueMask
;
421 r
= ctx
->Stencil
.Ref
& ctx
->Stencil
.ValueMask
;
424 s
= stencil
[i
] & ctx
->Stencil
.ValueMask
;
440 r
= ctx
->Stencil
.Ref
& ctx
->Stencil
.ValueMask
;
443 s
= stencil
[i
] & ctx
->Stencil
.ValueMask
;
459 r
= ctx
->Stencil
.Ref
& ctx
->Stencil
.ValueMask
;
462 s
= stencil
[i
] & ctx
->Stencil
.ValueMask
;
478 r
= ctx
->Stencil
.Ref
& ctx
->Stencil
.ValueMask
;
481 s
= stencil
[i
] & ctx
->Stencil
.ValueMask
;
497 r
= ctx
->Stencil
.Ref
& ctx
->Stencil
.ValueMask
;
500 s
= stencil
[i
] & ctx
->Stencil
.ValueMask
;
522 gl_problem(ctx
, "Bad stencil func in gl_stencil_span");
526 apply_stencil_op_to_span( ctx
, n
, x
, y
, ctx
->Stencil
.FailFunc
, fail
);
528 return (allfail
) ? 0 : 1;
535 * Apply the combination depth-buffer/stencil operator to a span of pixels.
536 * Input: n - number of pixels in the span
537 * x, y - location of leftmost pixel in span
538 * z - array [n] of z values
539 * Input: mask - array [n] of flags (1=test this pixel, 0=skip the pixel)
540 * Output: mask - array [n] of flags (1=depth test passed, 0=failed)
542 void gl_depth_stencil_span( GLcontext
*ctx
,
543 GLuint n
, GLint x
, GLint y
, const GLdepth z
[],
546 if (ctx
->Depth
.Test
==GL_FALSE
) {
548 * No depth buffer, just apply zpass stencil function to active pixels.
550 apply_stencil_op_to_span( ctx
, n
, x
, y
, ctx
->Stencil
.ZPassFunc
, mask
);
554 * Perform depth buffering, then apply zpass or zfail stencil function.
556 GLubyte passmask
[MAX_WIDTH
], failmask
[MAX_WIDTH
], oldmask
[MAX_WIDTH
];
559 /* init pass and fail masks to zero, copy mask[] to oldmask[] */
561 passmask
[i
] = failmask
[i
] = 0;
562 oldmask
[i
] = mask
[i
];
565 /* apply the depth test */
566 if (ctx
->Driver
.DepthTestSpan
)
567 (*ctx
->Driver
.DepthTestSpan
)( ctx
, n
, x
, y
, z
, mask
);
569 /* set the stencil pass/fail flags according to result of depth test */
581 /* apply the pass and fail operations */
582 apply_stencil_op_to_span( ctx
, n
, x
, y
, ctx
->Stencil
.ZFailFunc
, failmask
);
583 apply_stencil_op_to_span( ctx
, n
, x
, y
, ctx
->Stencil
.ZPassFunc
, passmask
);
591 * Apply the given stencil operator for each pixel in the array whose
593 * Input: n - number of pixels in the span
594 * x, y - array of [n] pixels
595 * operator - the stencil buffer operator
596 * mask - array [n] of flag: 1=apply operator, 0=don't apply operator
598 static void apply_stencil_op_to_pixels( GLcontext
*ctx
,
599 GLuint n
, const GLint x
[],
601 GLenum oper
, GLubyte mask
[] )
605 GLstencil wrtmask
, invmask
;
607 wrtmask
= ctx
->Stencil
.WriteMask
;
608 invmask
= ~ctx
->Stencil
.WriteMask
;
610 ref
= ctx
->Stencil
.Ref
;
620 GLstencil
*sptr
= STENCIL_ADDRESS( x
[i
], y
[i
] );
628 GLstencil
*sptr
= STENCIL_ADDRESS( x
[i
], y
[i
] );
629 *sptr
= invmask
& *sptr
;
638 GLstencil
*sptr
= STENCIL_ADDRESS( x
[i
], y
[i
] );
646 GLstencil
*sptr
= STENCIL_ADDRESS( x
[i
], y
[i
] );
647 *sptr
= (invmask
& *sptr
) | (wrtmask
& ref
);
656 GLstencil
*sptr
= STENCIL_ADDRESS( x
[i
], y
[i
] );
657 if (*sptr
< STENCIL_MAX
) {
666 GLstencil
*sptr
= STENCIL_ADDRESS( x
[i
], y
[i
] );
667 if (*sptr
< STENCIL_MAX
) {
668 *sptr
= (invmask
& *sptr
) | (wrtmask
& (*sptr
+1));
678 GLstencil
*sptr
= STENCIL_ADDRESS( x
[i
], y
[i
] );
688 GLstencil
*sptr
= STENCIL_ADDRESS( x
[i
], y
[i
] );
690 *sptr
= (invmask
& *sptr
) | (wrtmask
& (*sptr
-1));
696 case GL_INCR_WRAP_EXT
:
700 GLstencil
*sptr
= STENCIL_ADDRESS( x
[i
], y
[i
] );
708 GLstencil
*sptr
= STENCIL_ADDRESS( x
[i
], y
[i
] );
709 *sptr
= (invmask
& *sptr
) | (wrtmask
& (*sptr
+1));
714 case GL_DECR_WRAP_EXT
:
718 GLstencil
*sptr
= STENCIL_ADDRESS( x
[i
], y
[i
] );
726 GLstencil
*sptr
= STENCIL_ADDRESS( x
[i
], y
[i
] );
727 *sptr
= (invmask
& *sptr
) | (wrtmask
& (*sptr
-1));
736 GLstencil
*sptr
= STENCIL_ADDRESS( x
[i
], y
[i
] );
744 GLstencil
*sptr
= STENCIL_ADDRESS( x
[i
], y
[i
] );
745 *sptr
= (invmask
& *sptr
) | (wrtmask
& ~*sptr
);
751 gl_problem(ctx
, "Bad stencilop in apply_stencil_op_to_pixels");
758 * Apply stencil test to an array of pixels before depth buffering.
759 * Input: n - number of pixels in the span
760 * x, y - array of [n] pixels to stencil
761 * mask - array [n] of flag: 0=skip the pixel, 1=stencil the pixel
762 * Output: mask - pixels which fail the stencil test will have their
763 * mask flag set to 0.
764 * Return: 0 = all pixels failed, 1 = zero or more pixels passed.
766 GLint
gl_stencil_pixels( GLcontext
*ctx
,
767 GLuint n
, const GLint x
[], const GLint y
[],
770 GLubyte fail
[PB_SIZE
];
776 * Perform stencil test. The results of this operation are stored
777 * in the fail[] array:
778 * IF fail[i] is non-zero THEN
779 * the stencil fail operator is to be applied
781 * the stencil fail operator is not to be applied
785 switch (ctx
->Stencil
.Function
) {
800 r
= ctx
->Stencil
.Ref
& ctx
->Stencil
.ValueMask
;
803 GLstencil
*sptr
= STENCIL_ADDRESS(x
[i
],y
[i
]);
804 s
= *sptr
& ctx
->Stencil
.ValueMask
;
820 r
= ctx
->Stencil
.Ref
& ctx
->Stencil
.ValueMask
;
823 GLstencil
*sptr
= STENCIL_ADDRESS(x
[i
],y
[i
]);
824 s
= *sptr
& ctx
->Stencil
.ValueMask
;
840 r
= ctx
->Stencil
.Ref
& ctx
->Stencil
.ValueMask
;
843 GLstencil
*sptr
= STENCIL_ADDRESS(x
[i
],y
[i
]);
844 s
= *sptr
& ctx
->Stencil
.ValueMask
;
860 r
= ctx
->Stencil
.Ref
& ctx
->Stencil
.ValueMask
;
863 GLstencil
*sptr
= STENCIL_ADDRESS(x
[i
],y
[i
]);
864 s
= *sptr
& ctx
->Stencil
.ValueMask
;
880 r
= ctx
->Stencil
.Ref
& ctx
->Stencil
.ValueMask
;
883 GLstencil
*sptr
= STENCIL_ADDRESS(x
[i
],y
[i
]);
884 s
= *sptr
& ctx
->Stencil
.ValueMask
;
900 r
= ctx
->Stencil
.Ref
& ctx
->Stencil
.ValueMask
;
903 GLstencil
*sptr
= STENCIL_ADDRESS(x
[i
],y
[i
]);
904 s
= *sptr
& ctx
->Stencil
.ValueMask
;
926 gl_problem(ctx
, "Bad stencil func in gl_stencil_pixels");
930 apply_stencil_op_to_pixels( ctx
, n
, x
, y
, ctx
->Stencil
.FailFunc
, fail
);
932 return (allfail
) ? 0 : 1;
939 * Apply the combination depth-buffer/stencil operator to a span of pixels.
940 * Input: n - number of pixels in the span
941 * x, y - array of [n] pixels to stencil
942 * z - array [n] of z values
943 * Input: mask - array [n] of flags (1=test this pixel, 0=skip the pixel)
944 * Output: mask - array [n] of flags (1=depth test passed, 0=failed)
946 void gl_depth_stencil_pixels( GLcontext
*ctx
,
947 GLuint n
, const GLint x
[], const GLint y
[],
948 const GLdepth z
[], GLubyte mask
[] )
950 if (ctx
->Depth
.Test
==GL_FALSE
) {
952 * No depth buffer, just apply zpass stencil function to active pixels.
954 apply_stencil_op_to_pixels( ctx
, n
, x
, y
, ctx
->Stencil
.ZPassFunc
, mask
);
958 * Perform depth buffering, then apply zpass or zfail stencil function.
960 GLubyte passmask
[PB_SIZE
], failmask
[PB_SIZE
], oldmask
[PB_SIZE
];
963 /* init pass and fail masks to zero */
965 passmask
[i
] = failmask
[i
] = 0;
966 oldmask
[i
] = mask
[i
];
969 /* apply the depth test */
970 if (ctx
->Driver
.DepthTestPixels
)
971 (*ctx
->Driver
.DepthTestPixels
)( ctx
, n
, x
, y
, z
, mask
);
973 /* set the stencil pass/fail flags according to result of depth test */
985 /* apply the pass and fail operations */
986 apply_stencil_op_to_pixels( ctx
, n
, x
, y
,
987 ctx
->Stencil
.ZFailFunc
, failmask
);
988 apply_stencil_op_to_pixels( ctx
, n
, x
, y
,
989 ctx
->Stencil
.ZPassFunc
, passmask
);
997 * Return a span of stencil values from the stencil buffer.
998 * Input: n - how many pixels
999 * x,y - location of first pixel
1000 * Output: stencil - the array of stencil values
1002 void gl_read_stencil_span( GLcontext
*ctx
,
1003 GLuint n
, GLint x
, GLint y
, GLstencil stencil
[] )
1005 if (ctx
->Buffer
->Stencil
) {
1006 const GLstencil
*s
= STENCIL_ADDRESS( x
, y
);
1007 #if STENCIL_BITS == 8
1008 MEMCPY( stencil
, s
, n
* sizeof(GLstencil
) );
1020 * Write a span of stencil values to the stencil buffer.
1021 * Input: n - how many pixels
1022 * x,y - location of first pixel
1023 * stencil - the array of stencil values
1025 void gl_write_stencil_span( GLcontext
*ctx
,
1026 GLuint n
, GLint x
, GLint y
,
1027 const GLstencil stencil
[] )
1029 if (ctx
->Buffer
->Stencil
) {
1030 GLstencil
*s
= STENCIL_ADDRESS( x
, y
);
1031 #if STENCIL_BITS == 8
1032 MEMCPY( s
, stencil
, n
* sizeof(GLstencil
) );
1044 * Allocate a new stencil buffer. If there's an old one it will be
1045 * deallocated first. The new stencil buffer will be uninitialized.
1047 void gl_alloc_stencil_buffer( GLcontext
*ctx
)
1049 GLuint buffersize
= ctx
->Buffer
->Width
* ctx
->Buffer
->Height
;
1051 /* deallocate current stencil buffer if present */
1052 if (ctx
->Buffer
->Stencil
) {
1053 free(ctx
->Buffer
->Stencil
);
1054 ctx
->Buffer
->Stencil
= NULL
;
1057 /* allocate new stencil buffer */
1058 ctx
->Buffer
->Stencil
= (GLstencil
*) malloc(buffersize
* sizeof(GLstencil
));
1059 if (!ctx
->Buffer
->Stencil
) {
1061 gl_set_enable( ctx
, GL_STENCIL_TEST
, GL_FALSE
);
1062 gl_error( ctx
, GL_OUT_OF_MEMORY
, "gl_alloc_stencil_buffer" );
1070 * Clear the stencil buffer. If the stencil buffer doesn't exist yet we'll
1073 void gl_clear_stencil_buffer( GLcontext
*ctx
)
1075 if (ctx
->Visual
->StencilBits
==0 || !ctx
->Buffer
->Stencil
) {
1076 /* no stencil buffer */
1080 if (ctx
->Scissor
.Enabled
) {
1081 /* clear scissor region only */
1083 GLint width
= ctx
->Buffer
->Xmax
- ctx
->Buffer
->Xmin
+ 1;
1084 for (y
=ctx
->Buffer
->Ymin
; y
<=ctx
->Buffer
->Ymax
; y
++) {
1085 GLstencil
*ptr
= STENCIL_ADDRESS( ctx
->Buffer
->Xmin
, y
);
1087 MEMSET( ptr
, ctx
->Stencil
.Clear
, width
* sizeof(GLstencil
) );
1090 for (x
= 0; x
< width
; x
++)
1091 ptr
[x
] = ctx
->Stencil
.Clear
;
1096 /* clear whole stencil buffer */
1098 MEMSET( ctx
->Buffer
->Stencil
, ctx
->Stencil
.Clear
,
1099 ctx
->Buffer
->Width
* ctx
->Buffer
->Height
* sizeof(GLstencil
) );
1102 GLuint pixels
= ctx
->Buffer
->Width
* ctx
->Buffer
->Height
;
1103 GLstencil
*buffer
= ctx
->Buffer
->Stencil
;
1104 for (i
= 0; i
< pixels
; i
++)
1105 ptr
[i
] = ctx
->Stencil
.Clear
;