1 /* $Id: accum.c,v 1.2 1999/08/19 11:54:28 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.
43 #include "GL/xf86glx.h"
49 * Accumulation buffer notes
51 * Normally, accumulation buffer values are GLshorts with values in
52 * [-32767, 32767] which represent floating point colors in [-1, 1],
53 * as suggested by the OpenGL specification.
55 * We optimize for the common case used for full-scene antialiasing:
56 * // start with accum buffer cleared to zero
57 * glAccum(GL_LOAD, w); // or GL_ACCUM the first image
58 * glAccum(GL_ACCUM, w);
60 * glAccum(GL_ACCUM, w);
61 * glAccum(GL_RETURN, 1.0);
62 * That is, we start with an empty accumulation buffer and accumulate
63 * n images, each with weight w = 1/n.
64 * In this scenario, we can simply store unscaled integer values in
65 * the accum buffer instead of scaled integers. We'll also keep track
66 * of the w value so when we do GL_RETURN we simply divide the accumulated
68 * This lets us avoid _many_ int->float->int conversions.
73 void gl_alloc_accum_buffer( GLcontext
*ctx
)
77 if (ctx
->Buffer
->Accum
) {
78 free( ctx
->Buffer
->Accum
);
79 ctx
->Buffer
->Accum
= NULL
;
82 /* allocate accumulation buffer if not already present */
83 n
= ctx
->Buffer
->Width
* ctx
->Buffer
->Height
* 4 * sizeof(GLaccum
);
84 ctx
->Buffer
->Accum
= (GLaccum
*) malloc( n
);
85 if (!ctx
->Buffer
->Accum
) {
86 /* unable to setup accumulation buffer */
87 gl_error( ctx
, GL_OUT_OF_MEMORY
, "glAccum" );
89 ctx
->IntegerAccumMode
= GL_TRUE
;
90 ctx
->IntegerAccumScaler
= 0.0;
95 void gl_ClearAccum( GLcontext
*ctx
,
96 GLfloat red
, GLfloat green
, GLfloat blue
, GLfloat alpha
)
98 ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx
, "glAccum");
100 ctx
->Accum
.ClearColor
[0] = CLAMP( red
, -1.0, 1.0 );
101 ctx
->Accum
.ClearColor
[1] = CLAMP( green
, -1.0, 1.0 );
102 ctx
->Accum
.ClearColor
[2] = CLAMP( blue
, -1.0, 1.0 );
103 ctx
->Accum
.ClearColor
[3] = CLAMP( alpha
, -1.0, 1.0 );
109 * This is called when we fall out of optimized/unscaled accum buffer mode.
110 * That is, we convert each unscaled accum buffer value into a scaled value
111 * representing the range[-1, 1].
113 static void rescale_accum( GLcontext
*ctx
)
115 const GLuint n
= ctx
->Buffer
->Width
* ctx
->Buffer
->Height
* 4;
116 const GLfloat s
= ctx
->IntegerAccumScaler
* (32767.0 / 255.0);
117 GLaccum
*accum
= ctx
->Buffer
->Accum
;
120 assert(ctx
->IntegerAccumMode
);
121 assert(sizeof(GLchan
) == 1); /* if not true, 255.0 above must be fixed */
124 for (i
= 0; i
< n
; i
++) {
125 accum
[i
] = (GLaccum
) (accum
[i
] * s
);
128 ctx
->IntegerAccumMode
= GL_FALSE
;
133 void gl_Accum( GLcontext
*ctx
, GLenum op
, GLfloat value
)
135 GLuint xpos
, ypos
, width
, height
, width4
;
137 GLubyte rgba
[MAX_WIDTH
][4];
139 ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx
, "glAccum");
141 if (ctx
->Visual
->AccumBits
==0 || !ctx
->Buffer
->Accum
) {
142 /* No accumulation buffer! */
143 gl_warning(ctx
, "Calling glAccum() without an accumulation buffer");
147 if (sizeof(GLaccum
)==1) {
150 else if (sizeof(GLaccum
)==2) {
154 /* sizeof(GLaccum) > 2 (Cray) */
155 acc_scale
= (float) SHRT_MAX
;
159 gl_update_state( ctx
);
161 /* Determine region to operate upon. */
162 if (ctx
->Scissor
.Enabled
) {
163 xpos
= ctx
->Scissor
.X
;
164 ypos
= ctx
->Scissor
.Y
;
165 width
= ctx
->Scissor
.Width
;
166 height
= ctx
->Scissor
.Height
;
172 width
= ctx
->Buffer
->Width
;
173 height
= ctx
->Buffer
->Height
;
181 const GLaccum intVal
= (GLaccum
) (value
* acc_scale
);
183 /* May have to leave optimized accum buffer mode */
184 if (ctx
->IntegerAccumMode
)
186 for (j
= 0; j
< height
; j
++) {
187 GLaccum
* acc
= ctx
->Buffer
->Accum
+ ypos
* width4
+ 4 * xpos
;
189 for (i
= 0; i
< width4
; i
++) {
200 /* May have to leave optimized accum buffer mode */
201 if (ctx
->IntegerAccumMode
)
203 for (j
= 0; j
< height
; j
++) {
204 GLaccum
*acc
= ctx
->Buffer
->Accum
+ ypos
* width4
+ 4 * xpos
;
206 for (i
= 0; i
< width4
; i
++) {
207 acc
[i
] = (GLaccum
) ( (GLfloat
) acc
[i
] * value
);
215 (void) (*ctx
->Driver
.SetBuffer
)( ctx
, ctx
->Pixel
.DriverReadBuffer
);
217 /* May have to leave optimized accum buffer mode */
218 if (ctx
->IntegerAccumScaler
== 0.0 && value
> 0.0 && value
<= 1.0)
219 ctx
->IntegerAccumScaler
= value
;
220 if (ctx
->IntegerAccumMode
&& value
!= ctx
->IntegerAccumScaler
)
223 if (ctx
->IntegerAccumMode
) {
224 /* simply add integer color values into accum buffer */
226 GLaccum
*acc
= ctx
->Buffer
->Accum
+ ypos
* width4
+ xpos
* 4;
227 assert(ctx
->IntegerAccumScaler
> 0.0);
228 assert(ctx
->IntegerAccumScaler
<= 1.0);
229 for (j
= 0; j
< height
; j
++) {
232 gl_read_rgba_span(ctx
, width
, xpos
, ypos
, rgba
);
233 for (i
= i4
= 0; i
< width
; i
++, i4
+=4) {
234 acc
[i4
+0] += rgba
[i
][RCOMP
];
235 acc
[i4
+1] += rgba
[i
][GCOMP
];
236 acc
[i4
+2] += rgba
[i
][BCOMP
];
237 acc
[i4
+3] += rgba
[i
][ACOMP
];
244 /* scaled integer accum buffer */
245 const GLfloat rscale
= value
* acc_scale
/ 255.0;
246 const GLfloat gscale
= value
* acc_scale
/ 255.0;
247 const GLfloat bscale
= value
* acc_scale
/ 255.0;
248 const GLfloat ascale
= value
* acc_scale
/ 255.0;
250 for (j
=0;j
<height
;j
++) {
251 GLaccum
*acc
= ctx
->Buffer
->Accum
+ ypos
* width4
+ xpos
* 4;
253 gl_read_rgba_span(ctx
, width
, xpos
, ypos
, rgba
);
254 for (i
=0;i
<width
;i
++) {
255 *acc
+= (GLaccum
) ( (GLfloat
) rgba
[i
][RCOMP
] * rscale
); acc
++;
256 *acc
+= (GLaccum
) ( (GLfloat
) rgba
[i
][GCOMP
] * gscale
); acc
++;
257 *acc
+= (GLaccum
) ( (GLfloat
) rgba
[i
][BCOMP
] * bscale
); acc
++;
258 *acc
+= (GLaccum
) ( (GLfloat
) rgba
[i
][ACOMP
] * ascale
); acc
++;
263 (void) (*ctx
->Driver
.SetBuffer
)( ctx
, ctx
->Color
.DriverDrawBuffer
);
267 (void) (*ctx
->Driver
.SetBuffer
)( ctx
, ctx
->Pixel
.DriverReadBuffer
);
269 /* This is a change to go into optimized accum buffer mode */
270 if (value
> 0.0 && value
<= 1.0) {
271 ctx
->IntegerAccumMode
= GL_TRUE
;
272 ctx
->IntegerAccumScaler
= value
;
275 ctx
->IntegerAccumMode
= GL_FALSE
;
276 ctx
->IntegerAccumScaler
= 0.0;
279 if (ctx
->IntegerAccumMode
) {
280 /* just copy values into accum buffer */
282 GLaccum
*acc
= ctx
->Buffer
->Accum
+ ypos
* width4
+ xpos
* 4;
283 assert(ctx
->IntegerAccumScaler
> 0.0);
284 assert(ctx
->IntegerAccumScaler
<= 1.0);
285 for (j
= 0; j
< height
; j
++) {
287 gl_read_rgba_span(ctx
, width
, xpos
, ypos
, rgba
);
288 for (i
= i4
= 0; i
< width
; i
++, i4
+= 4) {
289 acc
[i4
+0] = rgba
[i
][RCOMP
];
290 acc
[i4
+1] = rgba
[i
][GCOMP
];
291 acc
[i4
+2] = rgba
[i
][BCOMP
];
292 acc
[i4
+3] = rgba
[i
][ACOMP
];
299 /* scaled integer accum buffer */
300 const GLfloat rscale
= value
* acc_scale
/ 255.0;
301 const GLfloat gscale
= value
* acc_scale
/ 255.0;
302 const GLfloat bscale
= value
* acc_scale
/ 255.0;
303 const GLfloat ascale
= value
* acc_scale
/ 255.0;
305 for (j
= 0; j
< height
; j
++) {
306 GLaccum
*acc
= ctx
->Buffer
->Accum
+ ypos
* width4
+ xpos
* 4;
307 gl_read_rgba_span(ctx
, width
, xpos
, ypos
, rgba
);
308 for (i
=0;i
<width
;i
++) {
309 *acc
++ = (GLaccum
) ( (GLfloat
) rgba
[i
][RCOMP
] * rscale
);
310 *acc
++ = (GLaccum
) ( (GLfloat
) rgba
[i
][GCOMP
] * gscale
);
311 *acc
++ = (GLaccum
) ( (GLfloat
) rgba
[i
][BCOMP
] * bscale
);
312 *acc
++ = (GLaccum
) ( (GLfloat
) rgba
[i
][ACOMP
] * ascale
);
317 (void) (*ctx
->Driver
.SetBuffer
)( ctx
, ctx
->Color
.DriverDrawBuffer
);
321 /* May have to leave optimized accum buffer mode */
322 if (ctx
->IntegerAccumMode
&& value
!= 1.0)
325 if (ctx
->IntegerAccumMode
) {
326 /* build lookup table to avoid integer divides */
327 GLint divisor
= (GLint
) ((1.0F
/ ctx
->IntegerAccumScaler
) + 0.5F
);
328 static GLubyte divTable
[32768];
329 static GLint prevDivisor
= 0.0;
331 if (divisor
!= prevDivisor
) {
332 assert(divisor
* 256 <= 32768);
333 for (j
= 0; j
< divisor
* 256; j
++)
334 divTable
[j
] = j
/ divisor
;
335 prevDivisor
= divisor
;
338 assert(ctx
->IntegerAccumScaler
> 0.0);
339 assert(ctx
->IntegerAccumScaler
<= 1.0);
340 for (j
= 0; j
< height
; j
++) {
341 const GLaccum
*acc
= ctx
->Buffer
->Accum
+ ypos
* width4
+ xpos
*4;
343 for (i
= i4
= 0; i
< width
; i
++, i4
+= 4) {
344 ASSERT(acc
[i4
+0] < divisor
* 256);
345 ASSERT(acc
[i4
+1] < divisor
* 256);
346 ASSERT(acc
[i4
+2] < divisor
* 256);
347 ASSERT(acc
[i4
+3] < divisor
* 256);
348 rgba
[i
][RCOMP
] = divTable
[acc
[i4
+0]];
349 rgba
[i
][GCOMP
] = divTable
[acc
[i4
+1]];
350 rgba
[i
][BCOMP
] = divTable
[acc
[i4
+2]];
351 rgba
[i
][ACOMP
] = divTable
[acc
[i4
+3]];
353 if (ctx
->Color
.SWmasking
) {
354 gl_mask_rgba_span( ctx
, width
, xpos
, ypos
, rgba
);
356 (*ctx
->Driver
.WriteRGBASpan
)( ctx
, width
, xpos
, ypos
,
357 (const GLubyte (*)[4])rgba
, NULL
);
362 const GLfloat rscale
= value
/ acc_scale
* 255.0F
;
363 const GLfloat gscale
= value
/ acc_scale
* 255.0F
;
364 const GLfloat bscale
= value
/ acc_scale
* 255.0F
;
365 const GLfloat ascale
= value
/ acc_scale
* 255.0F
;
367 for (j
=0;j
<height
;j
++) {
368 const GLaccum
*acc
= ctx
->Buffer
->Accum
+ ypos
* width4
+ xpos
*4;
369 for (i
=0;i
<width
;i
++) {
371 r
= (GLint
) ( (GLfloat
) (*acc
++) * rscale
+ 0.5F
);
372 g
= (GLint
) ( (GLfloat
) (*acc
++) * gscale
+ 0.5F
);
373 b
= (GLint
) ( (GLfloat
) (*acc
++) * bscale
+ 0.5F
);
374 a
= (GLint
) ( (GLfloat
) (*acc
++) * ascale
+ 0.5F
);
375 rgba
[i
][RCOMP
] = CLAMP( r
, 0, 255 );
376 rgba
[i
][GCOMP
] = CLAMP( g
, 0, 255 );
377 rgba
[i
][BCOMP
] = CLAMP( b
, 0, 255 );
378 rgba
[i
][ACOMP
] = CLAMP( a
, 0, 255 );
380 if (ctx
->Color
.SWmasking
) {
381 gl_mask_rgba_span( ctx
, width
, xpos
, ypos
, rgba
);
383 (*ctx
->Driver
.WriteRGBASpan
)( ctx
, width
, xpos
, ypos
,
384 (const GLubyte (*)[4])rgba
, NULL
);
391 gl_error( ctx
, GL_INVALID_ENUM
, "glAccum" );
398 * Clear the accumulation Buffer.
400 void gl_clear_accum_buffer( GLcontext
*ctx
)
405 if (ctx
->Visual
->AccumBits
==0) {
406 /* No accumulation buffer! */
410 if (sizeof(GLaccum
)==1) {
413 else if (sizeof(GLaccum
)==2) {
417 /* sizeof(GLaccum) > 2 (Cray) */
418 acc_scale
= (float) SHRT_MAX
;
421 /* number of pixels */
422 buffersize
= ctx
->Buffer
->Width
* ctx
->Buffer
->Height
;
424 if (!ctx
->Buffer
->Accum
) {
425 /* try to alloc accumulation buffer */
426 ctx
->Buffer
->Accum
= (GLaccum
*)
427 malloc( buffersize
* 4 * sizeof(GLaccum
) );
430 if (ctx
->Buffer
->Accum
) {
431 if (ctx
->Scissor
.Enabled
) {
432 /* Limit clear to scissor box */
437 r
= (GLaccum
) (ctx
->Accum
.ClearColor
[0] * acc_scale
);
438 g
= (GLaccum
) (ctx
->Accum
.ClearColor
[1] * acc_scale
);
439 b
= (GLaccum
) (ctx
->Accum
.ClearColor
[2] * acc_scale
);
440 a
= (GLaccum
) (ctx
->Accum
.ClearColor
[3] * acc_scale
);
441 /* size of region to clear */
442 width
= 4 * (ctx
->Buffer
->Xmax
- ctx
->Buffer
->Xmin
+ 1);
443 height
= ctx
->Buffer
->Ymax
- ctx
->Buffer
->Ymin
+ 1;
444 /* ptr to first element to clear */
445 row
= ctx
->Buffer
->Accum
446 + 4 * (ctx
->Buffer
->Ymin
* ctx
->Buffer
->Width
447 + ctx
->Buffer
->Xmin
);
448 for (j
=0;j
<height
;j
++) {
449 for (i
=0;i
<width
;i
+=4) {
455 row
+= 4 * ctx
->Buffer
->Width
;
459 /* clear whole buffer */
460 if (ctx
->Accum
.ClearColor
[0]==0.0 &&
461 ctx
->Accum
.ClearColor
[1]==0.0 &&
462 ctx
->Accum
.ClearColor
[2]==0.0 &&
463 ctx
->Accum
.ClearColor
[3]==0.0) {
465 MEMSET( ctx
->Buffer
->Accum
, 0, buffersize
* 4 * sizeof(GLaccum
) );
469 GLaccum
*acc
, r
, g
, b
, a
;
472 acc
= ctx
->Buffer
->Accum
;
473 r
= (GLaccum
) (ctx
->Accum
.ClearColor
[0] * acc_scale
);
474 g
= (GLaccum
) (ctx
->Accum
.ClearColor
[1] * acc_scale
);
475 b
= (GLaccum
) (ctx
->Accum
.ClearColor
[2] * acc_scale
);
476 a
= (GLaccum
) (ctx
->Accum
.ClearColor
[3] * acc_scale
);
477 for (i
=0;i
<buffersize
;i
++) {
486 /* update optimized accum state vars */
487 if (ctx
->Accum
.ClearColor
[0] == 0.0 && ctx
->Accum
.ClearColor
[1] == 0.0 &&
488 ctx
->Accum
.ClearColor
[2] == 0.0 && ctx
->Accum
.ClearColor
[3] == 0.0) {
489 ctx
->IntegerAccumMode
= GL_TRUE
;
490 ctx
->IntegerAccumScaler
= 0.0; /* denotes empty accum buffer */
493 ctx
->IntegerAccumMode
= GL_FALSE
;