1 /* $Id: accum.c,v 1.11 1999/11/11 01:22:25 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.
42 * Accumulation buffer notes
44 * Normally, accumulation buffer values are GLshorts with values in
45 * [-32767, 32767] which represent floating point colors in [-1, 1],
46 * as suggested by the OpenGL specification.
48 * We optimize for the common case used for full-scene antialiasing:
49 * // start with accum buffer cleared to zero
50 * glAccum(GL_LOAD, w); // or GL_ACCUM the first image
51 * glAccum(GL_ACCUM, w);
53 * glAccum(GL_ACCUM, w);
54 * glAccum(GL_RETURN, 1.0);
55 * That is, we start with an empty accumulation buffer and accumulate
56 * n images, each with weight w = 1/n.
57 * In this scenario, we can simply store unscaled integer values in
58 * the accum buffer instead of scaled integers. We'll also keep track
59 * of the w value so when we do GL_RETURN we simply divide the accumulated
61 * This lets us avoid _many_ int->float->int conversions.
65 #define USE_OPTIMIZED_ACCUM /* enable the optimization */
69 void gl_alloc_accum_buffer( GLcontext
*ctx
)
73 if (ctx
->Buffer
->Accum
) {
74 FREE( ctx
->Buffer
->Accum
);
75 ctx
->Buffer
->Accum
= NULL
;
78 /* allocate accumulation buffer if not already present */
79 n
= ctx
->Buffer
->Width
* ctx
->Buffer
->Height
* 4 * sizeof(GLaccum
);
80 ctx
->Buffer
->Accum
= (GLaccum
*) MALLOC( n
);
81 if (!ctx
->Buffer
->Accum
) {
82 /* unable to setup accumulation buffer */
83 gl_error( ctx
, GL_OUT_OF_MEMORY
, "glAccum" );
85 #ifdef USE_OPTIMIZED_ACCUM
86 ctx
->IntegerAccumMode
= GL_TRUE
;
88 ctx
->IntegerAccumMode
= GL_FALSE
;
90 ctx
->IntegerAccumScaler
= 0.0;
96 _mesa_ClearAccum( GLfloat red
, GLfloat green
, GLfloat blue
, GLfloat alpha
)
98 GET_CURRENT_CONTEXT(ctx
);
99 ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx
, "glAccum");
101 ctx
->Accum
.ClearColor
[0] = CLAMP( red
, -1.0, 1.0 );
102 ctx
->Accum
.ClearColor
[1] = CLAMP( green
, -1.0, 1.0 );
103 ctx
->Accum
.ClearColor
[2] = CLAMP( blue
, -1.0, 1.0 );
104 ctx
->Accum
.ClearColor
[3] = CLAMP( alpha
, -1.0, 1.0 );
110 * This is called when we fall out of optimized/unscaled accum buffer mode.
111 * That is, we convert each unscaled accum buffer value into a scaled value
112 * representing the range[-1, 1].
114 static void rescale_accum( GLcontext
*ctx
)
116 const GLuint n
= ctx
->Buffer
->Width
* ctx
->Buffer
->Height
* 4;
117 const GLfloat fChanMax
= (1 << (sizeof(GLchan
) * 8)) - 1;
118 const GLfloat s
= ctx
->IntegerAccumScaler
* (32767.0 / fChanMax
);
119 GLaccum
*accum
= ctx
->Buffer
->Accum
;
122 assert(ctx
->IntegerAccumMode
);
125 for (i
= 0; i
< n
; i
++) {
126 accum
[i
] = (GLaccum
) (accum
[i
] * s
);
129 ctx
->IntegerAccumMode
= GL_FALSE
;
135 _mesa_Accum( GLenum op
, GLfloat value
)
137 GET_CURRENT_CONTEXT(ctx
);
138 GLuint xpos
, ypos
, width
, height
, width4
;
140 GLubyte rgba
[MAX_WIDTH
][4];
141 const GLint iChanMax
= (1 << (sizeof(GLchan
) * 8)) - 1;
142 const GLfloat fChanMax
= (1 << (sizeof(GLchan
) * 8)) - 1;
144 ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx
, "glAccum");
146 if (ctx
->Visual
->AccumBits
==0 || !ctx
->Buffer
->Accum
) {
147 /* No accumulation buffer! */
148 gl_warning(ctx
, "Calling glAccum() without an accumulation buffer");
152 if (sizeof(GLaccum
)==1) {
155 else if (sizeof(GLaccum
)==2) {
159 /* sizeof(GLaccum) > 2 (Cray) */
160 acc_scale
= (float) SHRT_MAX
;
164 gl_update_state( ctx
);
166 /* Determine region to operate upon. */
167 if (ctx
->Scissor
.Enabled
) {
168 xpos
= ctx
->Scissor
.X
;
169 ypos
= ctx
->Scissor
.Y
;
170 width
= ctx
->Scissor
.Width
;
171 height
= ctx
->Scissor
.Height
;
177 width
= ctx
->Buffer
->Width
;
178 height
= ctx
->Buffer
->Height
;
186 const GLaccum intVal
= (GLaccum
) (value
* acc_scale
);
188 /* Leave optimized accum buffer mode */
189 if (ctx
->IntegerAccumMode
)
191 for (j
= 0; j
< height
; j
++) {
192 GLaccum
* acc
= ctx
->Buffer
->Accum
+ ypos
* width4
+ 4 * xpos
;
194 for (i
= 0; i
< width4
; i
++) {
205 /* Leave optimized accum buffer mode */
206 if (ctx
->IntegerAccumMode
)
208 for (j
= 0; j
< height
; j
++) {
209 GLaccum
*acc
= ctx
->Buffer
->Accum
+ ypos
* width4
+ 4 * xpos
;
211 for (i
= 0; i
< width4
; i
++) {
212 acc
[i
] = (GLaccum
) ( (GLfloat
) acc
[i
] * value
);
220 (void) (*ctx
->Driver
.SetBuffer
)( ctx
, ctx
->Pixel
.DriverReadBuffer
);
222 /* May have to leave optimized accum buffer mode */
223 if (ctx
->IntegerAccumScaler
== 0.0 && value
> 0.0 && value
<= 1.0)
224 ctx
->IntegerAccumScaler
= value
;
225 if (ctx
->IntegerAccumMode
&& value
!= ctx
->IntegerAccumScaler
)
228 if (ctx
->IntegerAccumMode
) {
229 /* simply add integer color values into accum buffer */
231 GLaccum
*acc
= ctx
->Buffer
->Accum
+ ypos
* width4
+ xpos
* 4;
232 assert(ctx
->IntegerAccumScaler
> 0.0);
233 assert(ctx
->IntegerAccumScaler
<= 1.0);
234 for (j
= 0; j
< height
; j
++) {
237 gl_read_rgba_span(ctx
, width
, xpos
, ypos
, rgba
);
238 for (i
= i4
= 0; i
< width
; i
++, i4
+=4) {
239 acc
[i4
+0] += rgba
[i
][RCOMP
];
240 acc
[i4
+1] += rgba
[i
][GCOMP
];
241 acc
[i4
+2] += rgba
[i
][BCOMP
];
242 acc
[i4
+3] += rgba
[i
][ACOMP
];
249 /* scaled integer accum buffer */
250 const GLfloat rscale
= value
* acc_scale
/ fChanMax
;
251 const GLfloat gscale
= value
* acc_scale
/ fChanMax
;
252 const GLfloat bscale
= value
* acc_scale
/ fChanMax
;
253 const GLfloat ascale
= value
* acc_scale
/ fChanMax
;
255 for (j
=0;j
<height
;j
++) {
256 GLaccum
*acc
= ctx
->Buffer
->Accum
+ ypos
* width4
+ xpos
* 4;
258 gl_read_rgba_span(ctx
, width
, xpos
, ypos
, rgba
);
259 for (i
=0;i
<width
;i
++) {
260 *acc
+= (GLaccum
) ( (GLfloat
) rgba
[i
][RCOMP
] * rscale
); acc
++;
261 *acc
+= (GLaccum
) ( (GLfloat
) rgba
[i
][GCOMP
] * gscale
); acc
++;
262 *acc
+= (GLaccum
) ( (GLfloat
) rgba
[i
][BCOMP
] * bscale
); acc
++;
263 *acc
+= (GLaccum
) ( (GLfloat
) rgba
[i
][ACOMP
] * ascale
); acc
++;
268 (void) (*ctx
->Driver
.SetBuffer
)( ctx
, ctx
->Color
.DriverDrawBuffer
);
272 (void) (*ctx
->Driver
.SetBuffer
)( ctx
, ctx
->Pixel
.DriverReadBuffer
);
274 /* This is a change to go into optimized accum buffer mode */
275 if (value
> 0.0 && value
<= 1.0) {
276 #ifdef USE_OPTIMIZED_ACCUM
277 ctx
->IntegerAccumMode
= GL_TRUE
;
279 ctx
->IntegerAccumMode
= GL_FALSE
;
281 ctx
->IntegerAccumScaler
= value
;
284 ctx
->IntegerAccumMode
= GL_FALSE
;
285 ctx
->IntegerAccumScaler
= 0.0;
288 if (ctx
->IntegerAccumMode
) {
289 /* just copy values into accum buffer */
291 GLaccum
*acc
= ctx
->Buffer
->Accum
+ ypos
* width4
+ xpos
* 4;
292 assert(ctx
->IntegerAccumScaler
> 0.0);
293 assert(ctx
->IntegerAccumScaler
<= 1.0);
294 for (j
= 0; j
< height
; j
++) {
296 gl_read_rgba_span(ctx
, width
, xpos
, ypos
, rgba
);
297 for (i
= i4
= 0; i
< width
; i
++, i4
+= 4) {
298 acc
[i4
+0] = rgba
[i
][RCOMP
];
299 acc
[i4
+1] = rgba
[i
][GCOMP
];
300 acc
[i4
+2] = rgba
[i
][BCOMP
];
301 acc
[i4
+3] = rgba
[i
][ACOMP
];
308 /* scaled integer accum buffer */
309 const GLfloat rscale
= value
* acc_scale
/ fChanMax
;
310 const GLfloat gscale
= value
* acc_scale
/ fChanMax
;
311 const GLfloat bscale
= value
* acc_scale
/ fChanMax
;
312 const GLfloat ascale
= value
* acc_scale
/ fChanMax
;
313 const GLfloat d
= 3.0 / acc_scale
;
315 for (j
= 0; j
< height
; j
++) {
316 GLaccum
*acc
= ctx
->Buffer
->Accum
+ ypos
* width4
+ xpos
* 4;
317 gl_read_rgba_span(ctx
, width
, xpos
, ypos
, rgba
);
318 for (i
=0;i
<width
;i
++) {
319 *acc
++ = (GLaccum
) ((GLfloat
) rgba
[i
][RCOMP
] * rscale
+ d
);
320 *acc
++ = (GLaccum
) ((GLfloat
) rgba
[i
][GCOMP
] * gscale
+ d
);
321 *acc
++ = (GLaccum
) ((GLfloat
) rgba
[i
][BCOMP
] * bscale
+ d
);
322 *acc
++ = (GLaccum
) ((GLfloat
) rgba
[i
][ACOMP
] * ascale
+ d
);
327 (void) (*ctx
->Driver
.SetBuffer
)( ctx
, ctx
->Color
.DriverDrawBuffer
);
331 /* May have to leave optimized accum buffer mode */
332 if (ctx
->IntegerAccumMode
&& value
!= 1.0)
335 if (ctx
->IntegerAccumMode
) {
336 /* build lookup table to avoid many floating point multiplies */
337 const GLfloat mult
= ctx
->IntegerAccumScaler
;
338 static GLchan multTable
[32768];
339 static GLfloat prevMult
= 0.0;
341 const GLint max
= 256 / mult
;
342 if (mult
!= prevMult
) {
343 assert(max
<= 32768);
344 for (j
= 0; j
< max
; j
++)
345 multTable
[j
] = (GLint
) ((GLfloat
) j
* mult
+ 0.5F
);
349 assert(ctx
->IntegerAccumScaler
> 0.0);
350 assert(ctx
->IntegerAccumScaler
<= 1.0);
351 for (j
= 0; j
< height
; j
++) {
352 const GLaccum
*acc
= ctx
->Buffer
->Accum
+ ypos
* width4
+ xpos
*4;
354 for (i
= i4
= 0; i
< width
; i
++, i4
+= 4) {
355 ASSERT(acc
[i4
+0] < max
);
356 ASSERT(acc
[i4
+1] < max
);
357 ASSERT(acc
[i4
+2] < max
);
358 ASSERT(acc
[i4
+3] < max
);
359 rgba
[i
][RCOMP
] = multTable
[acc
[i4
+0]];
360 rgba
[i
][GCOMP
] = multTable
[acc
[i4
+1]];
361 rgba
[i
][BCOMP
] = multTable
[acc
[i4
+2]];
362 rgba
[i
][ACOMP
] = multTable
[acc
[i4
+3]];
364 if (ctx
->Color
.SWmasking
) {
365 gl_mask_rgba_span( ctx
, width
, xpos
, ypos
, rgba
);
367 (*ctx
->Driver
.WriteRGBASpan
)( ctx
, width
, xpos
, ypos
,
368 (const GLubyte (*)[4])rgba
, NULL
);
373 const GLfloat rscale
= value
/ acc_scale
* fChanMax
;
374 const GLfloat gscale
= value
/ acc_scale
* fChanMax
;
375 const GLfloat bscale
= value
/ acc_scale
* fChanMax
;
376 const GLfloat ascale
= value
/ acc_scale
* fChanMax
;
378 for (j
=0;j
<height
;j
++) {
379 const GLaccum
*acc
= ctx
->Buffer
->Accum
+ ypos
* width4
+ xpos
*4;
380 for (i
=0;i
<width
;i
++) {
382 r
= (GLint
) ( (GLfloat
) (*acc
++) * rscale
+ 0.5F
);
383 g
= (GLint
) ( (GLfloat
) (*acc
++) * gscale
+ 0.5F
);
384 b
= (GLint
) ( (GLfloat
) (*acc
++) * bscale
+ 0.5F
);
385 a
= (GLint
) ( (GLfloat
) (*acc
++) * ascale
+ 0.5F
);
386 rgba
[i
][RCOMP
] = CLAMP( r
, 0, iChanMax
);
387 rgba
[i
][GCOMP
] = CLAMP( g
, 0, iChanMax
);
388 rgba
[i
][BCOMP
] = CLAMP( b
, 0, iChanMax
);
389 rgba
[i
][ACOMP
] = CLAMP( a
, 0, iChanMax
);
391 if (ctx
->Color
.SWmasking
) {
392 gl_mask_rgba_span( ctx
, width
, xpos
, ypos
, rgba
);
394 (*ctx
->Driver
.WriteRGBASpan
)( ctx
, width
, xpos
, ypos
,
395 (const GLubyte (*)[4])rgba
, NULL
);
402 gl_error( ctx
, GL_INVALID_ENUM
, "glAccum" );
409 * Clear the accumulation Buffer.
411 void gl_clear_accum_buffer( GLcontext
*ctx
)
416 if (ctx
->Visual
->AccumBits
==0) {
417 /* No accumulation buffer! */
421 if (sizeof(GLaccum
)==1) {
424 else if (sizeof(GLaccum
)==2) {
428 /* sizeof(GLaccum) > 2 (Cray) */
429 acc_scale
= (float) SHRT_MAX
;
432 /* number of pixels */
433 buffersize
= ctx
->Buffer
->Width
* ctx
->Buffer
->Height
;
435 if (!ctx
->Buffer
->Accum
) {
436 /* try to alloc accumulation buffer */
437 ctx
->Buffer
->Accum
= (GLaccum
*)
438 MALLOC( buffersize
* 4 * sizeof(GLaccum
) );
441 if (ctx
->Buffer
->Accum
) {
442 if (ctx
->Scissor
.Enabled
) {
443 /* Limit clear to scissor box */
448 r
= (GLaccum
) (ctx
->Accum
.ClearColor
[0] * acc_scale
);
449 g
= (GLaccum
) (ctx
->Accum
.ClearColor
[1] * acc_scale
);
450 b
= (GLaccum
) (ctx
->Accum
.ClearColor
[2] * acc_scale
);
451 a
= (GLaccum
) (ctx
->Accum
.ClearColor
[3] * acc_scale
);
452 /* size of region to clear */
453 width
= 4 * (ctx
->Buffer
->Xmax
- ctx
->Buffer
->Xmin
+ 1);
454 height
= ctx
->Buffer
->Ymax
- ctx
->Buffer
->Ymin
+ 1;
455 /* ptr to first element to clear */
456 row
= ctx
->Buffer
->Accum
457 + 4 * (ctx
->Buffer
->Ymin
* ctx
->Buffer
->Width
458 + ctx
->Buffer
->Xmin
);
459 for (j
=0;j
<height
;j
++) {
460 for (i
=0;i
<width
;i
+=4) {
466 row
+= 4 * ctx
->Buffer
->Width
;
470 /* clear whole buffer */
471 if (ctx
->Accum
.ClearColor
[0]==0.0 &&
472 ctx
->Accum
.ClearColor
[1]==0.0 &&
473 ctx
->Accum
.ClearColor
[2]==0.0 &&
474 ctx
->Accum
.ClearColor
[3]==0.0) {
476 MEMSET( ctx
->Buffer
->Accum
, 0, buffersize
* 4 * sizeof(GLaccum
) );
480 GLaccum
*acc
, r
, g
, b
, a
;
483 acc
= ctx
->Buffer
->Accum
;
484 r
= (GLaccum
) (ctx
->Accum
.ClearColor
[0] * acc_scale
);
485 g
= (GLaccum
) (ctx
->Accum
.ClearColor
[1] * acc_scale
);
486 b
= (GLaccum
) (ctx
->Accum
.ClearColor
[2] * acc_scale
);
487 a
= (GLaccum
) (ctx
->Accum
.ClearColor
[3] * acc_scale
);
488 for (i
=0;i
<buffersize
;i
++) {
497 /* update optimized accum state vars */
498 if (ctx
->Accum
.ClearColor
[0] == 0.0 && ctx
->Accum
.ClearColor
[1] == 0.0 &&
499 ctx
->Accum
.ClearColor
[2] == 0.0 && ctx
->Accum
.ClearColor
[3] == 0.0) {
500 #ifdef USE_OPTIMIZED_ACCUM
501 ctx
->IntegerAccumMode
= GL_TRUE
;
503 ctx
->IntegerAccumMode
= GL_FALSE
;
505 ctx
->IntegerAccumScaler
= 0.0; /* denotes empty accum buffer */
508 ctx
->IntegerAccumMode
= GL_FALSE
;