fix for bug#10182
[mesa.git] / src / mesa / main / blend.c
1 /**
2 * \file blend.c
3 * Blending operations.
4 */
5
6 /*
7 * Mesa 3-D graphics library
8 * Version: 6.5.1
9 *
10 * Copyright (C) 1999-2006 Brian Paul All Rights Reserved.
11 *
12 * Permission is hereby granted, free of charge, to any person obtaining a
13 * copy of this software and associated documentation files (the "Software"),
14 * to deal in the Software without restriction, including without limitation
15 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
16 * and/or sell copies of the Software, and to permit persons to whom the
17 * Software is furnished to do so, subject to the following conditions:
18 *
19 * The above copyright notice and this permission notice shall be included
20 * in all copies or substantial portions of the Software.
21 *
22 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
23 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
25 * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
26 * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
27 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
28 */
29
30
31
32 #include "glheader.h"
33 #include "blend.h"
34 #include "colormac.h"
35 #include "context.h"
36 #include "enums.h"
37 #include "macros.h"
38 #include "mtypes.h"
39
40
41 /**
42 * Specify the blending operation.
43 *
44 * \param sfactor source factor operator.
45 * \param dfactor destination factor operator.
46 *
47 * \sa glBlendFunc, glBlendFuncSeparateEXT
48 *
49 * Swizzles the inputs and calls \c glBlendFuncSeparateEXT. This is done
50 * using the \c CurrentDispatch table in the context, so this same function
51 * can be used while compiling display lists. Therefore, there is no need
52 * for the display list code to save and restore this function.
53 */
54 void GLAPIENTRY
55 _mesa_BlendFunc( GLenum sfactor, GLenum dfactor )
56 {
57 GET_CURRENT_CONTEXT(ctx);
58
59 (*ctx->CurrentDispatch->BlendFuncSeparateEXT)( sfactor, dfactor,
60 sfactor, dfactor );
61 }
62
63
64 /**
65 * Process GL_EXT_blend_func_separate().
66 *
67 * \param sfactorRGB RGB source factor operator.
68 * \param dfactorRGB RGB destination factor operator.
69 * \param sfactorA alpha source factor operator.
70 * \param dfactorA alpha destination factor operator.
71 *
72 * Verifies the parameters and updates gl_colorbuffer_attrib.
73 * On a change, flush the vertices and notify the driver via
74 * dd_function_table::BlendFuncSeparate.
75 */
76 void GLAPIENTRY
77 _mesa_BlendFuncSeparateEXT( GLenum sfactorRGB, GLenum dfactorRGB,
78 GLenum sfactorA, GLenum dfactorA )
79 {
80 GET_CURRENT_CONTEXT(ctx);
81 ASSERT_OUTSIDE_BEGIN_END(ctx);
82
83 if (MESA_VERBOSE & (VERBOSE_API|VERBOSE_TEXTURE))
84 _mesa_debug(ctx, "glBlendFuncSeparate %s %s %s %s\n",
85 _mesa_lookup_enum_by_nr(sfactorRGB),
86 _mesa_lookup_enum_by_nr(dfactorRGB),
87 _mesa_lookup_enum_by_nr(sfactorA),
88 _mesa_lookup_enum_by_nr(dfactorA));
89
90 switch (sfactorRGB) {
91 case GL_SRC_COLOR:
92 case GL_ONE_MINUS_SRC_COLOR:
93 if (!ctx->Extensions.NV_blend_square) {
94 _mesa_error(ctx, GL_INVALID_ENUM, "glBlendFunc or glBlendFuncSeparate (sfactorRGB)");
95 return;
96 }
97 /* fall-through */
98 case GL_ZERO:
99 case GL_ONE:
100 case GL_DST_COLOR:
101 case GL_ONE_MINUS_DST_COLOR:
102 case GL_SRC_ALPHA:
103 case GL_ONE_MINUS_SRC_ALPHA:
104 case GL_DST_ALPHA:
105 case GL_ONE_MINUS_DST_ALPHA:
106 case GL_SRC_ALPHA_SATURATE:
107 case GL_CONSTANT_COLOR:
108 case GL_ONE_MINUS_CONSTANT_COLOR:
109 case GL_CONSTANT_ALPHA:
110 case GL_ONE_MINUS_CONSTANT_ALPHA:
111 break;
112 default:
113 _mesa_error(ctx, GL_INVALID_ENUM, "glBlendFunc or glBlendFuncSeparate (sfactorRGB)");
114 return;
115 }
116
117 switch (dfactorRGB) {
118 case GL_DST_COLOR:
119 case GL_ONE_MINUS_DST_COLOR:
120 if (!ctx->Extensions.NV_blend_square) {
121 _mesa_error(ctx, GL_INVALID_ENUM, "glBlendFunc or glBlendFuncSeparate (dfactorRGB)");
122 return;
123 }
124 /* fall-through */
125 case GL_ZERO:
126 case GL_ONE:
127 case GL_SRC_COLOR:
128 case GL_ONE_MINUS_SRC_COLOR:
129 case GL_SRC_ALPHA:
130 case GL_ONE_MINUS_SRC_ALPHA:
131 case GL_DST_ALPHA:
132 case GL_ONE_MINUS_DST_ALPHA:
133 case GL_CONSTANT_COLOR:
134 case GL_ONE_MINUS_CONSTANT_COLOR:
135 case GL_CONSTANT_ALPHA:
136 case GL_ONE_MINUS_CONSTANT_ALPHA:
137 break;
138 default:
139 _mesa_error(ctx, GL_INVALID_ENUM, "glBlendFunc or glBlendFuncSeparate (dfactorRGB)");
140 return;
141 }
142
143 switch (sfactorA) {
144 case GL_SRC_COLOR:
145 case GL_ONE_MINUS_SRC_COLOR:
146 if (!ctx->Extensions.NV_blend_square) {
147 _mesa_error(ctx, GL_INVALID_ENUM, "glBlendFunc or glBlendFuncSeparate (sfactorA)");
148 return;
149 }
150 /* fall-through */
151 case GL_ZERO:
152 case GL_ONE:
153 case GL_DST_COLOR:
154 case GL_ONE_MINUS_DST_COLOR:
155 case GL_SRC_ALPHA:
156 case GL_ONE_MINUS_SRC_ALPHA:
157 case GL_DST_ALPHA:
158 case GL_ONE_MINUS_DST_ALPHA:
159 case GL_SRC_ALPHA_SATURATE:
160 case GL_CONSTANT_COLOR:
161 case GL_ONE_MINUS_CONSTANT_COLOR:
162 case GL_CONSTANT_ALPHA:
163 case GL_ONE_MINUS_CONSTANT_ALPHA:
164 break;
165 default:
166 _mesa_error(ctx, GL_INVALID_ENUM, "glBlendFunc or glBlendFuncSeparate (sfactorA)");
167 return;
168 }
169
170 switch (dfactorA) {
171 case GL_DST_COLOR:
172 case GL_ONE_MINUS_DST_COLOR:
173 if (!ctx->Extensions.NV_blend_square) {
174 _mesa_error(ctx, GL_INVALID_ENUM, "glBlendFunc or glBlendFuncSeparate (dfactorA)");
175 return;
176 }
177 /* fall-through */
178 case GL_ZERO:
179 case GL_ONE:
180 case GL_SRC_COLOR:
181 case GL_ONE_MINUS_SRC_COLOR:
182 case GL_SRC_ALPHA:
183 case GL_ONE_MINUS_SRC_ALPHA:
184 case GL_DST_ALPHA:
185 case GL_ONE_MINUS_DST_ALPHA:
186 case GL_CONSTANT_COLOR:
187 case GL_ONE_MINUS_CONSTANT_COLOR:
188 case GL_CONSTANT_ALPHA:
189 case GL_ONE_MINUS_CONSTANT_ALPHA:
190 break;
191 default:
192 _mesa_error( ctx, GL_INVALID_ENUM, "glBlendFunc or glBlendFuncSeparate (dfactorA)" );
193 return;
194 }
195
196 if (ctx->Color.BlendSrcRGB == sfactorRGB &&
197 ctx->Color.BlendDstRGB == dfactorRGB &&
198 ctx->Color.BlendSrcA == sfactorA &&
199 ctx->Color.BlendDstA == dfactorA)
200 return;
201
202 FLUSH_VERTICES(ctx, _NEW_COLOR);
203
204 ctx->Color.BlendSrcRGB = sfactorRGB;
205 ctx->Color.BlendDstRGB = dfactorRGB;
206 ctx->Color.BlendSrcA = sfactorA;
207 ctx->Color.BlendDstA = dfactorA;
208
209 if (ctx->Driver.BlendFuncSeparate) {
210 (*ctx->Driver.BlendFuncSeparate)( ctx, sfactorRGB, dfactorRGB,
211 sfactorA, dfactorA );
212 }
213 }
214
215
216 #if _HAVE_FULL_GL
217
218 static GLboolean
219 _mesa_validate_blend_equation( GLcontext *ctx,
220 GLenum mode, GLboolean is_separate )
221 {
222 switch (mode) {
223 case GL_FUNC_ADD:
224 break;
225 case GL_MIN:
226 case GL_MAX:
227 if (!ctx->Extensions.EXT_blend_minmax &&
228 !ctx->Extensions.ARB_imaging) {
229 return GL_FALSE;
230 }
231 break;
232 /* glBlendEquationSeparate cannot take GL_LOGIC_OP as a parameter.
233 */
234 case GL_LOGIC_OP:
235 if (!ctx->Extensions.EXT_blend_logic_op || is_separate) {
236 return GL_FALSE;
237 }
238 break;
239 case GL_FUNC_SUBTRACT:
240 case GL_FUNC_REVERSE_SUBTRACT:
241 if (!ctx->Extensions.EXT_blend_subtract &&
242 !ctx->Extensions.ARB_imaging) {
243 return GL_FALSE;
244 }
245 break;
246 default:
247 return GL_FALSE;
248 }
249
250 return GL_TRUE;
251 }
252
253
254 /* This is really an extension function! */
255 void GLAPIENTRY
256 _mesa_BlendEquation( GLenum mode )
257 {
258 GET_CURRENT_CONTEXT(ctx);
259 ASSERT_OUTSIDE_BEGIN_END(ctx);
260
261 if (MESA_VERBOSE & (VERBOSE_API|VERBOSE_TEXTURE))
262 _mesa_debug(ctx, "glBlendEquation %s\n",
263 _mesa_lookup_enum_by_nr(mode));
264
265 if ( ! _mesa_validate_blend_equation( ctx, mode, GL_FALSE ) ) {
266 _mesa_error(ctx, GL_INVALID_ENUM, "glBlendEquation");
267 return;
268 }
269
270 if ( (ctx->Color.BlendEquationRGB == mode) &&
271 (ctx->Color.BlendEquationA == mode) )
272 return;
273
274 FLUSH_VERTICES(ctx, _NEW_COLOR);
275 ctx->Color.BlendEquationRGB = mode;
276 ctx->Color.BlendEquationA = mode;
277
278 if (ctx->Driver.BlendEquationSeparate)
279 (*ctx->Driver.BlendEquationSeparate)( ctx, mode, mode );
280 }
281
282
283 void GLAPIENTRY
284 _mesa_BlendEquationSeparateEXT( GLenum modeRGB, GLenum modeA )
285 {
286 GET_CURRENT_CONTEXT(ctx);
287 ASSERT_OUTSIDE_BEGIN_END(ctx);
288
289 if (MESA_VERBOSE & (VERBOSE_API|VERBOSE_TEXTURE))
290 _mesa_debug(ctx, "glBlendEquationSeparateEXT %s %s\n",
291 _mesa_lookup_enum_by_nr(modeRGB),
292 _mesa_lookup_enum_by_nr(modeA));
293
294 if ( (modeRGB != modeA) && !ctx->Extensions.EXT_blend_equation_separate ) {
295 _mesa_error(ctx, GL_INVALID_OPERATION,
296 "glBlendEquationSeparateEXT not supported by driver");
297 return;
298 }
299
300 if ( ! _mesa_validate_blend_equation( ctx, modeRGB, GL_TRUE ) ) {
301 _mesa_error(ctx, GL_INVALID_ENUM, "glBlendEquationSeparateEXT(modeRGB)");
302 return;
303 }
304
305 if ( ! _mesa_validate_blend_equation( ctx, modeA, GL_TRUE ) ) {
306 _mesa_error(ctx, GL_INVALID_ENUM, "glBlendEquationSeparateEXT(modeA)");
307 return;
308 }
309
310
311 if ( (ctx->Color.BlendEquationRGB == modeRGB) &&
312 (ctx->Color.BlendEquationA == modeA) )
313 return;
314
315 FLUSH_VERTICES(ctx, _NEW_COLOR);
316 ctx->Color.BlendEquationRGB = modeRGB;
317 ctx->Color.BlendEquationA = modeA;
318
319 if (ctx->Driver.BlendEquationSeparate)
320 (*ctx->Driver.BlendEquationSeparate)( ctx, modeRGB, modeA );
321 }
322 #endif
323
324
325 /**
326 * Set the blending color.
327 *
328 * \param red red color component.
329 * \param green green color component.
330 * \param blue blue color component.
331 * \param alpha alpha color component.
332 *
333 * \sa glBlendColor().
334 *
335 * Clamps the parameters and updates gl_colorbuffer_attrib::BlendColor. On a
336 * change, flushes the vertices and notifies the driver via
337 * dd_function_table::BlendColor callback.
338 */
339 void GLAPIENTRY
340 _mesa_BlendColor( GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha )
341 {
342 GLfloat tmp[4];
343 GET_CURRENT_CONTEXT(ctx);
344 ASSERT_OUTSIDE_BEGIN_END(ctx);
345
346 tmp[0] = CLAMP( red, 0.0F, 1.0F );
347 tmp[1] = CLAMP( green, 0.0F, 1.0F );
348 tmp[2] = CLAMP( blue, 0.0F, 1.0F );
349 tmp[3] = CLAMP( alpha, 0.0F, 1.0F );
350
351 if (TEST_EQ_4V(tmp, ctx->Color.BlendColor))
352 return;
353
354 FLUSH_VERTICES(ctx, _NEW_COLOR);
355 COPY_4FV( ctx->Color.BlendColor, tmp );
356
357 if (ctx->Driver.BlendColor)
358 (*ctx->Driver.BlendColor)(ctx, tmp);
359 }
360
361
362 /**
363 * Specify the alpha test function.
364 *
365 * \param func alpha comparison function.
366 * \param ref reference value.
367 *
368 * Verifies the parameters and updates gl_colorbuffer_attrib.
369 * On a change, flushes the vertices and notifies the driver via
370 * dd_function_table::AlphaFunc callback.
371 */
372 void GLAPIENTRY
373 _mesa_AlphaFunc( GLenum func, GLclampf ref )
374 {
375 GET_CURRENT_CONTEXT(ctx);
376 ASSERT_OUTSIDE_BEGIN_END(ctx);
377
378 switch (func) {
379 case GL_NEVER:
380 case GL_LESS:
381 case GL_EQUAL:
382 case GL_LEQUAL:
383 case GL_GREATER:
384 case GL_NOTEQUAL:
385 case GL_GEQUAL:
386 case GL_ALWAYS:
387 ref = CLAMP(ref, 0.0F, 1.0F);
388
389 if (ctx->Color.AlphaFunc == func && ctx->Color.AlphaRef == ref)
390 return; /* no change */
391
392 FLUSH_VERTICES(ctx, _NEW_COLOR);
393 ctx->Color.AlphaFunc = func;
394 ctx->Color.AlphaRef = ref;
395
396 if (ctx->Driver.AlphaFunc)
397 ctx->Driver.AlphaFunc(ctx, func, ref);
398 return;
399
400 default:
401 _mesa_error( ctx, GL_INVALID_ENUM, "glAlphaFunc(func)" );
402 return;
403 }
404 }
405
406
407 /**
408 * Specify a logic pixel operation for color index rendering.
409 *
410 * \param opcode operation.
411 *
412 * Verifies that \p opcode is a valid enum and updates
413 gl_colorbuffer_attrib::LogicOp.
414 * On a change, flushes the vertices and notifies the driver via the
415 * dd_function_table::LogicOpcode callback.
416 */
417 void GLAPIENTRY
418 _mesa_LogicOp( GLenum opcode )
419 {
420 GET_CURRENT_CONTEXT(ctx);
421 ASSERT_OUTSIDE_BEGIN_END(ctx);
422
423 switch (opcode) {
424 case GL_CLEAR:
425 case GL_SET:
426 case GL_COPY:
427 case GL_COPY_INVERTED:
428 case GL_NOOP:
429 case GL_INVERT:
430 case GL_AND:
431 case GL_NAND:
432 case GL_OR:
433 case GL_NOR:
434 case GL_XOR:
435 case GL_EQUIV:
436 case GL_AND_REVERSE:
437 case GL_AND_INVERTED:
438 case GL_OR_REVERSE:
439 case GL_OR_INVERTED:
440 break;
441 default:
442 _mesa_error( ctx, GL_INVALID_ENUM, "glLogicOp" );
443 return;
444 }
445
446 if (ctx->Color.LogicOp == opcode)
447 return;
448
449 FLUSH_VERTICES(ctx, _NEW_COLOR);
450 ctx->Color.LogicOp = opcode;
451
452 if (ctx->Driver.LogicOpcode)
453 ctx->Driver.LogicOpcode( ctx, opcode );
454 }
455
456 #if _HAVE_FULL_GL
457 void GLAPIENTRY
458 _mesa_IndexMask( GLuint mask )
459 {
460 GET_CURRENT_CONTEXT(ctx);
461 ASSERT_OUTSIDE_BEGIN_END(ctx);
462
463 if (ctx->Color.IndexMask == mask)
464 return;
465
466 FLUSH_VERTICES(ctx, _NEW_COLOR);
467 ctx->Color.IndexMask = mask;
468
469 if (ctx->Driver.IndexMask)
470 ctx->Driver.IndexMask( ctx, mask );
471 }
472 #endif
473
474
475 /**
476 * Enable or disable writing of frame buffer color components.
477 *
478 * \param red whether to mask writing of the red color component.
479 * \param green whether to mask writing of the green color component.
480 * \param blue whether to mask writing of the blue color component.
481 * \param alpha whether to mask writing of the alpha color component.
482 *
483 * \sa glColorMask().
484 *
485 * Sets the appropriate value of gl_colorbuffer_attrib::ColorMask. On a
486 * change, flushes the vertices and notifies the driver via the
487 * dd_function_table::ColorMask callback.
488 */
489 void GLAPIENTRY
490 _mesa_ColorMask( GLboolean red, GLboolean green,
491 GLboolean blue, GLboolean alpha )
492 {
493 GET_CURRENT_CONTEXT(ctx);
494 GLubyte tmp[4];
495 ASSERT_OUTSIDE_BEGIN_END(ctx);
496
497 if (MESA_VERBOSE & VERBOSE_API)
498 _mesa_debug(ctx, "glColorMask %d %d %d %d\n", red, green, blue, alpha);
499
500 /* Shouldn't have any information about channel depth in core mesa
501 * -- should probably store these as the native booleans:
502 */
503 tmp[RCOMP] = red ? 0xff : 0x0;
504 tmp[GCOMP] = green ? 0xff : 0x0;
505 tmp[BCOMP] = blue ? 0xff : 0x0;
506 tmp[ACOMP] = alpha ? 0xff : 0x0;
507
508 if (TEST_EQ_4UBV(tmp, ctx->Color.ColorMask))
509 return;
510
511 FLUSH_VERTICES(ctx, _NEW_COLOR);
512 COPY_4UBV(ctx->Color.ColorMask, tmp);
513
514 if (ctx->Driver.ColorMask)
515 ctx->Driver.ColorMask( ctx, red, green, blue, alpha );
516 }
517
518
519 extern void GLAPIENTRY
520 _mesa_ClampColorARB(GLenum target, GLenum clamp)
521 {
522 GET_CURRENT_CONTEXT(ctx);
523
524 ASSERT_OUTSIDE_BEGIN_END(ctx);
525
526 if (clamp != GL_TRUE && clamp != GL_FALSE && clamp != GL_FIXED_ONLY_ARB) {
527 _mesa_error(ctx, GL_INVALID_ENUM, "glClampColorARB(clamp)");
528 return;
529 }
530
531 switch (target) {
532 case GL_CLAMP_VERTEX_COLOR_ARB:
533 ctx->Light.ClampVertexColor = clamp;
534 break;
535 case GL_CLAMP_FRAGMENT_COLOR_ARB:
536 ctx->Color.ClampFragmentColor = clamp;
537 break;
538 case GL_CLAMP_READ_COLOR_ARB:
539 ctx->Color.ClampReadColor = clamp;
540 break;
541 default:
542 _mesa_error(ctx, GL_INVALID_ENUM, "glClampColorARB(target)");
543 return;
544 }
545 }
546
547
548
549
550 /**********************************************************************/
551 /** \name Initialization */
552 /*@{*/
553
554 /**
555 * Initialization of the context's Color attribute group.
556 *
557 * \param ctx GL context.
558 *
559 * Initializes the related fields in the context color attribute group,
560 * __GLcontextRec::Color.
561 */
562 void _mesa_init_color( GLcontext * ctx )
563 {
564 /* Color buffer group */
565 ctx->Color.IndexMask = ~0u;
566 ctx->Color.ColorMask[0] = 0xff;
567 ctx->Color.ColorMask[1] = 0xff;
568 ctx->Color.ColorMask[2] = 0xff;
569 ctx->Color.ColorMask[3] = 0xff;
570 ctx->Color.ClearIndex = 0;
571 ASSIGN_4V( ctx->Color.ClearColor, 0, 0, 0, 0 );
572 ctx->Color.AlphaEnabled = GL_FALSE;
573 ctx->Color.AlphaFunc = GL_ALWAYS;
574 ctx->Color.AlphaRef = 0;
575 ctx->Color.BlendEnabled = GL_FALSE;
576 ctx->Color.BlendSrcRGB = GL_ONE;
577 ctx->Color.BlendDstRGB = GL_ZERO;
578 ctx->Color.BlendSrcA = GL_ONE;
579 ctx->Color.BlendDstA = GL_ZERO;
580 ctx->Color.BlendEquationRGB = GL_FUNC_ADD;
581 ctx->Color.BlendEquationA = GL_FUNC_ADD;
582 ASSIGN_4V( ctx->Color.BlendColor, 0.0, 0.0, 0.0, 0.0 );
583 ctx->Color.IndexLogicOpEnabled = GL_FALSE;
584 ctx->Color.ColorLogicOpEnabled = GL_FALSE;
585 ctx->Color._LogicOpEnabled = GL_FALSE;
586 ctx->Color.LogicOp = GL_COPY;
587 ctx->Color.DitherFlag = GL_TRUE;
588
589 if (ctx->Visual.doubleBufferMode) {
590 ctx->Color.DrawBuffer[0] = GL_BACK;
591 }
592 else {
593 ctx->Color.DrawBuffer[0] = GL_FRONT;
594 }
595
596 ctx->Color.ClampFragmentColor = GL_FIXED_ONLY_ARB;
597 ctx->Color.ClampReadColor = GL_FIXED_ONLY_ARB;
598 }
599
600 /*@}*/