1 /* $Id: accum.c,v 1.29 2000/10/29 18:23:16 brianp Exp $ */
4 * Mesa 3-D graphics library
7 * Copyright (C) 1999-2000 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.
44 * Accumulation buffer notes
46 * Normally, accumulation buffer values are GLshorts with values in
47 * [-32767, 32767] which represent floating point colors in [-1, 1],
48 * as suggested by the OpenGL specification.
50 * We optimize for the common case used for full-scene antialiasing:
51 * // start with accum buffer cleared to zero
52 * glAccum(GL_LOAD, w); // or GL_ACCUM the first image
53 * glAccum(GL_ACCUM, w);
55 * glAccum(GL_ACCUM, w);
56 * glAccum(GL_RETURN, 1.0);
57 * That is, we start with an empty accumulation buffer and accumulate
58 * n images, each with weight w = 1/n.
59 * In this scenario, we can simply store unscaled integer values in
60 * the accum buffer instead of scaled integers. We'll also keep track
61 * of the w value so when we do GL_RETURN we simply divide the accumulated
63 * This lets us avoid _many_ int->float->int conversions.
68 #define USE_OPTIMIZED_ACCUM /* enable the optimization */
74 _mesa_alloc_accum_buffer( GLcontext
*ctx
)
78 if (ctx
->DrawBuffer
->Accum
) {
79 FREE( ctx
->DrawBuffer
->Accum
);
80 ctx
->DrawBuffer
->Accum
= NULL
;
83 /* allocate accumulation buffer if not already present */
84 n
= ctx
->DrawBuffer
->Width
* ctx
->DrawBuffer
->Height
* 4 * sizeof(GLaccum
);
85 ctx
->DrawBuffer
->Accum
= (GLaccum
*) MALLOC( n
);
86 if (!ctx
->DrawBuffer
->Accum
) {
87 /* unable to setup accumulation buffer */
88 gl_error( ctx
, GL_OUT_OF_MEMORY
, "glAccum" );
90 #ifdef USE_OPTIMIZED_ACCUM
91 ctx
->IntegerAccumMode
= GL_TRUE
;
93 ctx
->IntegerAccumMode
= GL_FALSE
;
95 ctx
->IntegerAccumScaler
= 0.0;
101 _mesa_ClearAccum( GLfloat red
, GLfloat green
, GLfloat blue
, GLfloat alpha
)
103 GET_CURRENT_CONTEXT(ctx
);
104 ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx
, "glAccum");
106 ctx
->Accum
.ClearColor
[0] = CLAMP( red
, -1.0, 1.0 );
107 ctx
->Accum
.ClearColor
[1] = CLAMP( green
, -1.0, 1.0 );
108 ctx
->Accum
.ClearColor
[2] = CLAMP( blue
, -1.0, 1.0 );
109 ctx
->Accum
.ClearColor
[3] = CLAMP( alpha
, -1.0, 1.0 );
115 * This is called when we fall out of optimized/unscaled accum buffer mode.
116 * That is, we convert each unscaled accum buffer value into a scaled value
117 * representing the range[-1, 1].
119 static void rescale_accum( GLcontext
*ctx
)
121 const GLuint n
= ctx
->DrawBuffer
->Width
* ctx
->DrawBuffer
->Height
* 4;
122 const GLfloat fChanMax
= (1 << (sizeof(GLchan
) * 8)) - 1;
123 const GLfloat s
= ctx
->IntegerAccumScaler
* (32767.0 / fChanMax
);
124 GLaccum
*accum
= ctx
->DrawBuffer
->Accum
;
127 assert(ctx
->IntegerAccumMode
);
130 for (i
= 0; i
< n
; i
++) {
131 accum
[i
] = (GLaccum
) (accum
[i
] * s
);
134 ctx
->IntegerAccumMode
= GL_FALSE
;
140 _mesa_Accum( GLenum op
, GLfloat value
)
142 GET_CURRENT_CONTEXT(ctx
);
143 GLuint xpos
, ypos
, width
, height
, width4
;
145 GLchan rgba
[MAX_WIDTH
][4];
146 const GLuint colorMask
= *((GLuint
*) &ctx
->Color
.ColorMask
);
147 const GLint iChanMax
= (1 << (sizeof(GLchan
) * 8)) - 1;
148 const GLfloat fChanMax
= (1 << (sizeof(GLchan
) * 8)) - 1;
150 ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx
, "glAccum");
152 if (ctx
->Visual
.AccumRedBits
== 0 || ctx
->DrawBuffer
!= ctx
->ReadBuffer
) {
153 gl_error(ctx
, GL_INVALID_OPERATION
, "glAccum");
157 if (!ctx
->DrawBuffer
->Accum
) {
158 _mesa_warning(ctx
, "Calling glAccum() without an accumulation buffer (low memory?)");
162 if (sizeof(GLaccum
)==1) {
165 else if (sizeof(GLaccum
)==2) {
169 /* sizeof(GLaccum) > 2 (Cray) */
170 acc_scale
= (float) SHRT_MAX
;
174 gl_update_state( ctx
);
176 /* Determine region to operate upon. */
177 if (ctx
->Scissor
.Enabled
) {
178 xpos
= ctx
->Scissor
.X
;
179 ypos
= ctx
->Scissor
.Y
;
180 width
= ctx
->Scissor
.Width
;
181 height
= ctx
->Scissor
.Height
;
187 width
= ctx
->DrawBuffer
->Width
;
188 height
= ctx
->DrawBuffer
->Height
;
196 const GLaccum intVal
= (GLaccum
) (value
* acc_scale
);
198 /* Leave optimized accum buffer mode */
199 if (ctx
->IntegerAccumMode
)
201 for (j
= 0; j
< height
; j
++) {
202 GLaccum
* acc
= ctx
->DrawBuffer
->Accum
+ ypos
* width4
+ 4 * xpos
;
204 for (i
= 0; i
< width4
; i
++) {
215 /* Leave optimized accum buffer mode */
216 if (ctx
->IntegerAccumMode
)
218 for (j
= 0; j
< height
; j
++) {
219 GLaccum
*acc
= ctx
->DrawBuffer
->Accum
+ ypos
* width4
+ 4 * xpos
;
221 for (i
= 0; i
< width4
; i
++) {
222 acc
[i
] = (GLaccum
) ( (GLfloat
) acc
[i
] * value
);
233 (*ctx
->Driver
.SetReadBuffer
)( ctx
, ctx
->ReadBuffer
,
234 ctx
->Pixel
.DriverReadBuffer
);
236 /* May have to leave optimized accum buffer mode */
237 if (ctx
->IntegerAccumScaler
== 0.0 && value
> 0.0 && value
<= 1.0)
238 ctx
->IntegerAccumScaler
= value
;
239 if (ctx
->IntegerAccumMode
&& value
!= ctx
->IntegerAccumScaler
)
244 if (ctx
->IntegerAccumMode
) {
245 /* simply add integer color values into accum buffer */
247 GLaccum
*acc
= ctx
->DrawBuffer
->Accum
+ ypos
* width4
+ xpos
* 4;
248 assert(ctx
->IntegerAccumScaler
> 0.0);
249 assert(ctx
->IntegerAccumScaler
<= 1.0);
250 for (j
= 0; j
< height
; j
++) {
253 gl_read_rgba_span(ctx
, ctx
->DrawBuffer
, width
, xpos
, ypos
, rgba
);
254 for (i
= i4
= 0; i
< width
; i
++, i4
+=4) {
255 acc
[i4
+0] += rgba
[i
][RCOMP
];
256 acc
[i4
+1] += rgba
[i
][GCOMP
];
257 acc
[i4
+2] += rgba
[i
][BCOMP
];
258 acc
[i4
+3] += rgba
[i
][ACOMP
];
265 /* scaled integer accum buffer */
266 const GLfloat rscale
= value
* acc_scale
/ fChanMax
;
267 const GLfloat gscale
= value
* acc_scale
/ fChanMax
;
268 const GLfloat bscale
= value
* acc_scale
/ fChanMax
;
269 const GLfloat ascale
= value
* acc_scale
/ fChanMax
;
271 for (j
=0;j
<height
;j
++) {
272 GLaccum
*acc
= ctx
->DrawBuffer
->Accum
+ ypos
* width4
+ xpos
* 4;
274 gl_read_rgba_span(ctx
, ctx
->DrawBuffer
, width
, xpos
, ypos
, rgba
);
275 for (i
=0;i
<width
;i
++) {
276 *acc
+= (GLaccum
) ( (GLfloat
) rgba
[i
][RCOMP
] * rscale
); acc
++;
277 *acc
+= (GLaccum
) ( (GLfloat
) rgba
[i
][GCOMP
] * gscale
); acc
++;
278 *acc
+= (GLaccum
) ( (GLfloat
) rgba
[i
][BCOMP
] * bscale
); acc
++;
279 *acc
+= (GLaccum
) ( (GLfloat
) rgba
[i
][ACOMP
] * ascale
); acc
++;
284 /* restore read buffer = draw buffer (the default) */
285 (*ctx
->Driver
.SetReadBuffer
)( ctx
, ctx
->DrawBuffer
,
286 ctx
->Color
.DriverDrawBuffer
);
291 (*ctx
->Driver
.SetReadBuffer
)( ctx
, ctx
->ReadBuffer
,
292 ctx
->Pixel
.DriverReadBuffer
);
294 /* This is a change to go into optimized accum buffer mode */
295 if (value
> 0.0 && value
<= 1.0) {
296 #ifdef USE_OPTIMIZED_ACCUM
297 ctx
->IntegerAccumMode
= GL_TRUE
;
299 ctx
->IntegerAccumMode
= GL_FALSE
;
301 ctx
->IntegerAccumScaler
= value
;
304 ctx
->IntegerAccumMode
= GL_FALSE
;
305 ctx
->IntegerAccumScaler
= 0.0;
309 if (ctx
->IntegerAccumMode
) {
310 /* just copy values into accum buffer */
312 GLaccum
*acc
= ctx
->DrawBuffer
->Accum
+ ypos
* width4
+ xpos
* 4;
313 assert(ctx
->IntegerAccumScaler
> 0.0);
314 assert(ctx
->IntegerAccumScaler
<= 1.0);
315 for (j
= 0; j
< height
; j
++) {
317 gl_read_rgba_span(ctx
, ctx
->DrawBuffer
, width
, xpos
, ypos
, rgba
);
318 for (i
= i4
= 0; i
< width
; i
++, i4
+= 4) {
319 acc
[i4
+0] = rgba
[i
][RCOMP
];
320 acc
[i4
+1] = rgba
[i
][GCOMP
];
321 acc
[i4
+2] = rgba
[i
][BCOMP
];
322 acc
[i4
+3] = rgba
[i
][ACOMP
];
329 /* scaled integer accum buffer */
330 const GLfloat rscale
= value
* acc_scale
/ fChanMax
;
331 const GLfloat gscale
= value
* acc_scale
/ fChanMax
;
332 const GLfloat bscale
= value
* acc_scale
/ fChanMax
;
333 const GLfloat ascale
= value
* acc_scale
/ fChanMax
;
334 const GLfloat d
= 3.0 / acc_scale
;
336 for (j
= 0; j
< height
; j
++) {
337 GLaccum
*acc
= ctx
->DrawBuffer
->Accum
+ ypos
* width4
+ xpos
* 4;
338 gl_read_rgba_span(ctx
, ctx
->DrawBuffer
, width
, xpos
, ypos
, rgba
);
339 for (i
=0;i
<width
;i
++) {
340 *acc
++ = (GLaccum
) ((GLfloat
) rgba
[i
][RCOMP
] * rscale
+ d
);
341 *acc
++ = (GLaccum
) ((GLfloat
) rgba
[i
][GCOMP
] * gscale
+ d
);
342 *acc
++ = (GLaccum
) ((GLfloat
) rgba
[i
][BCOMP
] * bscale
+ d
);
343 *acc
++ = (GLaccum
) ((GLfloat
) rgba
[i
][ACOMP
] * ascale
+ d
);
349 /* restore read buffer = draw buffer (the default) */
350 (*ctx
->Driver
.SetReadBuffer
)( ctx
, ctx
->DrawBuffer
,
351 ctx
->Color
.DriverDrawBuffer
);
356 /* May have to leave optimized accum buffer mode */
357 if (ctx
->IntegerAccumMode
&& value
!= 1.0)
361 if (ctx
->IntegerAccumMode
&& ctx
->IntegerAccumScaler
> 0) {
362 /* build lookup table to avoid many floating point multiplies */
363 static GLchan multTable
[32768];
364 static GLfloat prevMult
= 0.0;
365 const GLfloat mult
= ctx
->IntegerAccumScaler
;
366 const GLint max
= MIN2((GLint
) (256 / mult
), 32767);
368 if (mult
!= prevMult
) {
369 for (j
= 0; j
< max
; j
++)
370 multTable
[j
] = (GLint
) ((GLfloat
) j
* mult
+ 0.5F
);
374 assert(ctx
->IntegerAccumScaler
> 0.0);
375 assert(ctx
->IntegerAccumScaler
<= 1.0);
376 for (j
= 0; j
< height
; j
++) {
377 const GLaccum
*acc
= ctx
->DrawBuffer
->Accum
+ ypos
* width4
+ xpos
*4;
379 for (i
= i4
= 0; i
< width
; i
++, i4
+= 4) {
380 ASSERT(acc
[i4
+0] < max
);
381 ASSERT(acc
[i4
+1] < max
);
382 ASSERT(acc
[i4
+2] < max
);
383 ASSERT(acc
[i4
+3] < max
);
384 rgba
[i
][RCOMP
] = multTable
[acc
[i4
+0]];
385 rgba
[i
][GCOMP
] = multTable
[acc
[i4
+1]];
386 rgba
[i
][BCOMP
] = multTable
[acc
[i4
+2]];
387 rgba
[i
][ACOMP
] = multTable
[acc
[i4
+3]];
389 if (colorMask
!= 0xffffffff) {
390 _mesa_mask_rgba_span( ctx
, width
, xpos
, ypos
, rgba
);
392 (*ctx
->Driver
.WriteRGBASpan
)( ctx
, width
, xpos
, ypos
,
393 (const GLchan (*)[4])rgba
, NULL
);
398 const GLfloat rscale
= value
/ acc_scale
* fChanMax
;
399 const GLfloat gscale
= value
/ acc_scale
* fChanMax
;
400 const GLfloat bscale
= value
/ acc_scale
* fChanMax
;
401 const GLfloat ascale
= value
/ acc_scale
* fChanMax
;
403 for (j
=0;j
<height
;j
++) {
404 const GLaccum
*acc
= ctx
->DrawBuffer
->Accum
+ ypos
* width4
+ xpos
*4;
405 for (i
=0;i
<width
;i
++) {
407 r
= (GLint
) ( (GLfloat
) (*acc
++) * rscale
+ 0.5F
);
408 g
= (GLint
) ( (GLfloat
) (*acc
++) * gscale
+ 0.5F
);
409 b
= (GLint
) ( (GLfloat
) (*acc
++) * bscale
+ 0.5F
);
410 a
= (GLint
) ( (GLfloat
) (*acc
++) * ascale
+ 0.5F
);
411 rgba
[i
][RCOMP
] = CLAMP( r
, 0, iChanMax
);
412 rgba
[i
][GCOMP
] = CLAMP( g
, 0, iChanMax
);
413 rgba
[i
][BCOMP
] = CLAMP( b
, 0, iChanMax
);
414 rgba
[i
][ACOMP
] = CLAMP( a
, 0, iChanMax
);
416 if (colorMask
!= 0xffffffff) {
417 _mesa_mask_rgba_span( ctx
, width
, xpos
, ypos
, rgba
);
419 (*ctx
->Driver
.WriteRGBASpan
)( ctx
, width
, xpos
, ypos
,
420 (const GLchan (*)[4])rgba
, NULL
);
428 gl_error( ctx
, GL_INVALID_ENUM
, "glAccum" );
435 * Clear the accumulation Buffer.
438 _mesa_clear_accum_buffer( GLcontext
*ctx
)
443 if (ctx
->Visual
.AccumRedBits
==0) {
444 /* No accumulation buffer! */
448 if (sizeof(GLaccum
)==1) {
451 else if (sizeof(GLaccum
)==2) {
455 /* sizeof(GLaccum) > 2 (Cray) */
456 acc_scale
= (float) SHRT_MAX
;
459 /* number of pixels */
460 buffersize
= ctx
->DrawBuffer
->Width
* ctx
->DrawBuffer
->Height
;
462 if (!ctx
->DrawBuffer
->Accum
) {
463 /* try to alloc accumulation buffer */
464 ctx
->DrawBuffer
->Accum
= (GLaccum
*)
465 MALLOC( buffersize
* 4 * sizeof(GLaccum
) );
468 if (ctx
->DrawBuffer
->Accum
) {
469 if (ctx
->Scissor
.Enabled
) {
470 /* Limit clear to scissor box */
475 r
= (GLaccum
) (ctx
->Accum
.ClearColor
[0] * acc_scale
);
476 g
= (GLaccum
) (ctx
->Accum
.ClearColor
[1] * acc_scale
);
477 b
= (GLaccum
) (ctx
->Accum
.ClearColor
[2] * acc_scale
);
478 a
= (GLaccum
) (ctx
->Accum
.ClearColor
[3] * acc_scale
);
479 /* size of region to clear */
480 width
= 4 * (ctx
->DrawBuffer
->Xmax
- ctx
->DrawBuffer
->Xmin
);
481 height
= ctx
->DrawBuffer
->Ymax
- ctx
->DrawBuffer
->Ymin
;
482 /* ptr to first element to clear */
483 row
= ctx
->DrawBuffer
->Accum
484 + 4 * (ctx
->DrawBuffer
->Ymin
* ctx
->DrawBuffer
->Width
485 + ctx
->DrawBuffer
->Xmin
);
486 for (j
=0;j
<height
;j
++) {
487 for (i
=0;i
<width
;i
+=4) {
493 row
+= 4 * ctx
->DrawBuffer
->Width
;
497 /* clear whole buffer */
498 if (ctx
->Accum
.ClearColor
[0]==0.0 &&
499 ctx
->Accum
.ClearColor
[1]==0.0 &&
500 ctx
->Accum
.ClearColor
[2]==0.0 &&
501 ctx
->Accum
.ClearColor
[3]==0.0) {
503 BZERO( ctx
->DrawBuffer
->Accum
, buffersize
* 4 * sizeof(GLaccum
) );
507 GLaccum
*acc
, r
, g
, b
, a
;
510 acc
= ctx
->DrawBuffer
->Accum
;
511 r
= (GLaccum
) (ctx
->Accum
.ClearColor
[0] * acc_scale
);
512 g
= (GLaccum
) (ctx
->Accum
.ClearColor
[1] * acc_scale
);
513 b
= (GLaccum
) (ctx
->Accum
.ClearColor
[2] * acc_scale
);
514 a
= (GLaccum
) (ctx
->Accum
.ClearColor
[3] * acc_scale
);
515 for (i
=0;i
<buffersize
;i
++) {
524 /* update optimized accum state vars */
525 if (ctx
->Accum
.ClearColor
[0] == 0.0 && ctx
->Accum
.ClearColor
[1] == 0.0 &&
526 ctx
->Accum
.ClearColor
[2] == 0.0 && ctx
->Accum
.ClearColor
[3] == 0.0) {
527 #ifdef USE_OPTIMIZED_ACCUM
528 ctx
->IntegerAccumMode
= GL_TRUE
;
530 ctx
->IntegerAccumMode
= GL_FALSE
;
532 ctx
->IntegerAccumScaler
= 0.0; /* denotes empty accum buffer */
535 ctx
->IntegerAccumMode
= GL_FALSE
;