3 * Mesa 3-D graphics library
6 * Copyright (C) 1999-2002 Brian Paul All Rights Reserved.
8 * Permission is hereby granted, free of charge, to any person obtaining a
9 * copy of this software and associated documentation files (the "Software"),
10 * to deal in the Software without restriction, including without limitation
11 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
12 * and/or sell copies of the Software, and to permit persons to whom the
13 * Software is furnished to do so, subject to the following conditions:
15 * The above copyright notice and this permission notice shall be included
16 * in all copies or substantial portions of the Software.
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
19 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
21 * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
22 * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
23 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
33 #include "s_alphabuf.h"
34 #include "s_context.h"
35 #include "s_masking.h"
40 * Accumulation buffer notes
42 * Normally, accumulation buffer values are GLshorts with values in
43 * [-32767, 32767] which represent floating point colors in [-1, 1],
44 * as suggested by the OpenGL specification.
46 * We optimize for the common case used for full-scene antialiasing:
47 * // start with accum buffer cleared to zero
48 * glAccum(GL_LOAD, w); // or GL_ACCUM the first image
49 * glAccum(GL_ACCUM, w);
51 * glAccum(GL_ACCUM, w);
52 * glAccum(GL_RETURN, 1.0);
53 * That is, we start with an empty accumulation buffer and accumulate
54 * n images, each with weight w = 1/n.
55 * In this scenario, we can simply store unscaled integer values in
56 * the accum buffer instead of scaled integers. We'll also keep track
57 * of the w value so when we do GL_RETURN we simply divide the accumulated
59 * This lets us avoid _many_ int->float->int conversions.
63 #if CHAN_BITS == 8 && ACCUM_BITS < 32
64 #define USE_OPTIMIZED_ACCUM /* enable the optimization */
69 _swrast_alloc_accum_buffer( GLframebuffer
*buffer
)
71 GET_CURRENT_CONTEXT(ctx
);
75 MESA_PBUFFER_FREE( buffer
->Accum
);
79 /* allocate accumulation buffer if not already present */
80 n
= buffer
->Width
* buffer
->Height
* 4 * sizeof(GLaccum
);
81 buffer
->Accum
= (GLaccum
*) MESA_PBUFFER_ALLOC( n
);
83 /* unable to setup accumulation buffer */
84 _mesa_error( NULL
, GL_OUT_OF_MEMORY
, "glAccum" );
88 SWcontext
*swrast
= SWRAST_CONTEXT(ctx
);
89 /* XXX these fields should probably be in the GLframebuffer */
90 #ifdef USE_OPTIMIZED_ACCUM
91 swrast
->_IntegerAccumMode
= GL_TRUE
;
93 swrast
->_IntegerAccumMode
= GL_FALSE
;
95 swrast
->_IntegerAccumScaler
= 0.0;
101 * This is called when we fall out of optimized/unscaled accum buffer mode.
102 * That is, we convert each unscaled accum buffer value into a scaled value
103 * representing the range[-1, 1].
105 static void rescale_accum( GLcontext
*ctx
)
107 SWcontext
*swrast
= SWRAST_CONTEXT(ctx
);
108 const GLuint n
= ctx
->DrawBuffer
->Width
* ctx
->DrawBuffer
->Height
* 4;
109 const GLfloat s
= swrast
->_IntegerAccumScaler
* (32767.0F
/ CHAN_MAXF
);
110 GLaccum
*accum
= ctx
->DrawBuffer
->Accum
;
113 assert(swrast
->_IntegerAccumMode
);
116 for (i
= 0; i
< n
; i
++) {
117 accum
[i
] = (GLaccum
) (accum
[i
] * s
);
120 swrast
->_IntegerAccumMode
= GL_FALSE
;
129 * Clear the accumulation Buffer.
132 _swarst_clear_accum_buffer( GLcontext
*ctx
)
134 SWcontext
*swrast
= SWRAST_CONTEXT(ctx
);
138 if (ctx
->Visual
.accumRedBits
==0) {
139 /* No accumulation buffer! */
143 if (sizeof(GLaccum
)==1) {
146 else if (sizeof(GLaccum
)==2) {
153 /* number of pixels */
154 buffersize
= ctx
->DrawBuffer
->Width
* ctx
->DrawBuffer
->Height
;
156 if (!ctx
->DrawBuffer
->Accum
) {
157 /* try to alloc accumulation buffer */
158 ctx
->DrawBuffer
->Accum
= (GLaccum
*)
159 MALLOC( buffersize
* 4 * sizeof(GLaccum
) );
162 if (ctx
->DrawBuffer
->Accum
) {
163 if (ctx
->Scissor
.Enabled
) {
164 /* Limit clear to scissor box */
165 const GLaccum r
= (GLaccum
) (ctx
->Accum
.ClearColor
[0] * acc_scale
);
166 const GLaccum g
= (GLaccum
) (ctx
->Accum
.ClearColor
[1] * acc_scale
);
167 const GLaccum b
= (GLaccum
) (ctx
->Accum
.ClearColor
[2] * acc_scale
);
168 const GLaccum a
= (GLaccum
) (ctx
->Accum
.ClearColor
[3] * acc_scale
);
172 /* size of region to clear */
173 width
= 4 * (ctx
->DrawBuffer
->_Xmax
- ctx
->DrawBuffer
->_Xmin
);
174 height
= ctx
->DrawBuffer
->_Ymax
- ctx
->DrawBuffer
->_Ymin
;
175 /* ptr to first element to clear */
176 row
= ctx
->DrawBuffer
->Accum
177 + 4 * (ctx
->DrawBuffer
->_Ymin
* ctx
->DrawBuffer
->Width
178 + ctx
->DrawBuffer
->_Xmin
);
179 for (j
=0;j
<height
;j
++) {
180 for (i
=0;i
<width
;i
+=4) {
186 row
+= 4 * ctx
->DrawBuffer
->Width
;
190 /* clear whole buffer */
191 if (ctx
->Accum
.ClearColor
[0]==0.0 &&
192 ctx
->Accum
.ClearColor
[1]==0.0 &&
193 ctx
->Accum
.ClearColor
[2]==0.0 &&
194 ctx
->Accum
.ClearColor
[3]==0.0) {
196 _mesa_bzero( ctx
->DrawBuffer
->Accum
,
197 buffersize
* 4 * sizeof(GLaccum
) );
201 const GLaccum r
= (GLaccum
) (ctx
->Accum
.ClearColor
[0] * acc_scale
);
202 const GLaccum g
= (GLaccum
) (ctx
->Accum
.ClearColor
[1] * acc_scale
);
203 const GLaccum b
= (GLaccum
) (ctx
->Accum
.ClearColor
[2] * acc_scale
);
204 const GLaccum a
= (GLaccum
) (ctx
->Accum
.ClearColor
[3] * acc_scale
);
205 GLaccum
*acc
= ctx
->DrawBuffer
->Accum
;
207 for (i
=0;i
<buffersize
;i
++) {
216 /* update optimized accum state vars */
217 if (ctx
->Accum
.ClearColor
[0] == 0.0 && ctx
->Accum
.ClearColor
[1] == 0.0 &&
218 ctx
->Accum
.ClearColor
[2] == 0.0 && ctx
->Accum
.ClearColor
[3] == 0.0) {
219 #ifdef USE_OPTIMIZED_ACCUM
220 swrast
->_IntegerAccumMode
= GL_TRUE
;
222 swrast
->_IntegerAccumMode
= GL_FALSE
;
224 swrast
->_IntegerAccumScaler
= 0.0; /* denotes empty accum buffer */
227 swrast
->_IntegerAccumMode
= GL_FALSE
;
234 _swrast_Accum( GLcontext
*ctx
, GLenum op
, GLfloat value
,
235 GLint xpos
, GLint ypos
,
236 GLint width
, GLint height
)
239 SWcontext
*swrast
= SWRAST_CONTEXT(ctx
);
242 GLchan rgba
[MAX_WIDTH
][4];
243 const GLuint colorMask
= *((GLuint
*) &ctx
->Color
.ColorMask
);
246 if (SWRAST_CONTEXT(ctx
)->NewState
)
247 _swrast_validate_derived( ctx
);
249 if (!ctx
->DrawBuffer
->Accum
) {
251 "Calling glAccum() without an accumulation "
252 "buffer (low memory?)");
256 if (sizeof(GLaccum
)==1) {
259 else if (sizeof(GLaccum
)==2) {
271 const GLaccum val
= (GLaccum
) (value
* acc_scale
);
273 /* Leave optimized accum buffer mode */
274 if (swrast
->_IntegerAccumMode
)
276 for (j
= 0; j
< height
; j
++) {
277 GLaccum
*acc
= ctx
->DrawBuffer
->Accum
+ ypos
* width4
+ 4*xpos
;
279 for (i
= 0; i
< width4
; i
++) {
290 /* Leave optimized accum buffer mode */
291 if (swrast
->_IntegerAccumMode
)
293 for (j
= 0; j
< height
; j
++) {
294 GLaccum
*acc
= ctx
->DrawBuffer
->Accum
+ ypos
* width4
+ 4 * xpos
;
296 for (i
= 0; i
< width4
; i
++) {
297 acc
[i
] = (GLaccum
) ( (GLfloat
) acc
[i
] * value
);
308 _swrast_use_read_buffer(ctx
);
310 /* May have to leave optimized accum buffer mode */
311 if (swrast
->_IntegerAccumScaler
== 0.0 && value
> 0.0 && value
<= 1.0)
312 swrast
->_IntegerAccumScaler
= value
;
313 if (swrast
->_IntegerAccumMode
&& value
!= swrast
->_IntegerAccumScaler
)
316 RENDER_START(swrast
,ctx
);
318 if (swrast
->_IntegerAccumMode
) {
319 /* simply add integer color values into accum buffer */
321 GLaccum
*acc
= ctx
->DrawBuffer
->Accum
+ ypos
* width4
+ xpos
* 4;
322 assert(swrast
->_IntegerAccumScaler
> 0.0);
323 assert(swrast
->_IntegerAccumScaler
<= 1.0);
324 for (j
= 0; j
< height
; j
++) {
327 _swrast_read_rgba_span(ctx
, ctx
->DrawBuffer
, width
, xpos
, ypos
, rgba
);
328 for (i
= i4
= 0; i
< width
; i
++, i4
+=4) {
329 acc
[i4
+0] += rgba
[i
][RCOMP
];
330 acc
[i4
+1] += rgba
[i
][GCOMP
];
331 acc
[i4
+2] += rgba
[i
][BCOMP
];
332 acc
[i4
+3] += rgba
[i
][ACOMP
];
339 /* scaled integer (or float) accum buffer */
340 const GLfloat rscale
= value
* acc_scale
/ CHAN_MAXF
;
341 const GLfloat gscale
= value
* acc_scale
/ CHAN_MAXF
;
342 const GLfloat bscale
= value
* acc_scale
/ CHAN_MAXF
;
343 const GLfloat ascale
= value
* acc_scale
/ CHAN_MAXF
;
345 for (j
=0;j
<height
;j
++) {
346 GLaccum
*acc
= ctx
->DrawBuffer
->Accum
+ ypos
* width4
+ xpos
* 4;
348 _swrast_read_rgba_span(ctx
, ctx
->DrawBuffer
, width
, xpos
, ypos
, rgba
);
349 for (i
=0;i
<width
;i
++) {
350 acc
[0] += (GLaccum
) ( (GLfloat
) rgba
[i
][RCOMP
] * rscale
);
351 acc
[1] += (GLaccum
) ( (GLfloat
) rgba
[i
][GCOMP
] * gscale
);
352 acc
[2] += (GLaccum
) ( (GLfloat
) rgba
[i
][BCOMP
] * bscale
);
353 acc
[3] += (GLaccum
) ( (GLfloat
) rgba
[i
][ACOMP
] * ascale
);
359 /* restore read buffer = draw buffer (the default) */
360 _swrast_use_draw_buffer(ctx
);
362 RENDER_FINISH(swrast
,ctx
);
366 _swrast_use_read_buffer(ctx
);
368 /* This is a change to go into optimized accum buffer mode */
369 if (value
> 0.0 && value
<= 1.0) {
370 #ifdef USE_OPTIMIZED_ACCUM
371 swrast
->_IntegerAccumMode
= GL_TRUE
;
373 swrast
->_IntegerAccumMode
= GL_FALSE
;
375 swrast
->_IntegerAccumScaler
= value
;
378 swrast
->_IntegerAccumMode
= GL_FALSE
;
379 swrast
->_IntegerAccumScaler
= 0.0;
382 RENDER_START(swrast
,ctx
);
383 if (swrast
->_IntegerAccumMode
) {
384 /* just copy values into accum buffer */
386 GLaccum
*acc
= ctx
->DrawBuffer
->Accum
+ ypos
* width4
+ xpos
* 4;
387 assert(swrast
->_IntegerAccumScaler
> 0.0);
388 assert(swrast
->_IntegerAccumScaler
<= 1.0);
389 for (j
= 0; j
< height
; j
++) {
391 _swrast_read_rgba_span(ctx
, ctx
->DrawBuffer
, width
, xpos
, ypos
, rgba
);
392 for (i
= i4
= 0; i
< width
; i
++, i4
+= 4) {
393 acc
[i4
+0] = rgba
[i
][RCOMP
];
394 acc
[i4
+1] = rgba
[i
][GCOMP
];
395 acc
[i4
+2] = rgba
[i
][BCOMP
];
396 acc
[i4
+3] = rgba
[i
][ACOMP
];
403 /* scaled integer (or float) accum buffer */
404 const GLfloat rscale
= value
* acc_scale
/ CHAN_MAXF
;
405 const GLfloat gscale
= value
* acc_scale
/ CHAN_MAXF
;
406 const GLfloat bscale
= value
* acc_scale
/ CHAN_MAXF
;
407 const GLfloat ascale
= value
* acc_scale
/ CHAN_MAXF
;
409 const GLfloat d
= 3.0 / acc_scale
; /* XXX what's this? */
412 for (j
= 0; j
< height
; j
++) {
413 GLaccum
*acc
= ctx
->DrawBuffer
->Accum
+ ypos
* width4
+ xpos
* 4;
414 _swrast_read_rgba_span(ctx
, ctx
->DrawBuffer
, width
, xpos
, ypos
, rgba
);
415 for (i
=0;i
<width
;i
++) {
417 *acc
++ = (GLaccum
) ((GLfloat
) rgba
[i
][RCOMP
] * rscale
+ d
);
418 *acc
++ = (GLaccum
) ((GLfloat
) rgba
[i
][GCOMP
] * gscale
+ d
);
419 *acc
++ = (GLaccum
) ((GLfloat
) rgba
[i
][BCOMP
] * bscale
+ d
);
420 *acc
++ = (GLaccum
) ((GLfloat
) rgba
[i
][ACOMP
] * ascale
+ d
);
422 *acc
++ = (GLaccum
) ((GLfloat
) rgba
[i
][RCOMP
] * rscale
);
423 *acc
++ = (GLaccum
) ((GLfloat
) rgba
[i
][GCOMP
] * gscale
);
424 *acc
++ = (GLaccum
) ((GLfloat
) rgba
[i
][BCOMP
] * bscale
);
425 *acc
++ = (GLaccum
) ((GLfloat
) rgba
[i
][ACOMP
] * ascale
);
432 /* restore read buffer = draw buffer (the default) */
433 _swrast_use_draw_buffer(ctx
);
435 RENDER_FINISH(swrast
,ctx
);
439 /* May have to leave optimized accum buffer mode */
440 if (swrast
->_IntegerAccumMode
&& value
!= 1.0)
443 RENDER_START(swrast
,ctx
);
444 #ifdef USE_OPTIMIZED_ACCUM
445 if (swrast
->_IntegerAccumMode
&& swrast
->_IntegerAccumScaler
> 0) {
446 /* build lookup table to avoid many floating point multiplies */
447 static GLchan multTable
[32768];
448 static GLfloat prevMult
= 0.0;
449 const GLfloat mult
= swrast
->_IntegerAccumScaler
;
450 const GLint max
= MIN2((GLint
) (256 / mult
), 32767);
452 if (mult
!= prevMult
) {
453 for (j
= 0; j
< max
; j
++)
454 multTable
[j
] = IROUND((GLfloat
) j
* mult
);
458 assert(swrast
->_IntegerAccumScaler
> 0.0);
459 assert(swrast
->_IntegerAccumScaler
<= 1.0);
460 for (j
= 0; j
< height
; j
++) {
461 const GLaccum
*acc
= ctx
->DrawBuffer
->Accum
+ ypos
* width4
+ xpos
*4;
463 for (i
= i4
= 0; i
< width
; i
++, i4
+= 4) {
464 ASSERT(acc
[i4
+0] < max
);
465 ASSERT(acc
[i4
+1] < max
);
466 ASSERT(acc
[i4
+2] < max
);
467 ASSERT(acc
[i4
+3] < max
);
468 rgba
[i
][RCOMP
] = multTable
[acc
[i4
+0]];
469 rgba
[i
][GCOMP
] = multTable
[acc
[i4
+1]];
470 rgba
[i
][BCOMP
] = multTable
[acc
[i4
+2]];
471 rgba
[i
][ACOMP
] = multTable
[acc
[i4
+3]];
473 if (colorMask
!= 0xffffffff) {
474 _swrast_mask_rgba_array( ctx
, width
, xpos
, ypos
, rgba
);
476 (*swrast
->Driver
.WriteRGBASpan
)( ctx
, width
, xpos
, ypos
,
477 (const GLchan (*)[4])rgba
, NULL
);
478 if (ctx
->DrawBuffer
->UseSoftwareAlphaBuffers
479 && ctx
->Color
.ColorMask
[ACOMP
]) {
480 _swrast_write_alpha_span(ctx
, width
, xpos
, ypos
,
481 (CONST
GLchan (*)[4]) rgba
, NULL
);
487 #endif /* USE_OPTIMIZED_ACCUM */
489 /* scaled integer (or float) accum buffer */
490 const GLfloat rscale
= value
/ acc_scale
* CHAN_MAXF
;
491 const GLfloat gscale
= value
/ acc_scale
* CHAN_MAXF
;
492 const GLfloat bscale
= value
/ acc_scale
* CHAN_MAXF
;
493 const GLfloat ascale
= value
/ acc_scale
* CHAN_MAXF
;
495 for (j
=0;j
<height
;j
++) {
496 const GLaccum
*acc
= ctx
->DrawBuffer
->Accum
+ ypos
* width4
+ xpos
*4;
497 for (i
=0;i
<width
;i
++) {
498 GLint r
= IROUND( (GLfloat
) (acc
[0]) * rscale
);
499 GLint g
= IROUND( (GLfloat
) (acc
[1]) * gscale
);
500 GLint b
= IROUND( (GLfloat
) (acc
[2]) * bscale
);
501 GLint a
= IROUND( (GLfloat
) (acc
[3]) * ascale
);
503 rgba
[i
][RCOMP
] = CLAMP( r
, 0, CHAN_MAX
);
504 rgba
[i
][GCOMP
] = CLAMP( g
, 0, CHAN_MAX
);
505 rgba
[i
][BCOMP
] = CLAMP( b
, 0, CHAN_MAX
);
506 rgba
[i
][ACOMP
] = CLAMP( a
, 0, CHAN_MAX
);
508 if (colorMask
!= 0xffffffff) {
509 _swrast_mask_rgba_array( ctx
, width
, xpos
, ypos
, rgba
);
511 (*swrast
->Driver
.WriteRGBASpan
)( ctx
, width
, xpos
, ypos
,
512 (const GLchan (*)[4])rgba
, NULL
);
513 if (ctx
->DrawBuffer
->UseSoftwareAlphaBuffers
514 && ctx
->Color
.ColorMask
[ACOMP
]) {
515 _swrast_write_alpha_span(ctx
, width
, xpos
, ypos
,
516 (CONST
GLchan (*)[4]) rgba
, NULL
);
521 RENDER_FINISH(swrast
,ctx
);
525 _mesa_error( ctx
, GL_INVALID_ENUM
, "glAccum" );