source: trunk/src/opengl/mesa/stencil.c

Last change on this file was 3598, checked in by jeroen, 25 years ago

* empty log message *

File size: 40.0 KB
Line 
1/* $Id: stencil.c,v 1.3 2000-05-23 20:40:54 jeroen Exp $ */
2
3/*
4 * Mesa 3-D graphics library
5 * Version: 3.3
6 *
7 * Copyright (C) 1999 Brian Paul All Rights Reserved.
8 *
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:
15 *
16 * The above copyright notice and this permission notice shall be included
17 * in all copies or substantial portions of the Software.
18 *
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.
25 */
26
27
28/* $XFree86: xc/lib/GL/mesa/src/stencil.c,v 1.3 1999/04/04 00:20:32 dawes Exp $ */
29
30#ifdef PC_HEADER
31#include "all.h"
32#else
33#include "glheader.h"
34#include "types.h"
35#include "context.h"
36#include "macros.h"
37#include "pb.h"
38#include "stencil.h"
39#include "enable.h"
40#include "mem.h"
41#include "depth.h"
42#endif
43
44
45
46void
47_mesa_ClearStencil( GLint s )
48{
49 GET_CURRENT_CONTEXT(ctx);
50 ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx, "glClearStencil");
51 ctx->Stencil.Clear = (GLstencil) s;
52
53 if (ctx->Driver.ClearStencil) {
54 (*ctx->Driver.ClearStencil)( ctx, s );
55 }
56}
57
58
59
60void
61_mesa_StencilFunc( GLenum func, GLint ref, GLuint mask )
62{
63 GET_CURRENT_CONTEXT(ctx);
64 GLint maxref;
65
66 ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx, "glStencilFunc");
67
68 switch (func) {
69 case GL_NEVER:
70 case GL_LESS:
71 case GL_LEQUAL:
72 case GL_GREATER:
73 case GL_GEQUAL:
74 case GL_EQUAL:
75 case GL_NOTEQUAL:
76 case GL_ALWAYS:
77 ctx->Stencil.Function = func;
78 break;
79 default:
80 gl_error( ctx, GL_INVALID_ENUM, "glStencilFunc" );
81 return;
82 }
83
84 maxref = (1 << STENCIL_BITS) - 1;
85 ctx->Stencil.Ref = (GLstencil) CLAMP( ref, 0, maxref );
86 ctx->Stencil.ValueMask = (GLstencil) mask;
87
88 if (ctx->Driver.StencilFunc) {
89 (*ctx->Driver.StencilFunc)( ctx, func, ctx->Stencil.Ref, mask );
90 }
91}
92
93
94
95void
96_mesa_StencilMask( GLuint mask )
97{
98 GET_CURRENT_CONTEXT(ctx);
99 ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx, "glStencilMask");
100 ctx->Stencil.WriteMask = (GLstencil) mask;
101
102 if (ctx->Driver.StencilMask) {
103 (*ctx->Driver.StencilMask)( ctx, mask );
104 }
105}
106
107
108
109void
110_mesa_StencilOp( GLenum fail, GLenum zfail, GLenum zpass )
111{
112 GET_CURRENT_CONTEXT(ctx);
113 ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx, "glStencilOp");
114 switch (fail) {
115 case GL_KEEP:
116 case GL_ZERO:
117 case GL_REPLACE:
118 case GL_INCR:
119 case GL_DECR:
120 case GL_INVERT:
121 case GL_INCR_WRAP_EXT:
122 case GL_DECR_WRAP_EXT:
123 ctx->Stencil.FailFunc = fail;
124 break;
125 default:
126 gl_error( ctx, GL_INVALID_ENUM, "glStencilOp" );
127 return;
128 }
129 switch (zfail) {
130 case GL_KEEP:
131 case GL_ZERO:
132 case GL_REPLACE:
133 case GL_INCR:
134 case GL_DECR:
135 case GL_INVERT:
136 case GL_INCR_WRAP_EXT:
137 case GL_DECR_WRAP_EXT:
138 ctx->Stencil.ZFailFunc = zfail;
139 break;
140 default:
141 gl_error( ctx, GL_INVALID_ENUM, "glStencilOp" );
142 return;
143 }
144 switch (zpass) {
145 case GL_KEEP:
146 case GL_ZERO:
147 case GL_REPLACE:
148 case GL_INCR:
149 case GL_DECR:
150 case GL_INVERT:
151 case GL_INCR_WRAP_EXT:
152 case GL_DECR_WRAP_EXT:
153 ctx->Stencil.ZPassFunc = zpass;
154 break;
155 default:
156 gl_error( ctx, GL_INVALID_ENUM, "glStencilOp" );
157 return;
158 }
159
160 if (ctx->Driver.StencilOp) {
161 (*ctx->Driver.StencilOp)( ctx, fail, zfail, zpass );
162 }
163}
164
165
166
167/* Stencil Logic:
168
169IF stencil test fails THEN
170 Apply fail-op to stencil value
171 Don't write the pixel (RGBA,Z)
172ELSE
173 IF doing depth test && depth test fails THEN
174 Apply zfail-op to stencil value
175 Write RGBA and Z to appropriate buffers
176 ELSE
177 Apply zpass-op to stencil value
178ENDIF
179
180*/
181
182
183
184
185/*
186 * Return the address of a stencil buffer value given the window coords:
187 */
188#define STENCIL_ADDRESS(X,Y) \
189 (ctx->DrawBuffer->Stencil + ctx->DrawBuffer->Width * (Y) + (X))
190
191
192
193/*
194 * Apply the given stencil operator to the array of stencil values.
195 * Don't touch stencil[i] if mask[i] is zero.
196 * Input: n - size of stencil array
197 * oper - the stencil buffer operator
198 * stencil - array of stencil values
199 * mask - array [n] of flag: 1=apply operator, 0=don't apply operator
200 * Output: stencil - modified values
201 */
202static void apply_stencil_op( const GLcontext *ctx, GLenum oper,
203 GLuint n, GLstencil stencil[],
204 const GLubyte mask[] )
205{
206 const GLstencil ref = ctx->Stencil.Ref;
207 const GLstencil wrtmask = ctx->Stencil.WriteMask;
208 const GLstencil invmask = (GLstencil) (~ctx->Stencil.WriteMask);
209 GLuint i;
210
211 switch (oper) {
212 case GL_KEEP:
213 /* do nothing */
214 break;
215 case GL_ZERO:
216 if (invmask==0) {
217 for (i=0;i<n;i++) {
218 if (mask[i]) {
219 stencil[i] = 0;
220 }
221 }
222 }
223 else {
224 for (i=0;i<n;i++) {
225 if (mask[i]) {
226 stencil[i] = (GLstencil) (stencil[i] & invmask);
227 }
228 }
229 }
230 break;
231 case GL_REPLACE:
232 if (invmask==0) {
233 for (i=0;i<n;i++) {
234 if (mask[i]) {
235 stencil[i] = ref;
236 }
237 }
238 }
239 else {
240 for (i=0;i<n;i++) {
241 if (mask[i]) {
242 GLstencil s = stencil[i];
243 stencil[i] = (GLstencil) ((invmask & s ) | (wrtmask & ref));
244 }
245 }
246 }
247 break;
248 case GL_INCR:
249 if (invmask==0) {
250 for (i=0;i<n;i++) {
251 if (mask[i]) {
252 GLstencil s = stencil[i];
253 if (s < STENCIL_MAX) {
254 stencil[i] = (GLstencil) (s+1);
255 }
256 }
257 }
258 }
259 else {
260 for (i=0;i<n;i++) {
261 if (mask[i]) {
262 /* VERIFY logic of adding 1 to a write-masked value */
263 GLstencil s = stencil[i];
264 if (s < STENCIL_MAX) {
265 stencil[i] = (GLstencil) ((invmask & s) | (wrtmask & (s+1)));
266 }
267 }
268 }
269 }
270 break;
271 case GL_DECR:
272 if (invmask==0) {
273 for (i=0;i<n;i++) {
274 if (mask[i]) {
275 GLstencil s = stencil[i];
276 if (s>0) {
277 stencil[i] = (GLstencil) (s-1);
278 }
279 }
280 }
281 }
282 else {
283 for (i=0;i<n;i++) {
284 if (mask[i]) {
285 /* VERIFY logic of subtracting 1 to a write-masked value */
286 GLstencil s = stencil[i];
287 if (s>0) {
288 stencil[i] = (GLstencil) ((invmask & s) | (wrtmask & (s-1)));
289 }
290 }
291 }
292 }
293 break;
294 case GL_INCR_WRAP_EXT:
295 if (invmask==0) {
296 for (i=0;i<n;i++) {
297 if (mask[i]) {
298 stencil[i]++;
299 }
300 }
301 }
302 else {
303 for (i=0;i<n;i++) {
304 if (mask[i]) {
305 GLstencil s = stencil[i];
306 stencil[i] = (GLstencil) ((invmask & s) | (wrtmask & (s+1)));
307 }
308 }
309 }
310 break;
311 case GL_DECR_WRAP_EXT:
312 if (invmask==0) {
313 for (i=0;i<n;i++) {
314 if (mask[i]) {
315 stencil[i]--;
316 }
317 }
318 }
319 else {
320 for (i=0;i<n;i++) {
321 if (mask[i]) {
322 GLstencil s = stencil[i];
323 stencil[i] = (GLstencil) ((invmask & s) | (wrtmask & (s-1)));
324 }
325 }
326 }
327 break;
328 case GL_INVERT:
329 if (invmask==0) {
330 for (i=0;i<n;i++) {
331 if (mask[i]) {
332 GLstencil s = stencil[i];
333 stencil[i] = (GLstencil) ~s;
334 }
335 }
336 }
337 else {
338 for (i=0;i<n;i++) {
339 if (mask[i]) {
340 GLstencil s = stencil[i];
341 stencil[i] = (GLstencil) ((invmask & s) | (wrtmask & ~s));
342 }
343 }
344 }
345 break;
346 default:
347 gl_problem(ctx, "Bad stencil op in apply_stencil_op");
348 }
349}
350
351
352
353/*
354 * Apply stencil test to an array of stencil values (before depth buffering).
355 * Input: n - number of pixels in the array
356 * stencil - array of [n] stencil values
357 * mask - array [n] of flag: 0=skip the pixel, 1=stencil the pixel
358 * Output: mask - pixels which fail the stencil test will have their
359 * mask flag set to 0.
360 * stencil - updated stencil values (where the test passed)
361 * Return: GL_FALSE = all pixels failed, GL_TRUE = zero or more pixels passed.
362 */
363static GLboolean
364do_stencil_test( GLcontext *ctx, GLuint n, GLstencil stencil[],
365 GLubyte mask[] )
366{
367 GLubyte fail[PB_SIZE];
368 GLboolean allfail = GL_FALSE;
369 GLuint i;
370 GLstencil r, s;
371
372 ASSERT(n <= PB_SIZE);
373
374 /*
375 * Perform stencil test. The results of this operation are stored
376 * in the fail[] array:
377 * IF fail[i] is non-zero THEN
378 * the stencil fail operator is to be applied
379 * ELSE
380 * the stencil fail operator is not to be applied
381 * ENDIF
382 */
383 switch (ctx->Stencil.Function) {
384 case GL_NEVER:
385 /* always fail */
386 for (i=0;i<n;i++) {
387 if (mask[i]) {
388 mask[i] = 0;
389 fail[i] = 1;
390 }
391 else {
392 fail[i] = 0;
393 }
394 }
395 allfail = GL_TRUE;
396 break;
397 case GL_LESS:
398 r = (GLstencil) (ctx->Stencil.Ref & ctx->Stencil.ValueMask);
399 for (i=0;i<n;i++) {
400 if (mask[i]) {
401 s = (GLstencil) (stencil[i] & ctx->Stencil.ValueMask);
402 if (r < s) {
403 /* passed */
404 fail[i] = 0;
405 }
406 else {
407 fail[i] = 1;
408 mask[i] = 0;
409 }
410 }
411 else {
412 fail[i] = 0;
413 }
414 }
415 break;
416 case GL_LEQUAL:
417 r = (GLstencil) (ctx->Stencil.Ref & ctx->Stencil.ValueMask);
418 for (i=0;i<n;i++) {
419 if (mask[i]) {
420 s = (GLstencil) (stencil[i] & ctx->Stencil.ValueMask);
421 if (r <= s) {
422 /* pass */
423 fail[i] = 0;
424 }
425 else {
426 fail[i] = 1;
427 mask[i] = 0;
428 }
429 }
430 else {
431 fail[i] = 0;
432 }
433 }
434 break;
435 case GL_GREATER:
436 r = (GLstencil) (ctx->Stencil.Ref & ctx->Stencil.ValueMask);
437 for (i=0;i<n;i++) {
438 if (mask[i]) {
439 s = (GLstencil) (stencil[i] & ctx->Stencil.ValueMask);
440 if (r > s) {
441 /* passed */
442 fail[i] = 0;
443 }
444 else {
445 fail[i] = 1;
446 mask[i] = 0;
447 }
448 }
449 else {
450 fail[i] = 0;
451 }
452 }
453 break;
454 case GL_GEQUAL:
455 r = (GLstencil) (ctx->Stencil.Ref & ctx->Stencil.ValueMask);
456 for (i=0;i<n;i++) {
457 if (mask[i]) {
458 s = (GLstencil) (stencil[i] & ctx->Stencil.ValueMask);
459 if (r >= s) {
460 /* passed */
461 fail[i] = 0;
462 }
463 else {
464 fail[i] = 1;
465 mask[i] = 0;
466 }
467 }
468 else {
469 fail[i] = 0;
470 }
471 }
472 break;
473 case GL_EQUAL:
474 r = (GLstencil) (ctx->Stencil.Ref & ctx->Stencil.ValueMask);
475 for (i=0;i<n;i++) {
476 if (mask[i]) {
477 s = (GLstencil) (stencil[i] & ctx->Stencil.ValueMask);
478 if (r == s) {
479 /* passed */
480 fail[i] = 0;
481 }
482 else {
483 fail[i] = 1;
484 mask[i] = 0;
485 }
486 }
487 else {
488 fail[i] = 0;
489 }
490 }
491 break;
492 case GL_NOTEQUAL:
493 r = (GLstencil) (ctx->Stencil.Ref & ctx->Stencil.ValueMask);
494 for (i=0;i<n;i++) {
495 if (mask[i]) {
496 s = (GLstencil) (stencil[i] & ctx->Stencil.ValueMask);
497 if (r != s) {
498 /* passed */
499 fail[i] = 0;
500 }
501 else {
502 fail[i] = 1;
503 mask[i] = 0;
504 }
505 }
506 else {
507 fail[i] = 0;
508 }
509 }
510 break;
511 case GL_ALWAYS:
512 /* always pass */
513 for (i=0;i<n;i++) {
514 fail[i] = 0;
515 }
516 break;
517 default:
518 gl_problem(ctx, "Bad stencil func in gl_stencil_span");
519 return 0;
520 }
521
522 if (ctx->Stencil.FailFunc != GL_KEEP) {
523 apply_stencil_op( ctx, ctx->Stencil.FailFunc, n, stencil, fail );
524 }
525
526 return !allfail;
527}
528
529
530
531
532/*
533 * Apply stencil and depth testing to an array of pixels.
534 * Hardware or software stencil buffer acceptable.
535 * Input: n - number of pixels in the span
536 * z - array [n] of z values
537 * stencil - array [n] of stencil values
538 * mask - array [n] of flags (1=test this pixel, 0=skip the pixel)
539 * Output: stencil - modified stencil values
540 * mask - array [n] of flags (1=stencil and depth test passed)
541 * Return: GL_TRUE - all fragments failed the testing
542 * GL_FALSE - one or more fragments passed the testing
543 *
544 */
545static GLboolean
546stencil_and_depth_test_span( GLcontext *ctx, GLuint n, GLint x, GLint y,
547 const GLdepth z[], GLstencil stencil[],
548 GLubyte mask[] )
549{
550 ASSERT(ctx->Stencil.Enabled);
551 ASSERT(n <= PB_SIZE);
552
553 /*
554 * Apply the stencil test to the fragments.
555 * failMask[i] is 1 if the stencil test failed.
556 */
557 if (do_stencil_test( ctx, n, stencil, mask ) == GL_FALSE) {
558 /* all fragments failed the stencil test, we're done. */
559 return GL_FALSE;
560 }
561
562
563 /*
564 * Some fragments passed the stencil test, apply depth test to them
565 * and apply Zpass and Zfail stencil ops.
566 */
567 if (ctx->Depth.Test==GL_FALSE) {
568 /*
569 * No depth buffer, just apply zpass stencil function to active pixels.
570 */
571 apply_stencil_op( ctx, ctx->Stencil.ZPassFunc, n, stencil, mask );
572 }
573 else {
574 /*
575 * Perform depth buffering, then apply zpass or zfail stencil function.
576 */
577 GLubyte passmask[MAX_WIDTH], failmask[MAX_WIDTH], oldmask[MAX_WIDTH];
578 GLuint i;
579
580 /* save the current mask bits */
581 MEMCPY(oldmask, mask, n * sizeof(GLubyte));
582
583 /* apply the depth test */
584 _mesa_depth_test_span(ctx, n, x, y, z, mask);
585
586 /* Set the stencil pass/fail flags according to result of depth testing.
587 * if oldmask[i] == 0 then
588 * Don't touch the stencil value
589 * else if oldmask[i] and newmask[i] then
590 * Depth test passed
591 * else
592 * ASSERT(oldmask[i] && !newmask[i])
593 * Depth test failed
594 * endif
595 */
596 for (i=0;i<n;i++) {
597 ASSERT(mask[i] == 0 || mask[i] == 1);
598 passmask[i] = oldmask[i] & mask[i];
599 failmask[i] = oldmask[i] & (mask[i] ^ 1);
600 }
601
602 /* apply the pass and fail operations */
603 if (ctx->Stencil.ZFailFunc != GL_KEEP) {
604 apply_stencil_op( ctx, ctx->Stencil.ZFailFunc, n, stencil, failmask );
605 }
606 if (ctx->Stencil.ZPassFunc != GL_KEEP) {
607 apply_stencil_op( ctx, ctx->Stencil.ZPassFunc, n, stencil, passmask );
608 }
609 }
610
611 return GL_TRUE; /* one or more fragments passed both tests */
612}
613
614
615
616/*
617 * Apply stencil and depth testing to the span of pixels.
618 * Both software and hardware stencil buffers are acceptable.
619 * Input: n - number of pixels in the span
620 * x, y - location of leftmost pixel in span
621 * z - array [n] of z values
622 * mask - array [n] of flags (1=test this pixel, 0=skip the pixel)
623 * Output: mask - array [n] of flags (1=stencil and depth test passed)
624 * Return: GL_TRUE - all fragments failed the testing
625 * GL_FALSE - one or more fragments passed the testing
626 *
627 */
628GLboolean
629gl_stencil_and_depth_test_span( GLcontext *ctx, GLuint n, GLint x, GLint y,
630 const GLdepth z[], GLubyte mask[] )
631{
632 GLstencil stencilRow[MAX_WIDTH];
633 GLstencil *stencil;
634 GLboolean result;
635
636 ASSERT(ctx->Stencil.Enabled);
637 ASSERT(n <= MAX_WIDTH);
638
639 /* Get initial stencil values */
640 if (ctx->Driver.WriteStencilSpan) {
641 ASSERT(ctx->Driver.ReadStencilSpan);
642 /* Get stencil values from the hardware stencil buffer */
643 (*ctx->Driver.ReadStencilSpan)(ctx, n, x, y, stencilRow);
644 stencil = stencilRow;
645 }
646 else {
647 /* software stencil buffer */
648 stencil = STENCIL_ADDRESS(x, y);
649 }
650
651 /* do all the stencil/depth testing/updating */
652 result = stencil_and_depth_test_span( ctx, n, x, y, z, stencil, mask );
653
654 if (ctx->Driver.WriteStencilSpan) {
655 /* Write updated stencil values into hardware stencil buffer */
656 (ctx->Driver.WriteStencilSpan)(ctx, n, x, y, stencil, mask );
657 }
658
659 return result;
660}
661
662
663
664
665/*
666 * Apply the given stencil operator for each pixel in the array whose
667 * mask flag is set. This is for software stencil buffers only.
668 * Input: n - number of pixels in the span
669 * x, y - array of [n] pixels
670 * operator - the stencil buffer operator
671 * mask - array [n] of flag: 1=apply operator, 0=don't apply operator
672 */
673static void
674apply_stencil_op_to_pixels( const GLcontext *ctx,
675 GLuint n, const GLint x[], const GLint y[],
676 GLenum oper, const GLubyte mask[] )
677{
678 const GLstencil ref = ctx->Stencil.Ref;
679 const GLstencil wrtmask = ctx->Stencil.WriteMask;
680 const GLstencil invmask = (GLstencil) (~ctx->Stencil.WriteMask);
681 GLuint i;
682
683 ASSERT(!ctx->Driver.WriteStencilSpan); /* software stencil buffer only! */
684
685 switch (oper) {
686 case GL_KEEP:
687 /* do nothing */
688 break;
689 case GL_ZERO:
690 if (invmask==0) {
691 for (i=0;i<n;i++) {
692 if (mask[i]) {
693 GLstencil *sptr = STENCIL_ADDRESS( x[i], y[i] );
694 *sptr = 0;
695 }
696 }
697 }
698 else {
699 for (i=0;i<n;i++) {
700 if (mask[i]) {
701 GLstencil *sptr = STENCIL_ADDRESS( x[i], y[i] );
702 *sptr = (GLstencil) (invmask & *sptr);
703 }
704 }
705 }
706 break;
707 case GL_REPLACE:
708 if (invmask==0) {
709 for (i=0;i<n;i++) {
710 if (mask[i]) {
711 GLstencil *sptr = STENCIL_ADDRESS( x[i], y[i] );
712 *sptr = ref;
713 }
714 }
715 }
716 else {
717 for (i=0;i<n;i++) {
718 if (mask[i]) {
719 GLstencil *sptr = STENCIL_ADDRESS( x[i], y[i] );
720 *sptr = (GLstencil) ((invmask & *sptr ) | (wrtmask & ref));
721 }
722 }
723 }
724 break;
725 case GL_INCR:
726 if (invmask==0) {
727 for (i=0;i<n;i++) {
728 if (mask[i]) {
729 GLstencil *sptr = STENCIL_ADDRESS( x[i], y[i] );
730 if (*sptr < STENCIL_MAX) {
731 *sptr = (GLstencil) (*sptr + 1);
732 }
733 }
734 }
735 }
736 else {
737 for (i=0;i<n;i++) {
738 if (mask[i]) {
739 GLstencil *sptr = STENCIL_ADDRESS( x[i], y[i] );
740 if (*sptr < STENCIL_MAX) {
741 *sptr = (GLstencil) ((invmask & *sptr) | (wrtmask & (*sptr+1)));
742 }
743 }
744 }
745 }
746 break;
747 case GL_DECR:
748 if (invmask==0) {
749 for (i=0;i<n;i++) {
750 if (mask[i]) {
751 GLstencil *sptr = STENCIL_ADDRESS( x[i], y[i] );
752 if (*sptr>0) {
753 *sptr = (GLstencil) (*sptr - 1);
754 }
755 }
756 }
757 }
758 else {
759 for (i=0;i<n;i++) {
760 if (mask[i]) {
761 GLstencil *sptr = STENCIL_ADDRESS( x[i], y[i] );
762 if (*sptr>0) {
763 *sptr = (GLstencil) ((invmask & *sptr) | (wrtmask & (*sptr-1)));
764 }
765 }
766 }
767 }
768 break;
769 case GL_INCR_WRAP_EXT:
770 if (invmask==0) {
771 for (i=0;i<n;i++) {
772 if (mask[i]) {
773 GLstencil *sptr = STENCIL_ADDRESS( x[i], y[i] );
774 *sptr = (GLstencil) (*sptr + 1);
775 }
776 }
777 }
778 else {
779 for (i=0;i<n;i++) {
780 if (mask[i]) {
781 GLstencil *sptr = STENCIL_ADDRESS( x[i], y[i] );
782 *sptr = (GLstencil) ((invmask & *sptr) | (wrtmask & (*sptr+1)));
783 }
784 }
785 }
786 break;
787 case GL_DECR_WRAP_EXT:
788 if (invmask==0) {
789 for (i=0;i<n;i++) {
790 if (mask[i]) {
791 GLstencil *sptr = STENCIL_ADDRESS( x[i], y[i] );
792 *sptr = (GLstencil) (*sptr - 1);
793 }
794 }
795 }
796 else {
797 for (i=0;i<n;i++) {
798 if (mask[i]) {
799 GLstencil *sptr = STENCIL_ADDRESS( x[i], y[i] );
800 *sptr = (GLstencil) ((invmask & *sptr) | (wrtmask & (*sptr-1)));
801 }
802 }
803 }
804 break;
805 case GL_INVERT:
806 if (invmask==0) {
807 for (i=0;i<n;i++) {
808 if (mask[i]) {
809 GLstencil *sptr = STENCIL_ADDRESS( x[i], y[i] );
810 *sptr = (GLstencil) (~*sptr);
811 }
812 }
813 }
814 else {
815 for (i=0;i<n;i++) {
816 if (mask[i]) {
817 GLstencil *sptr = STENCIL_ADDRESS( x[i], y[i] );
818 *sptr = (GLstencil) ((invmask & *sptr) | (wrtmask & ~*sptr));
819 }
820 }
821 }
822 break;
823 default:
824 gl_problem(ctx, "Bad stencilop in apply_stencil_op_to_pixels");
825 }
826}
827
828
829
830/*
831 * Apply stencil test to an array of pixels before depth buffering.
832 * Used for software stencil buffer only.
833 * Input: n - number of pixels in the span
834 * x, y - array of [n] pixels to stencil
835 * mask - array [n] of flag: 0=skip the pixel, 1=stencil the pixel
836 * Output: mask - pixels which fail the stencil test will have their
837 * mask flag set to 0.
838 * Return: 0 = all pixels failed, 1 = zero or more pixels passed.
839 */
840static GLboolean
841stencil_test_pixels( GLcontext *ctx, GLuint n,
842 const GLint x[], const GLint y[], GLubyte mask[] )
843{
844 GLubyte fail[PB_SIZE];
845 GLstencil r, s;
846 GLuint i;
847 GLboolean allfail = GL_FALSE;
848
849 ASSERT(!ctx->Driver.WriteStencilSpan); /* software stencil buffer only! */
850
851 /*
852 * Perform stencil test. The results of this operation are stored
853 * in the fail[] array:
854 * IF fail[i] is non-zero THEN
855 * the stencil fail operator is to be applied
856 * ELSE
857 * the stencil fail operator is not to be applied
858 * ENDIF
859 */
860
861 switch (ctx->Stencil.Function) {
862 case GL_NEVER:
863 /* always fail */
864 for (i=0;i<n;i++) {
865 if (mask[i]) {
866 mask[i] = 0;
867 fail[i] = 1;
868 }
869 else {
870 fail[i] = 0;
871 }
872 }
873 allfail = GL_TRUE;
874 break;
875 case GL_LESS:
876 r = (GLstencil) (ctx->Stencil.Ref & ctx->Stencil.ValueMask);
877 for (i=0;i<n;i++) {
878 if (mask[i]) {
879 GLstencil *sptr = STENCIL_ADDRESS(x[i],y[i]);
880 s = (GLstencil) (*sptr & ctx->Stencil.ValueMask);
881 if (r < s) {
882 /* passed */
883 fail[i] = 0;
884 }
885 else {
886 fail[i] = 1;
887 mask[i] = 0;
888 }
889 }
890 else {
891 fail[i] = 0;
892 }
893 }
894 break;
895 case GL_LEQUAL:
896 r = (GLstencil) (ctx->Stencil.Ref & ctx->Stencil.ValueMask);
897 for (i=0;i<n;i++) {
898 if (mask[i]) {
899 GLstencil *sptr = STENCIL_ADDRESS(x[i],y[i]);
900 s = (GLstencil) (*sptr & ctx->Stencil.ValueMask);
901 if (r <= s) {
902 /* pass */
903 fail[i] = 0;
904 }
905 else {
906 fail[i] = 1;
907 mask[i] = 0;
908 }
909 }
910 else {
911 fail[i] = 0;
912 }
913 }
914 break;
915 case GL_GREATER:
916 r = (GLstencil) (ctx->Stencil.Ref & ctx->Stencil.ValueMask);
917 for (i=0;i<n;i++) {
918 if (mask[i]) {
919 GLstencil *sptr = STENCIL_ADDRESS(x[i],y[i]);
920 s = (GLstencil) (*sptr & ctx->Stencil.ValueMask);
921 if (r > s) {
922 /* passed */
923 fail[i] = 0;
924 }
925 else {
926 fail[i] = 1;
927 mask[i] = 0;
928 }
929 }
930 else {
931 fail[i] = 0;
932 }
933 }
934 break;
935 case GL_GEQUAL:
936 r = (GLstencil) (ctx->Stencil.Ref & ctx->Stencil.ValueMask);
937 for (i=0;i<n;i++) {
938 if (mask[i]) {
939 GLstencil *sptr = STENCIL_ADDRESS(x[i],y[i]);
940 s = (GLstencil) (*sptr & ctx->Stencil.ValueMask);
941 if (r >= s) {
942 /* passed */
943 fail[i] = 0;
944 }
945 else {
946 fail[i] = 1;
947 mask[i] = 0;
948 }
949 }
950 else {
951 fail[i] = 0;
952 }
953 }
954 break;
955 case GL_EQUAL:
956 r = (GLstencil) (ctx->Stencil.Ref & ctx->Stencil.ValueMask);
957 for (i=0;i<n;i++) {
958 if (mask[i]) {
959 GLstencil *sptr = STENCIL_ADDRESS(x[i],y[i]);
960 s = (GLstencil) (*sptr & ctx->Stencil.ValueMask);
961 if (r == s) {
962 /* passed */
963 fail[i] = 0;
964 }
965 else {
966 fail[i] = 1;
967 mask[i] = 0;
968 }
969 }
970 else {
971 fail[i] = 0;
972 }
973 }
974 break;
975 case GL_NOTEQUAL:
976 r = (GLstencil) (ctx->Stencil.Ref & ctx->Stencil.ValueMask);
977 for (i=0;i<n;i++) {
978 if (mask[i]) {
979 GLstencil *sptr = STENCIL_ADDRESS(x[i],y[i]);
980 s = (GLstencil) (*sptr & ctx->Stencil.ValueMask);
981 if (r != s) {
982 /* passed */
983 fail[i] = 0;
984 }
985 else {
986 fail[i] = 1;
987 mask[i] = 0;
988 }
989 }
990 else {
991 fail[i] = 0;
992 }
993 }
994 break;
995 case GL_ALWAYS:
996 /* always pass */
997 for (i=0;i<n;i++) {
998 fail[i] = 0;
999 }
1000 break;
1001 default:
1002 gl_problem(ctx, "Bad stencil func in gl_stencil_pixels");
1003 return 0;
1004 }
1005
1006 if (ctx->Stencil.FailFunc != GL_KEEP) {
1007 apply_stencil_op_to_pixels( ctx, n, x, y, ctx->Stencil.FailFunc, fail );
1008 }
1009
1010 return !allfail;
1011}
1012
1013
1014
1015
1016/*
1017 * Apply stencil and depth testing to an array of pixels.
1018 * This is used both for software and hardware stencil buffers.
1019 *
1020 * The comments in this function are a bit sparse but the code is
1021 * almost identical to stencil_and_depth_test_span(), which is well
1022 * commented.
1023 *
1024 * Input: n - number of pixels in the array
1025 * x, y - array of [n] pixel positions
1026 * z - array [n] of z values
1027 * mask - array [n] of flags (1=test this pixel, 0=skip the pixel)
1028 * Output: mask - array [n] of flags (1=stencil and depth test passed)
1029 * Return: GL_TRUE - all fragments failed the testing
1030 * GL_FALSE - one or more fragments passed the testing
1031 */
1032GLboolean
1033gl_stencil_and_depth_test_pixels( GLcontext *ctx,
1034 GLuint n, const GLint x[], const GLint y[],
1035 const GLdepth z[], GLubyte mask[] )
1036{
1037 ASSERT(ctx->Stencil.Enabled);
1038 ASSERT(n <= PB_SIZE);
1039
1040 if (ctx->Driver.WriteStencilPixels) {
1041 /*** Hardware stencil buffer ***/
1042 GLstencil stencil[PB_SIZE];
1043 GLubyte mask[PB_SIZE];
1044
1045 ASSERT(ctx->Driver.ReadStencilPixels);
1046 (*ctx->Driver.ReadStencilPixels)(ctx, n, x, y, stencil);
1047
1048
1049 if (do_stencil_test( ctx, n, stencil, mask ) == GL_FALSE) {
1050 /* all fragments failed the stencil test, we're done. */
1051 return GL_FALSE;
1052 }
1053
1054 if (ctx->Depth.Test == GL_FALSE) {
1055 apply_stencil_op( ctx, ctx->Stencil.ZPassFunc, n, stencil, mask );
1056 }
1057 else {
1058 GLubyte passmask[PB_SIZE], failmask[PB_SIZE], oldmask[PB_SIZE];
1059 GLuint i;
1060
1061 MEMCPY(oldmask, mask, n * sizeof(GLubyte));
1062
1063 _mesa_depth_test_pixels(ctx, n, x, y, z, mask);
1064
1065 for (i=0;i<n;i++) {
1066 ASSERT(mask[i] == 0 || mask[i] == 1);
1067 passmask[i] = oldmask[i] & mask[i];
1068 failmask[i] = oldmask[i] & (mask[i] ^ 1);
1069 }
1070
1071 if (ctx->Stencil.ZFailFunc != GL_KEEP) {
1072 apply_stencil_op( ctx, ctx->Stencil.ZFailFunc, n, stencil, failmask );
1073 }
1074 if (ctx->Stencil.ZPassFunc != GL_KEEP) {
1075 apply_stencil_op( ctx, ctx->Stencil.ZPassFunc, n, stencil, passmask );
1076 }
1077 }
1078
1079 /* Write updated stencil values into hardware stencil buffer */
1080 (ctx->Driver.WriteStencilPixels)(ctx, n, x, y, stencil, mask );
1081
1082 return GL_TRUE;
1083
1084 }
1085 else {
1086 /*** Software stencil buffer ***/
1087
1088 if (stencil_test_pixels(ctx, n, x, y, mask) == GL_FALSE) {
1089 /* all fragments failed the stencil test, we're done. */
1090 return GL_FALSE;
1091 }
1092
1093
1094 if (ctx->Depth.Test==GL_FALSE) {
1095 apply_stencil_op_to_pixels( ctx, n, x, y, ctx->Stencil.ZPassFunc, mask );
1096 }
1097 else {
1098 GLubyte passmask[PB_SIZE], failmask[PB_SIZE], oldmask[PB_SIZE];
1099 GLuint i;
1100
1101 MEMCPY(oldmask, mask, n * sizeof(GLubyte));
1102
1103 _mesa_depth_test_pixels(ctx, n, x, y, z, mask);
1104
1105 for (i=0;i<n;i++) {
1106 ASSERT(mask[i] == 0 || mask[i] == 1);
1107 passmask[i] = oldmask[i] & mask[i];
1108 failmask[i] = oldmask[i] & (mask[i] ^ 1);
1109 }
1110
1111 if (ctx->Stencil.ZFailFunc != GL_KEEP) {
1112 apply_stencil_op_to_pixels( ctx, n, x, y,
1113 ctx->Stencil.ZFailFunc, failmask );
1114 }
1115 if (ctx->Stencil.ZPassFunc != GL_KEEP) {
1116 apply_stencil_op_to_pixels( ctx, n, x, y,
1117 ctx->Stencil.ZPassFunc, passmask );
1118 }
1119 }
1120
1121 return GL_TRUE; /* one or more fragments passed both tests */
1122 }
1123}
1124
1125
1126
1127/*
1128 * Return a span of stencil values from the stencil buffer.
1129 * Used for glRead/CopyPixels
1130 * Input: n - how many pixels
1131 * x,y - location of first pixel
1132 * Output: stencil - the array of stencil values
1133 */
1134void gl_read_stencil_span( GLcontext *ctx,
1135 GLint n, GLint x, GLint y, GLstencil stencil[] )
1136{
1137 ASSERT(n >= 0);
1138 if (ctx->DrawBuffer->Stencil) {
1139 if (ctx->Driver.ReadStencilSpan) {
1140 (*ctx->Driver.ReadStencilSpan)( ctx, (GLuint) n, x, y, stencil );
1141 }
1142 else {
1143 const GLstencil *s = STENCIL_ADDRESS( x, y );
1144#if STENCIL_BITS == 8
1145 MEMCPY( stencil, s, n * sizeof(GLstencil) );
1146#else
1147 GLuint i;
1148 for (i=0;i<n;i++)
1149 stencil[i] = s[i];
1150#endif
1151 }
1152 }
1153}
1154
1155
1156
1157/*
1158 * Write a span of stencil values to the stencil buffer.
1159 * Used for glDraw/CopyPixels
1160 * Input: n - how many pixels
1161 * x, y - location of first pixel
1162 * stencil - the array of stencil values
1163 */
1164void gl_write_stencil_span( GLcontext *ctx,
1165 GLint n, GLint x, GLint y,
1166 const GLstencil stencil[] )
1167{
1168 ASSERT(n >= 0);
1169 if (ctx->DrawBuffer->Stencil) {
1170 /* do clipping */
1171 if (y < ctx->DrawBuffer->Ymin || y > ctx->DrawBuffer->Ymax)
1172 return;
1173 if (x < ctx->DrawBuffer->Xmin) {
1174 GLint diff = ctx->DrawBuffer->Xmin - x;
1175 n -= diff;
1176 stencil += diff;
1177 x = ctx->DrawBuffer->Xmin;
1178 }
1179 if (x + n > ctx->DrawBuffer->Xmax) {
1180 GLint diff = x + n - ctx->DrawBuffer->Xmax;
1181 n -= diff;
1182 }
1183
1184 ASSERT( n >= 0);
1185
1186 if (ctx->Driver.WriteStencilSpan) {
1187 (*ctx->Driver.WriteStencilSpan)( ctx, n, x, y, stencil, NULL );
1188 }
1189 else {
1190 GLstencil *s = STENCIL_ADDRESS( x, y );
1191#if STENCIL_BITS == 8
1192 MEMCPY( s, stencil, n * sizeof(GLstencil) );
1193#else
1194 GLuint i;
1195 for (i=0;i<n;i++)
1196 s[i] = stencil[i];
1197#endif
1198 }
1199 }
1200}
1201
1202
1203
1204/*
1205 * Allocate a new stencil buffer. If there's an old one it will be
1206 * deallocated first. The new stencil buffer will be uninitialized.
1207 */
1208void gl_alloc_stencil_buffer( GLcontext *ctx )
1209{
1210 GLuint buffersize = ctx->DrawBuffer->Width * ctx->DrawBuffer->Height;
1211
1212 /* deallocate current stencil buffer if present */
1213 if (ctx->DrawBuffer->Stencil) {
1214 FREE(ctx->DrawBuffer->Stencil);
1215 ctx->DrawBuffer->Stencil = NULL;
1216 }
1217
1218 /* allocate new stencil buffer */
1219 ctx->DrawBuffer->Stencil = (GLstencil *) MALLOC(buffersize * sizeof(GLstencil));
1220 if (!ctx->DrawBuffer->Stencil) {
1221 /* out of memory */
1222 _mesa_set_enable( ctx, GL_STENCIL_TEST, GL_FALSE );
1223 gl_error( ctx, GL_OUT_OF_MEMORY, "gl_alloc_stencil_buffer" );
1224 }
1225}
1226
1227
1228
1229/*
1230 * Clear the software (malloc'd) stencil buffer.
1231 */
1232static void
1233clear_software_stencil_buffer( GLcontext *ctx )
1234{
1235 if (ctx->Visual->StencilBits==0 || !ctx->DrawBuffer->Stencil) {
1236 /* no stencil buffer */
1237 return;
1238 }
1239
1240 if (ctx->Scissor.Enabled) {
1241 /* clear scissor region only */
1242 const GLint width = ctx->DrawBuffer->Xmax - ctx->DrawBuffer->Xmin + 1;
1243 if (ctx->Stencil.WriteMask != STENCIL_MAX) {
1244 /* must apply mask to the clear */
1245 GLint y;
1246 for (y = ctx->DrawBuffer->Ymin; y <= ctx->DrawBuffer->Ymax; y++) {
1247 const GLstencil mask = ctx->Stencil.WriteMask;
1248 const GLstencil invMask = ~mask;
1249 const GLstencil clearVal = (ctx->Stencil.Clear & mask);
1250 GLstencil *stencil = STENCIL_ADDRESS( ctx->DrawBuffer->Xmin, y );
1251 GLint i;
1252 for (i = 0; i < width; i++) {
1253 stencil[i] = (stencil[i] & invMask) | clearVal;
1254 }
1255 }
1256 }
1257 else {
1258 /* no masking */
1259 GLint y;
1260 for (y = ctx->DrawBuffer->Ymin; y <= ctx->DrawBuffer->Ymax; y++) {
1261 GLstencil *stencil = STENCIL_ADDRESS( ctx->DrawBuffer->Xmin, y );
1262#if STENCIL_BITS==8
1263 MEMSET( stencil, ctx->Stencil.Clear, width * sizeof(GLstencil) );
1264#else
1265 GLint i;
1266 for (i = 0; i < width; i++)
1267 stencil[x] = ctx->Stencil.Clear;
1268#endif
1269 }
1270 }
1271 }
1272 else {
1273 /* clear whole stencil buffer */
1274 if (ctx->Stencil.WriteMask != STENCIL_MAX) {
1275 /* must apply mask to the clear */
1276 const GLuint n = ctx->DrawBuffer->Width * ctx->DrawBuffer->Height;
1277 GLstencil *stencil = ctx->DrawBuffer->Stencil;
1278 const GLstencil mask = ctx->Stencil.WriteMask;
1279 const GLstencil invMask = ~mask;
1280 const GLstencil clearVal = (ctx->Stencil.Clear & mask);
1281 GLuint i;
1282 for (i = 0; i < n; i++) {
1283 stencil[i] = (stencil[i] & invMask) | clearVal;
1284 }
1285 }
1286 else {
1287 /* clear whole buffer without masking */
1288 const GLuint n = ctx->DrawBuffer->Width * ctx->DrawBuffer->Height;
1289 GLstencil *stencil = ctx->DrawBuffer->Stencil;
1290
1291#if STENCIL_BITS==8
1292 MEMSET(stencil, ctx->Stencil.Clear, n * sizeof(GLstencil) );
1293#else
1294 GLuint i;
1295 for (i = 0; i < n; i++) {
1296 stencil[i] = ctx->Stencil.Clear;
1297 }
1298#endif
1299 }
1300 }
1301}
1302
1303
1304
1305/*
1306 * Clear the hardware (in graphics card) stencil buffer.
1307 * This is done with the Driver.WriteStencilSpan() and Driver.ReadStencilSpan()
1308 * functions.
1309 * Actually, if there is a hardware stencil buffer it really should have
1310 * been cleared in Driver.Clear()! However, if the hardware does not
1311 * support scissored clears or masked clears (i.e. glStencilMask) then
1312 * we have to use the span-based functions.
1313 */
1314static void
1315clear_hardware_stencil_buffer( GLcontext *ctx )
1316{
1317 ASSERT(ctx->Driver.WriteStencilSpan);
1318 ASSERT(ctx->Driver.ReadStencilSpan);
1319
1320 if (ctx->Scissor.Enabled) {
1321 /* clear scissor region only */
1322 const GLint x = ctx->DrawBuffer->Xmin;
1323 const GLint width = ctx->DrawBuffer->Xmax - ctx->DrawBuffer->Xmin + 1;
1324 if (ctx->Stencil.WriteMask != STENCIL_MAX) {
1325 /* must apply mask to the clear */
1326 GLint y;
1327 for (y = ctx->DrawBuffer->Ymin; y <= ctx->DrawBuffer->Ymax; y++) {
1328 const GLstencil mask = ctx->Stencil.WriteMask;
1329 const GLstencil invMask = ~mask;
1330 const GLstencil clearVal = (ctx->Stencil.Clear & mask);
1331 GLstencil stencil[MAX_WIDTH];
1332 GLint i;
1333 (*ctx->Driver.ReadStencilSpan)(ctx, x, y, width, stencil);
1334 for (i = 0; i < width; i++) {
1335 stencil[i] = (stencil[i] & invMask) | clearVal;
1336 }
1337 (*ctx->Driver.WriteStencilSpan)(ctx, x, y, width, stencil, NULL);
1338 }
1339 }
1340 else {
1341 /* no masking */
1342 GLstencil stencil[MAX_WIDTH];
1343 GLint y, i;
1344 for (i = 0; i < width; i++) {
1345 stencil[i] = ctx->Stencil.Clear;
1346 }
1347 for (y = ctx->DrawBuffer->Ymin; y <= ctx->DrawBuffer->Ymax; y++) {
1348 (*ctx->Driver.WriteStencilSpan)(ctx, x, y, width, stencil, NULL);
1349 }
1350 }
1351 }
1352 else {
1353 /* clear whole stencil buffer */
1354 if (ctx->Stencil.WriteMask != STENCIL_MAX) {
1355 /* must apply mask to the clear */
1356 const GLstencil mask = ctx->Stencil.WriteMask;
1357 const GLstencil invMask = ~mask;
1358 const GLstencil clearVal = (ctx->Stencil.Clear & mask);
1359 const GLint width = ctx->DrawBuffer->Width;
1360 const GLint height = ctx->DrawBuffer->Height;
1361 const GLint x = ctx->DrawBuffer->Xmin;
1362 GLint y;
1363 for (y = 0; y < height; y++) {
1364 GLstencil stencil[MAX_WIDTH];
1365 GLuint i;
1366 (*ctx->Driver.ReadStencilSpan)(ctx, x, y, width, stencil);
1367 for (i = 0; i < width; i++) {
1368 stencil[i] = (stencil[i] & invMask) | clearVal;
1369 }
1370 (*ctx->Driver.WriteStencilSpan)(ctx, x, y, width, stencil, NULL);
1371 }
1372 }
1373 else {
1374 /* clear whole buffer without masking */
1375 const GLint width = ctx->DrawBuffer->Width;
1376 const GLint height = ctx->DrawBuffer->Width;
1377 const GLint x = ctx->DrawBuffer->Xmin;
1378 GLstencil stencil[MAX_WIDTH];
1379 GLint y, i;
1380 for (i = 0; i < width; i++) {
1381 stencil[i] = ctx->Stencil.Clear;
1382 }
1383 for (y = 0; y < height; y++) {
1384 (*ctx->Driver.WriteStencilSpan)(ctx, x, y, width, stencil, NULL);
1385 }
1386 }
1387 }
1388}
1389
1390
1391
1392/*
1393 * Clear the stencil buffer.
1394 */
1395void gl_clear_stencil_buffer( GLcontext *ctx )
1396{
1397 if (ctx->Driver.WriteStencilSpan) {
1398 ASSERT(ctx->Driver.ReadStencilSpan);
1399 clear_hardware_stencil_buffer(ctx);
1400 }
1401 else {
1402 clear_software_stencil_buffer(ctx);
1403 }
1404}
1405
Note: See TracBrowser for help on using the repository browser.