stencil.c revision dde7cb962860e72e1bf3175069767358cc5b3f3c
1/* 2 * Mesa 3-D graphics library 3 * Version: 7.1 4 * 5 * Copyright (C) 1999-2007 Brian Paul All Rights Reserved. 6 * 7 * Permission is hereby granted, free of charge, to any person obtaining a 8 * copy of this software and associated documentation files (the "Software"), 9 * to deal in the Software without restriction, including without limitation 10 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 11 * and/or sell copies of the Software, and to permit persons to whom the 12 * Software is furnished to do so, subject to the following conditions: 13 * 14 * The above copyright notice and this permission notice shall be included 15 * in all copies or substantial portions of the Software. 16 * 17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 18 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 20 * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN 21 * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 22 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 */ 24 25 26/** 27 * \file stencil.c 28 * Stencil operations. 29 * 30 */ 31 32 33#include "glheader.h" 34#include "imports.h" 35#include "context.h" 36#include "macros.h" 37#include "stencil.h" 38#include "mtypes.h" 39 40 41static GLboolean 42validate_stencil_op(GLcontext *ctx, GLenum op) 43{ 44 switch (op) { 45 case GL_KEEP: 46 case GL_ZERO: 47 case GL_REPLACE: 48 case GL_INCR: 49 case GL_DECR: 50 case GL_INVERT: 51 return GL_TRUE; 52 case GL_INCR_WRAP_EXT: 53 case GL_DECR_WRAP_EXT: 54 if (ctx->Extensions.EXT_stencil_wrap) { 55 return GL_TRUE; 56 } 57 /* FALL-THROUGH */ 58 default: 59 return GL_FALSE; 60 } 61} 62 63 64static GLboolean 65validate_stencil_func(GLcontext *ctx, GLenum func) 66{ 67 switch (func) { 68 case GL_NEVER: 69 case GL_LESS: 70 case GL_LEQUAL: 71 case GL_GREATER: 72 case GL_GEQUAL: 73 case GL_EQUAL: 74 case GL_NOTEQUAL: 75 case GL_ALWAYS: 76 return GL_TRUE; 77 default: 78 return GL_FALSE; 79 } 80} 81 82 83/** 84 * Set the clear value for the stencil buffer. 85 * 86 * \param s clear value. 87 * 88 * \sa glClearStencil(). 89 * 90 * Updates gl_stencil_attrib::Clear. On change 91 * flushes the vertices and notifies the driver via 92 * the dd_function_table::ClearStencil callback. 93 */ 94void GLAPIENTRY 95_mesa_ClearStencil( GLint s ) 96{ 97 GET_CURRENT_CONTEXT(ctx); 98 ASSERT_OUTSIDE_BEGIN_END(ctx); 99 100 if (ctx->Stencil.Clear == (GLuint) s) 101 return; 102 103 FLUSH_VERTICES(ctx, _NEW_STENCIL); 104 ctx->Stencil.Clear = (GLuint) s; 105 106 if (ctx->Driver.ClearStencil) { 107 ctx->Driver.ClearStencil( ctx, s ); 108 } 109} 110 111 112/** 113 * Set the function and reference value for stencil testing. 114 * 115 * \param frontfunc front test function. 116 * \param backfunc back test function. 117 * \param ref front and back reference value. 118 * \param mask front and back bitmask. 119 * 120 * \sa glStencilFunc(). 121 * 122 * Verifies the parameters and updates the respective values in 123 * __GLcontextRec::Stencil. On change flushes the vertices and notifies the 124 * driver via the dd_function_table::StencilFunc callback. 125 */ 126void GLAPIENTRY 127_mesa_StencilFuncSeparateATI( GLenum frontfunc, GLenum backfunc, GLint ref, GLuint mask ) 128{ 129 GET_CURRENT_CONTEXT(ctx); 130 const GLint stencilMax = (1 << ctx->DrawBuffer->Visual.stencilBits) - 1; 131 ASSERT_OUTSIDE_BEGIN_END(ctx); 132 133 if (!validate_stencil_func(ctx, frontfunc)) { 134 _mesa_error(ctx, GL_INVALID_ENUM, 135 "glStencilFuncSeparateATI(frontfunc)"); 136 return; 137 } 138 if (!validate_stencil_func(ctx, backfunc)) { 139 _mesa_error(ctx, GL_INVALID_ENUM, 140 "glStencilFuncSeparateATI(backfunc)"); 141 return; 142 } 143 144 ref = CLAMP( ref, 0, stencilMax ); 145 146 /* set both front and back state */ 147 if (ctx->Stencil.Function[0] == frontfunc && 148 ctx->Stencil.Function[1] == backfunc && 149 ctx->Stencil.ValueMask[0] == mask && 150 ctx->Stencil.ValueMask[1] == mask && 151 ctx->Stencil.Ref[0] == ref && 152 ctx->Stencil.Ref[1] == ref) 153 return; 154 FLUSH_VERTICES(ctx, _NEW_STENCIL); 155 ctx->Stencil.Function[0] = frontfunc; 156 ctx->Stencil.Function[1] = backfunc; 157 ctx->Stencil.Ref[0] = ctx->Stencil.Ref[1] = ref; 158 ctx->Stencil.ValueMask[0] = ctx->Stencil.ValueMask[1] = mask; 159 if (ctx->Driver.StencilFuncSeparate) { 160 ctx->Driver.StencilFuncSeparate(ctx, GL_FRONT, 161 frontfunc, ref, mask); 162 ctx->Driver.StencilFuncSeparate(ctx, GL_BACK, 163 backfunc, ref, mask); 164 } 165} 166 167 168/** 169 * Set the function and reference value for stencil testing. 170 * 171 * \param func test function. 172 * \param ref reference value. 173 * \param mask bitmask. 174 * 175 * \sa glStencilFunc(). 176 * 177 * Verifies the parameters and updates the respective values in 178 * __GLcontextRec::Stencil. On change flushes the vertices and notifies the 179 * driver via the dd_function_table::StencilFunc callback. 180 */ 181void GLAPIENTRY 182_mesa_StencilFunc( GLenum func, GLint ref, GLuint mask ) 183{ 184 GET_CURRENT_CONTEXT(ctx); 185 const GLint stencilMax = (1 << ctx->DrawBuffer->Visual.stencilBits) - 1; 186 const GLint face = ctx->Stencil.ActiveFace; 187 ASSERT_OUTSIDE_BEGIN_END(ctx); 188 189 if (!validate_stencil_func(ctx, func)) { 190 _mesa_error(ctx, GL_INVALID_ENUM, "glStencilFunc(func)"); 191 return; 192 } 193 194 ref = CLAMP( ref, 0, stencilMax ); 195 196 if (face != 0) { 197 if (ctx->Stencil.Function[face] == func && 198 ctx->Stencil.ValueMask[face] == mask && 199 ctx->Stencil.Ref[face] == ref) 200 return; 201 FLUSH_VERTICES(ctx, _NEW_STENCIL); 202 ctx->Stencil.Function[face] = func; 203 ctx->Stencil.Ref[face] = ref; 204 ctx->Stencil.ValueMask[face] = mask; 205 206 /* Only propagate the change to the driver if EXT_stencil_two_side 207 * is enabled. 208 */ 209 if (ctx->Driver.StencilFuncSeparate && ctx->Stencil.TestTwoSide) { 210 ctx->Driver.StencilFuncSeparate(ctx, GL_BACK, func, ref, mask); 211 } 212 } 213 else { 214 /* set both front and back state */ 215 if (ctx->Stencil.Function[0] == func && 216 ctx->Stencil.Function[1] == func && 217 ctx->Stencil.ValueMask[0] == mask && 218 ctx->Stencil.ValueMask[1] == mask && 219 ctx->Stencil.Ref[0] == ref && 220 ctx->Stencil.Ref[1] == ref) 221 return; 222 FLUSH_VERTICES(ctx, _NEW_STENCIL); 223 ctx->Stencil.Function[0] = ctx->Stencil.Function[1] = func; 224 ctx->Stencil.Ref[0] = ctx->Stencil.Ref[1] = ref; 225 ctx->Stencil.ValueMask[0] = ctx->Stencil.ValueMask[1] = mask; 226 if (ctx->Driver.StencilFuncSeparate) { 227 ctx->Driver.StencilFuncSeparate(ctx, 228 ((ctx->Stencil.TestTwoSide) 229 ? GL_FRONT : GL_FRONT_AND_BACK), 230 func, ref, mask); 231 } 232 } 233} 234 235 236/** 237 * Set the stencil writing mask. 238 * 239 * \param mask bit-mask to enable/disable writing of individual bits in the 240 * stencil planes. 241 * 242 * \sa glStencilMask(). 243 * 244 * Updates gl_stencil_attrib::WriteMask. On change flushes the vertices and 245 * notifies the driver via the dd_function_table::StencilMask callback. 246 */ 247void GLAPIENTRY 248_mesa_StencilMask( GLuint mask ) 249{ 250 GET_CURRENT_CONTEXT(ctx); 251 const GLint face = ctx->Stencil.ActiveFace; 252 253 ASSERT_OUTSIDE_BEGIN_END(ctx); 254 255 if (face != 0) { 256 /* Only modify the EXT_stencil_two_side back-face state. 257 */ 258 if (ctx->Stencil.WriteMask[face] == mask) 259 return; 260 FLUSH_VERTICES(ctx, _NEW_STENCIL); 261 ctx->Stencil.WriteMask[face] = mask; 262 263 /* Only propagate the change to the driver if EXT_stencil_two_side 264 * is enabled. 265 */ 266 if (ctx->Driver.StencilMaskSeparate && ctx->Stencil.TestTwoSide) { 267 ctx->Driver.StencilMaskSeparate(ctx, GL_BACK, mask); 268 } 269 } 270 else { 271 /* set both front and back state */ 272 if (ctx->Stencil.WriteMask[0] == mask && 273 ctx->Stencil.WriteMask[1] == mask) 274 return; 275 FLUSH_VERTICES(ctx, _NEW_STENCIL); 276 ctx->Stencil.WriteMask[0] = ctx->Stencil.WriteMask[1] = mask; 277 if (ctx->Driver.StencilMaskSeparate) { 278 ctx->Driver.StencilMaskSeparate(ctx, 279 ((ctx->Stencil.TestTwoSide) 280 ? GL_FRONT : GL_FRONT_AND_BACK), 281 mask); 282 } 283 } 284} 285 286 287/** 288 * Set the stencil test actions. 289 * 290 * \param fail action to take when stencil test fails. 291 * \param zfail action to take when stencil test passes, but depth test fails. 292 * \param zpass action to take when stencil test passes and the depth test 293 * passes (or depth testing is not enabled). 294 * 295 * \sa glStencilOp(). 296 * 297 * Verifies the parameters and updates the respective fields in 298 * __GLcontextRec::Stencil. On change flushes the vertices and notifies the 299 * driver via the dd_function_table::StencilOp callback. 300 */ 301void GLAPIENTRY 302_mesa_StencilOp(GLenum fail, GLenum zfail, GLenum zpass) 303{ 304 GET_CURRENT_CONTEXT(ctx); 305 const GLint face = ctx->Stencil.ActiveFace; 306 307 ASSERT_OUTSIDE_BEGIN_END(ctx); 308 309 if (!validate_stencil_op(ctx, fail)) { 310 _mesa_error(ctx, GL_INVALID_ENUM, "glStencilOp(sfail)"); 311 return; 312 } 313 if (!validate_stencil_op(ctx, zfail)) { 314 _mesa_error(ctx, GL_INVALID_ENUM, "glStencilOp(zfail)"); 315 return; 316 } 317 if (!validate_stencil_op(ctx, zpass)) { 318 _mesa_error(ctx, GL_INVALID_ENUM, "glStencilOp(zpass)"); 319 return; 320 } 321 322 if (face != 0) { 323 /* only set active face state */ 324 if (ctx->Stencil.ZFailFunc[face] == zfail && 325 ctx->Stencil.ZPassFunc[face] == zpass && 326 ctx->Stencil.FailFunc[face] == fail) 327 return; 328 FLUSH_VERTICES(ctx, _NEW_STENCIL); 329 ctx->Stencil.ZFailFunc[face] = zfail; 330 ctx->Stencil.ZPassFunc[face] = zpass; 331 ctx->Stencil.FailFunc[face] = fail; 332 333 /* Only propagate the change to the driver if EXT_stencil_two_side 334 * is enabled. 335 */ 336 if (ctx->Driver.StencilOpSeparate && ctx->Stencil.TestTwoSide) { 337 ctx->Driver.StencilOpSeparate(ctx, GL_BACK, fail, zfail, zpass); 338 } 339 } 340 else { 341 /* set both front and back state */ 342 if (ctx->Stencil.ZFailFunc[0] == zfail && 343 ctx->Stencil.ZFailFunc[1] == zfail && 344 ctx->Stencil.ZPassFunc[0] == zpass && 345 ctx->Stencil.ZPassFunc[1] == zpass && 346 ctx->Stencil.FailFunc[0] == fail && 347 ctx->Stencil.FailFunc[1] == fail) 348 return; 349 FLUSH_VERTICES(ctx, _NEW_STENCIL); 350 ctx->Stencil.ZFailFunc[0] = ctx->Stencil.ZFailFunc[1] = zfail; 351 ctx->Stencil.ZPassFunc[0] = ctx->Stencil.ZPassFunc[1] = zpass; 352 ctx->Stencil.FailFunc[0] = ctx->Stencil.FailFunc[1] = fail; 353 if (ctx->Driver.StencilOpSeparate) { 354 ctx->Driver.StencilOpSeparate(ctx, 355 ((ctx->Stencil.TestTwoSide) 356 ? GL_FRONT : GL_FRONT_AND_BACK), 357 fail, zfail, zpass); 358 } 359 } 360} 361 362 363 364#if _HAVE_FULL_GL 365/* GL_EXT_stencil_two_side */ 366void GLAPIENTRY 367_mesa_ActiveStencilFaceEXT(GLenum face) 368{ 369 GET_CURRENT_CONTEXT(ctx); 370 ASSERT_OUTSIDE_BEGIN_END(ctx); 371 372 if (!ctx->Extensions.EXT_stencil_two_side) { 373 _mesa_error(ctx, GL_INVALID_OPERATION, "glActiveStencilFaceEXT"); 374 return; 375 } 376 377 if (face == GL_FRONT || face == GL_BACK) { 378 FLUSH_VERTICES(ctx, _NEW_STENCIL); 379 ctx->Stencil.ActiveFace = (face == GL_FRONT) ? 0 : 2; 380 } 381 else { 382 _mesa_error(ctx, GL_INVALID_ENUM, "glActiveStencilFaceEXT(face)"); 383 } 384} 385#endif 386 387 388 389/** 390 * OpenGL 2.0 function. 391 * \todo Make StencilOp() call this function. And eventually remove the 392 * ctx->Driver.StencilOp function and use ctx->Driver.StencilOpSeparate 393 * instead. 394 */ 395void GLAPIENTRY 396_mesa_StencilOpSeparate(GLenum face, GLenum sfail, GLenum zfail, GLenum zpass) 397{ 398 GLboolean set = GL_FALSE; 399 GET_CURRENT_CONTEXT(ctx); 400 ASSERT_OUTSIDE_BEGIN_END(ctx); 401 402 if (!validate_stencil_op(ctx, sfail)) { 403 _mesa_error(ctx, GL_INVALID_ENUM, "glStencilOpSeparate(sfail)"); 404 return; 405 } 406 if (!validate_stencil_op(ctx, zfail)) { 407 _mesa_error(ctx, GL_INVALID_ENUM, "glStencilOpSeparate(zfail)"); 408 return; 409 } 410 if (!validate_stencil_op(ctx, zpass)) { 411 _mesa_error(ctx, GL_INVALID_ENUM, "glStencilOpSeparate(zpass)"); 412 return; 413 } 414 if (face != GL_FRONT && face != GL_BACK && face != GL_FRONT_AND_BACK) { 415 _mesa_error(ctx, GL_INVALID_ENUM, "glStencilOpSeparate(face)"); 416 return; 417 } 418 419 if (face != GL_BACK) { 420 /* set front */ 421 if (ctx->Stencil.ZFailFunc[0] != zfail || 422 ctx->Stencil.ZPassFunc[0] != zpass || 423 ctx->Stencil.FailFunc[0] != sfail){ 424 FLUSH_VERTICES(ctx, _NEW_STENCIL); 425 ctx->Stencil.ZFailFunc[0] = zfail; 426 ctx->Stencil.ZPassFunc[0] = zpass; 427 ctx->Stencil.FailFunc[0] = sfail; 428 set = GL_TRUE; 429 } 430 } 431 if (face != GL_FRONT) { 432 /* set back */ 433 if (ctx->Stencil.ZFailFunc[1] != zfail || 434 ctx->Stencil.ZPassFunc[1] != zpass || 435 ctx->Stencil.FailFunc[1] != sfail) { 436 FLUSH_VERTICES(ctx, _NEW_STENCIL); 437 ctx->Stencil.ZFailFunc[1] = zfail; 438 ctx->Stencil.ZPassFunc[1] = zpass; 439 ctx->Stencil.FailFunc[1] = sfail; 440 set = GL_TRUE; 441 } 442 } 443 if (set && ctx->Driver.StencilOpSeparate) { 444 ctx->Driver.StencilOpSeparate(ctx, face, sfail, zfail, zpass); 445 } 446} 447 448 449/* OpenGL 2.0 */ 450void GLAPIENTRY 451_mesa_StencilFuncSeparate(GLenum face, GLenum func, GLint ref, GLuint mask) 452{ 453 GET_CURRENT_CONTEXT(ctx); 454 const GLint stencilMax = (1 << ctx->DrawBuffer->Visual.stencilBits) - 1; 455 ASSERT_OUTSIDE_BEGIN_END(ctx); 456 457 if (face != GL_FRONT && face != GL_BACK && face != GL_FRONT_AND_BACK) { 458 _mesa_error(ctx, GL_INVALID_ENUM, "glStencilFuncSeparate(face)"); 459 return; 460 } 461 if (!validate_stencil_func(ctx, func)) { 462 _mesa_error(ctx, GL_INVALID_ENUM, "glStencilFuncSeparate(func)"); 463 return; 464 } 465 466 ref = CLAMP(ref, 0, stencilMax); 467 468 FLUSH_VERTICES(ctx, _NEW_STENCIL); 469 470 if (face != GL_BACK) { 471 /* set front */ 472 ctx->Stencil.Function[0] = func; 473 ctx->Stencil.Ref[0] = ref; 474 ctx->Stencil.ValueMask[0] = mask; 475 } 476 if (face != GL_FRONT) { 477 /* set back */ 478 ctx->Stencil.Function[1] = func; 479 ctx->Stencil.Ref[1] = ref; 480 ctx->Stencil.ValueMask[1] = mask; 481 } 482 if (ctx->Driver.StencilFuncSeparate) { 483 ctx->Driver.StencilFuncSeparate(ctx, face, func, ref, mask); 484 } 485} 486 487 488/* OpenGL 2.0 */ 489void GLAPIENTRY 490_mesa_StencilMaskSeparate(GLenum face, GLuint mask) 491{ 492 GET_CURRENT_CONTEXT(ctx); 493 ASSERT_OUTSIDE_BEGIN_END(ctx); 494 495 if (face != GL_FRONT && face != GL_BACK && face != GL_FRONT_AND_BACK) { 496 _mesa_error(ctx, GL_INVALID_ENUM, "glStencilaMaskSeparate(face)"); 497 return; 498 } 499 500 FLUSH_VERTICES(ctx, _NEW_STENCIL); 501 502 if (face != GL_BACK) { 503 ctx->Stencil.WriteMask[0] = mask; 504 } 505 if (face != GL_FRONT) { 506 ctx->Stencil.WriteMask[1] = mask; 507 } 508 if (ctx->Driver.StencilMaskSeparate) { 509 ctx->Driver.StencilMaskSeparate(ctx, face, mask); 510 } 511} 512 513 514/** 515 * Update derived stencil state. 516 */ 517void 518_mesa_update_stencil(GLcontext *ctx) 519{ 520 const GLint face = ctx->Stencil._BackFace; 521 522 ctx->Stencil._TestTwoSide = 523 (ctx->Stencil.Function[0] != ctx->Stencil.Function[face] || 524 ctx->Stencil.FailFunc[0] != ctx->Stencil.FailFunc[face] || 525 ctx->Stencil.ZPassFunc[0] != ctx->Stencil.ZPassFunc[face] || 526 ctx->Stencil.ZFailFunc[0] != ctx->Stencil.ZFailFunc[face] || 527 ctx->Stencil.Ref[0] != ctx->Stencil.Ref[face] || 528 ctx->Stencil.ValueMask[0] != ctx->Stencil.ValueMask[face] || 529 ctx->Stencil.WriteMask[0] != ctx->Stencil.WriteMask[face]); 530} 531 532 533/** 534 * Initialize the context stipple state. 535 * 536 * \param ctx GL context. 537 * 538 * Initializes __GLcontextRec::Stencil attribute group. 539 */ 540void 541_mesa_init_stencil(GLcontext *ctx) 542{ 543 ctx->Stencil.Enabled = GL_FALSE; 544 ctx->Stencil.TestTwoSide = GL_FALSE; 545 ctx->Stencil.ActiveFace = 0; /* 0 = GL_FRONT, 1 = GL_BACK */ 546 ctx->Stencil.Function[0] = GL_ALWAYS; 547 ctx->Stencil.Function[1] = GL_ALWAYS; 548 ctx->Stencil.Function[2] = GL_ALWAYS; 549 ctx->Stencil.FailFunc[0] = GL_KEEP; 550 ctx->Stencil.FailFunc[1] = GL_KEEP; 551 ctx->Stencil.FailFunc[2] = GL_KEEP; 552 ctx->Stencil.ZPassFunc[0] = GL_KEEP; 553 ctx->Stencil.ZPassFunc[1] = GL_KEEP; 554 ctx->Stencil.ZPassFunc[2] = GL_KEEP; 555 ctx->Stencil.ZFailFunc[0] = GL_KEEP; 556 ctx->Stencil.ZFailFunc[1] = GL_KEEP; 557 ctx->Stencil.ZFailFunc[2] = GL_KEEP; 558 ctx->Stencil.Ref[0] = 0; 559 ctx->Stencil.Ref[1] = 0; 560 ctx->Stencil.Ref[2] = 0; 561 ctx->Stencil.ValueMask[0] = ~0U; 562 ctx->Stencil.ValueMask[1] = ~0U; 563 ctx->Stencil.ValueMask[2] = ~0U; 564 ctx->Stencil.WriteMask[0] = ~0U; 565 ctx->Stencil.WriteMask[1] = ~0U; 566 ctx->Stencil.WriteMask[2] = ~0U; 567 ctx->Stencil.Clear = 0; 568} 569