1 /* $Id: accum.c,v 1.7 1999/10/20 22:32:02 brianp Exp $ */
4 * Mesa 3-D graphics library
7 * Copyright (C) 1999 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.
28 /* $XFree86: xc/lib/GL/mesa/src/accum.c,v 1.3 1999/04/04 00:20:17 dawes Exp $ */
38 #include "GL/xf86glx.h"
48 #include "GL/xf86glx.h"
54 * Accumulation buffer notes
56 * Normally, accumulation buffer values are GLshorts with values in
57 * [-32767, 32767] which represent floating point colors in [-1, 1],
58 * as suggested by the OpenGL specification.
60 * We optimize for the common case used for full-scene antialiasing:
61 * // start with accum buffer cleared to zero
62 * glAccum(GL_LOAD, w); // or GL_ACCUM the first image
63 * glAccum(GL_ACCUM, w);
65 * glAccum(GL_ACCUM, w);
66 * glAccum(GL_RETURN, 1.0);
67 * That is, we start with an empty accumulation buffer and accumulate
68 * n images, each with weight w = 1/n.
69 * In this scenario, we can simply store unscaled integer values in
70 * the accum buffer instead of scaled integers. We'll also keep track
71 * of the w value so when we do GL_RETURN we simply divide the accumulated
73 * This lets us avoid _many_ int->float->int conversions.
77 #define USE_OPTIMIZED_ACCUM /* enable the optimization */
81 void gl_alloc_accum_buffer( GLcontext
*ctx
)
85 if (ctx
->Buffer
->Accum
) {
86 FREE( ctx
->Buffer
->Accum
);
87 ctx
->Buffer
->Accum
= NULL
;
90 /* allocate accumulation buffer if not already present */
91 n
= ctx
->Buffer
->Width
* ctx
->Buffer
->Height
* 4 * sizeof(GLaccum
);
92 ctx
->Buffer
->Accum
= (GLaccum
*) MALLOC( n
);
93 if (!ctx
->Buffer
->Accum
) {
94 /* unable to setup accumulation buffer */
95 gl_error( ctx
, GL_OUT_OF_MEMORY
, "glAccum" );
97 #ifdef USE_OPTIMIZED_ACCUM
98 ctx
->IntegerAccumMode
= GL_TRUE
;
100 ctx
->IntegerAccumMode
= GL_FALSE
;
102 ctx
->IntegerAccumScaler
= 0.0;
107 void gl_ClearAccum( GLcontext
*ctx
,
108 GLfloat red
, GLfloat green
, GLfloat blue
, GLfloat alpha
)
110 ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx
, "glAccum");
112 ctx
->Accum
.ClearColor
[0] = CLAMP( red
, -1.0, 1.0 );
113 ctx
->Accum
.ClearColor
[1] = CLAMP( green
, -1.0, 1.0 );
114 ctx
->Accum
.ClearColor
[2] = CLAMP( blue
, -1.0, 1.0 );
115 ctx
->Accum
.ClearColor
[3] = CLAMP( alpha
, -1.0, 1.0 );
121 * This is called when we fall out of optimized/unscaled accum buffer mode.
122 * That is, we convert each unscaled accum buffer value into a scaled value
123 * representing the range[-1, 1].
125 static void rescale_accum( GLcontext
*ctx
)
127 const GLuint n
= ctx
->Buffer
->Width
* ctx
->Buffer
->Height
* 4;
128 const GLfloat fChanMax
= (1 << (sizeof(GLchan
) * 8)) - 1;
129 const GLfloat s
= ctx
->IntegerAccumScaler
* (32767.0 / fChanMax
);
130 GLaccum
*accum
= ctx
->Buffer
->Accum
;
133 assert(ctx
->IntegerAccumMode
);
136 for (i
= 0; i
< n
; i
++) {
137 accum
[i
] = (GLaccum
) (accum
[i
] * s
);
140 ctx
->IntegerAccumMode
= GL_FALSE
;
145 void gl_Accum( GLcontext
*ctx
, GLenum op
, GLfloat value
)
147 GLuint xpos
, ypos
, width
, height
, width4
;
149 GLubyte rgba
[MAX_WIDTH
][4];
150 const GLint iChanMax
= (1 << (sizeof(GLchan
) * 8)) - 1;
151 const GLfloat fChanMax
= (1 << (sizeof(GLchan
) * 8)) - 1;
153 ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx
, "glAccum");
155 if (ctx
->Visual
->AccumBits
==0 || !ctx
->Buffer
->Accum
) {
156 /* No accumulation buffer! */
157 gl_warning(ctx
, "Calling glAccum() without an accumulation buffer");
161 if (sizeof(GLaccum
)==1) {
164 else if (sizeof(GLaccum
)==2) {
168 /* sizeof(GLaccum) > 2 (Cray) */
169 acc_scale
= (float) SHRT_MAX
;
173 gl_update_state( ctx
);
175 /* Determine region to operate upon. */
176 if (ctx
->Scissor
.Enabled
) {
177 xpos
= ctx
->Scissor
.X
;
178 ypos
= ctx
->Scissor
.Y
;
179 width
= ctx
->Scissor
.Width
;
180 height
= ctx
->Scissor
.Height
;
186 width
= ctx
->Buffer
->Width
;
187 height
= ctx
->Buffer
->Height
;
195 const GLaccum intVal
= (GLaccum
) (value
* acc_scale
);
197 /* Leave optimized accum buffer mode */
198 if (ctx
->IntegerAccumMode
)
200 for (j
= 0; j
< height
; j
++) {
201 GLaccum
* acc
= ctx
->Buffer
->Accum
+ ypos
* width4
+ 4 * xpos
;
203 for (i
= 0; i
< width4
; i
++) {
214 /* Leave optimized accum buffer mode */
215 if (ctx
->IntegerAccumMode
)
217 for (j
= 0; j
< height
; j
++) {
218 GLaccum
*acc
= ctx
->Buffer
->Accum
+ ypos
* width4
+ 4 * xpos
;
220 for (i
= 0; i
< width4
; i
++) {
221 acc
[i
] = (GLaccum
) ( (GLfloat
) acc
[i
] * value
);
229 (void) (*ctx
->Driver
.SetBuffer
)( ctx
, ctx
->Pixel
.DriverReadBuffer
);
231 /* May have to leave optimized accum buffer mode */
232 if (ctx
->IntegerAccumScaler
== 0.0 && value
> 0.0 && value
<= 1.0)
233 ctx
->IntegerAccumScaler
= value
;
234 if (ctx
->IntegerAccumMode
&& value
!= ctx
->IntegerAccumScaler
)
237 if (ctx
->IntegerAccumMode
) {
238 /* simply add integer color values into accum buffer */
240 GLaccum
*acc
= ctx
->Buffer
->Accum
+ ypos
* width4
+ xpos
* 4;
241 assert(ctx
->IntegerAccumScaler
> 0.0);
242 assert(ctx
->IntegerAccumScaler
<= 1.0);
243 for (j
= 0; j
< height
; j
++) {
246 gl_read_rgba_span(ctx
, width
, xpos
, ypos
, rgba
);
247 for (i
= i4
= 0; i
< width
; i
++, i4
+=4) {
248 acc
[i4
+0] += rgba
[i
][RCOMP
];
249 acc
[i4
+1] += rgba
[i
][GCOMP
];
250 acc
[i4
+2] += rgba
[i
][BCOMP
];
251 acc
[i4
+3] += rgba
[i
][ACOMP
];
258 /* scaled integer accum buffer */
259 const GLfloat rscale
= value
* acc_scale
/ fChanMax
;
260 const GLfloat gscale
= value
* acc_scale
/ fChanMax
;
261 const GLfloat bscale
= value
* acc_scale
/ fChanMax
;
262 const GLfloat ascale
= value
* acc_scale
/ fChanMax
;
264 for (j
=0;j
<height
;j
++) {
265 GLaccum
*acc
= ctx
->Buffer
->Accum
+ ypos
* width4
+ xpos
* 4;
267 gl_read_rgba_span(ctx
, width
, xpos
, ypos
, rgba
);
268 for (i
=0;i
<width
;i
++) {
269 *acc
+= (GLaccum
) ( (GLfloat
) rgba
[i
][RCOMP
] * rscale
); acc
++;
270 *acc
+= (GLaccum
) ( (GLfloat
) rgba
[i
][GCOMP
] * gscale
); acc
++;
271 *acc
+= (GLaccum
) ( (GLfloat
) rgba
[i
][BCOMP
] * bscale
); acc
++;
272 *acc
+= (GLaccum
) ( (GLfloat
) rgba
[i
][ACOMP
] * ascale
); acc
++;
277 (void) (*ctx
->Driver
.SetBuffer
)( ctx
, ctx
->Color
.DriverDrawBuffer
);
281 (void) (*ctx
->Driver
.SetBuffer
)( ctx
, ctx
->Pixel
.DriverReadBuffer
);
283 /* This is a change to go into optimized accum buffer mode */
284 if (value
> 0.0 && value
<= 1.0) {
285 #ifdef USE_OPTIMIZED_ACCUM
286 ctx
->IntegerAccumMode
= GL_TRUE
;
288 ctx
->IntegerAccumMode
= GL_FALSE
;
290 ctx
->IntegerAccumScaler
= value
;
293 ctx
->IntegerAccumMode
= GL_FALSE
;
294 ctx
->IntegerAccumScaler
= 0.0;
297 if (ctx
->IntegerAccumMode
) {
298 /* just copy values into accum buffer */
300 GLaccum
*acc
= ctx
->Buffer
->Accum
+ ypos
* width4
+ xpos
* 4;
301 assert(ctx
->IntegerAccumScaler
> 0.0);
302 assert(ctx
->IntegerAccumScaler
<= 1.0);
303 for (j
= 0; j
< height
; j
++) {
305 gl_read_rgba_span(ctx
, width
, xpos
, ypos
, rgba
);
306 for (i
= i4
= 0; i
< width
; i
++, i4
+= 4) {
307 acc
[i4
+0] = rgba
[i
][RCOMP
];
308 acc
[i4
+1] = rgba
[i
][GCOMP
];
309 acc
[i4
+2] = rgba
[i
][BCOMP
];
310 acc
[i4
+3] = rgba
[i
][ACOMP
];
317 /* scaled integer accum buffer */
318 const GLfloat rscale
= value
* acc_scale
/ fChanMax
;
319 const GLfloat gscale
= value
* acc_scale
/ fChanMax
;
320 const GLfloat bscale
= value
* acc_scale
/ fChanMax
;
321 const GLfloat ascale
= value
* acc_scale
/ fChanMax
;
322 const GLfloat d
= 3.0 / acc_scale
;
324 for (j
= 0; j
< height
; j
++) {
325 GLaccum
*acc
= ctx
->Buffer
->Accum
+ ypos
* width4
+ xpos
* 4;
326 gl_read_rgba_span(ctx
, width
, xpos
, ypos
, rgba
);
327 for (i
=0;i
<width
;i
++) {
328 *acc
++ = (GLaccum
) ((GLfloat
) rgba
[i
][RCOMP
] * rscale
+ d
);
329 *acc
++ = (GLaccum
) ((GLfloat
) rgba
[i
][GCOMP
] * gscale
+ d
);
330 *acc
++ = (GLaccum
) ((GLfloat
) rgba
[i
][BCOMP
] * bscale
+ d
);
331 *acc
++ = (GLaccum
) ((GLfloat
) rgba
[i
][ACOMP
] * ascale
+ d
);
336 (void) (*ctx
->Driver
.SetBuffer
)( ctx
, ctx
->Color
.DriverDrawBuffer
);
340 /* May have to leave optimized accum buffer mode */
341 if (ctx
->IntegerAccumMode
&& value
!= 1.0)
344 if (ctx
->IntegerAccumMode
) {
345 /* build lookup table to avoid integer divides */
346 GLfloat divisor
= 1.0F
/ ctx
->IntegerAccumScaler
;
347 static GLubyte divTable
[32768];
348 static GLfloat prevDivisor
= 0.0;
350 if (divisor
!= prevDivisor
) {
351 assert(divisor
* 256 <= 32768);
352 for (j
= 0; j
< divisor
* 256; j
++)
353 divTable
[j
] = (GLint
) ((GLfloat
) j
/ divisor
+ 0.5F
);
354 prevDivisor
= divisor
;
357 assert(ctx
->IntegerAccumScaler
> 0.0);
358 assert(ctx
->IntegerAccumScaler
<= 1.0);
359 for (j
= 0; j
< height
; j
++) {
360 const GLaccum
*acc
= ctx
->Buffer
->Accum
+ ypos
* width4
+ xpos
*4;
362 for (i
= i4
= 0; i
< width
; i
++, i4
+= 4) {
363 ASSERT(acc
[i4
+0] < divisor
* 256);
364 ASSERT(acc
[i4
+1] < divisor
* 256);
365 ASSERT(acc
[i4
+2] < divisor
* 256);
366 ASSERT(acc
[i4
+3] < divisor
* 256);
367 rgba
[i
][RCOMP
] = divTable
[acc
[i4
+0]];
368 rgba
[i
][GCOMP
] = divTable
[acc
[i4
+1]];
369 rgba
[i
][BCOMP
] = divTable
[acc
[i4
+2]];
370 rgba
[i
][ACOMP
] = divTable
[acc
[i4
+3]];
372 if (ctx
->Color
.SWmasking
) {
373 gl_mask_rgba_span( ctx
, width
, xpos
, ypos
, rgba
);
375 (*ctx
->Driver
.WriteRGBASpan
)( ctx
, width
, xpos
, ypos
,
376 (const GLubyte (*)[4])rgba
, NULL
);
381 const GLfloat rscale
= value
/ acc_scale
* fChanMax
;
382 const GLfloat gscale
= value
/ acc_scale
* fChanMax
;
383 const GLfloat bscale
= value
/ acc_scale
* fChanMax
;
384 const GLfloat ascale
= value
/ acc_scale
* fChanMax
;
386 for (j
=0;j
<height
;j
++) {
387 const GLaccum
*acc
= ctx
->Buffer
->Accum
+ ypos
* width4
+ xpos
*4;
388 for (i
=0;i
<width
;i
++) {
390 r
= (GLint
) ( (GLfloat
) (*acc
++) * rscale
+ 0.5F
);
391 g
= (GLint
) ( (GLfloat
) (*acc
++) * gscale
+ 0.5F
);
392 b
= (GLint
) ( (GLfloat
) (*acc
++) * bscale
+ 0.5F
);
393 a
= (GLint
) ( (GLfloat
) (*acc
++) * ascale
+ 0.5F
);
394 rgba
[i
][RCOMP
] = CLAMP( r
, 0, iChanMax
);
395 rgba
[i
][GCOMP
] = CLAMP( g
, 0, iChanMax
);
396 rgba
[i
][BCOMP
] = CLAMP( b
, 0, iChanMax
);
397 rgba
[i
][ACOMP
] = CLAMP( a
, 0, iChanMax
);
399 if (ctx
->Color
.SWmasking
) {
400 gl_mask_rgba_span( ctx
, width
, xpos
, ypos
, rgba
);
402 (*ctx
->Driver
.WriteRGBASpan
)( ctx
, width
, xpos
, ypos
,
403 (const GLubyte (*)[4])rgba
, NULL
);
410 gl_error( ctx
, GL_INVALID_ENUM
, "glAccum" );
417 * Clear the accumulation Buffer.
419 void gl_clear_accum_buffer( GLcontext
*ctx
)
424 if (ctx
->Visual
->AccumBits
==0) {
425 /* No accumulation buffer! */
429 if (sizeof(GLaccum
)==1) {
432 else if (sizeof(GLaccum
)==2) {
436 /* sizeof(GLaccum) > 2 (Cray) */
437 acc_scale
= (float) SHRT_MAX
;
440 /* number of pixels */
441 buffersize
= ctx
->Buffer
->Width
* ctx
->Buffer
->Height
;
443 if (!ctx
->Buffer
->Accum
) {
444 /* try to alloc accumulation buffer */
445 ctx
->Buffer
->Accum
= (GLaccum
*)
446 MALLOC( buffersize
* 4 * sizeof(GLaccum
) );
449 if (ctx
->Buffer
->Accum
) {
450 if (ctx
->Scissor
.Enabled
) {
451 /* Limit clear to scissor box */
456 r
= (GLaccum
) (ctx
->Accum
.ClearColor
[0] * acc_scale
);
457 g
= (GLaccum
) (ctx
->Accum
.ClearColor
[1] * acc_scale
);
458 b
= (GLaccum
) (ctx
->Accum
.ClearColor
[2] * acc_scale
);
459 a
= (GLaccum
) (ctx
->Accum
.ClearColor
[3] * acc_scale
);
460 /* size of region to clear */
461 width
= 4 * (ctx
->Buffer
->Xmax
- ctx
->Buffer
->Xmin
+ 1);
462 height
= ctx
->Buffer
->Ymax
- ctx
->Buffer
->Ymin
+ 1;
463 /* ptr to first element to clear */
464 row
= ctx
->Buffer
->Accum
465 + 4 * (ctx
->Buffer
->Ymin
* ctx
->Buffer
->Width
466 + ctx
->Buffer
->Xmin
);
467 for (j
=0;j
<height
;j
++) {
468 for (i
=0;i
<width
;i
+=4) {
474 row
+= 4 * ctx
->Buffer
->Width
;
478 /* clear whole buffer */
479 if (ctx
->Accum
.ClearColor
[0]==0.0 &&
480 ctx
->Accum
.ClearColor
[1]==0.0 &&
481 ctx
->Accum
.ClearColor
[2]==0.0 &&
482 ctx
->Accum
.ClearColor
[3]==0.0) {
484 MEMSET( ctx
->Buffer
->Accum
, 0, buffersize
* 4 * sizeof(GLaccum
) );
488 GLaccum
*acc
, r
, g
, b
, a
;
491 acc
= ctx
->Buffer
->Accum
;
492 r
= (GLaccum
) (ctx
->Accum
.ClearColor
[0] * acc_scale
);
493 g
= (GLaccum
) (ctx
->Accum
.ClearColor
[1] * acc_scale
);
494 b
= (GLaccum
) (ctx
->Accum
.ClearColor
[2] * acc_scale
);
495 a
= (GLaccum
) (ctx
->Accum
.ClearColor
[3] * acc_scale
);
496 for (i
=0;i
<buffersize
;i
++) {
505 /* update optimized accum state vars */
506 if (ctx
->Accum
.ClearColor
[0] == 0.0 && ctx
->Accum
.ClearColor
[1] == 0.0 &&
507 ctx
->Accum
.ClearColor
[2] == 0.0 && ctx
->Accum
.ClearColor
[3] == 0.0) {
508 #ifdef USE_OPTIMIZED_ACCUM
509 ctx
->IntegerAccumMode
= GL_TRUE
;
511 ctx
->IntegerAccumMode
= GL_FALSE
;
513 ctx
->IntegerAccumScaler
= 0.0; /* denotes empty accum buffer */
516 ctx
->IntegerAccumMode
= GL_FALSE
;