1 /*
2  * Mesa 3-D graphics library
3  * Version:  6.5
4  *
5  * Copyright (C) 1999-2005  Brian Paul   All Rights Reserved.
6  *
7  * Permission is hereby granted, free of charge, to any person obtaining a
8  * copy of this software and associated documentation files (the "Software"),
9  * to deal in the Software without restriction, including without limitation
10  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
11  * and/or sell copies of the Software, and to permit persons to whom the
12  * Software is furnished to do so, subject to the following conditions:
13  *
14  * The above copyright notice and this permission notice shall be included
15  * in all copies or substantial portions of the Software.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
18  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
20  * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
21  * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23  */
24 
25 #include "glheader.h"
26 #include "accum.h"
27 #include "condrender.h"
28 #include "context.h"
29 #include "format_unpack.h"
30 #include "format_pack.h"
31 #include "imports.h"
32 #include "macros.h"
33 #include "mfeatures.h"
34 #include "state.h"
35 #include "mtypes.h"
36 #include "main/dispatch.h"
37 
38 
39 #if FEATURE_accum
40 
41 
42 void GLAPIENTRY
_mesa_ClearAccum(GLfloat red,GLfloat green,GLfloat blue,GLfloat alpha)43 _mesa_ClearAccum( GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha )
44 {
45    GLfloat tmp[4];
46    GET_CURRENT_CONTEXT(ctx);
47    ASSERT_OUTSIDE_BEGIN_END(ctx);
48 
49    tmp[0] = CLAMP( red,   -1.0F, 1.0F );
50    tmp[1] = CLAMP( green, -1.0F, 1.0F );
51    tmp[2] = CLAMP( blue,  -1.0F, 1.0F );
52    tmp[3] = CLAMP( alpha, -1.0F, 1.0F );
53 
54    if (TEST_EQ_4V(tmp, ctx->Accum.ClearColor))
55       return;
56 
57    COPY_4FV( ctx->Accum.ClearColor, tmp );
58 }
59 
60 
61 static void GLAPIENTRY
_mesa_Accum(GLenum op,GLfloat value)62 _mesa_Accum( GLenum op, GLfloat value )
63 {
64    GET_CURRENT_CONTEXT(ctx);
65    ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx);
66 
67    switch (op) {
68    case GL_ADD:
69    case GL_MULT:
70    case GL_ACCUM:
71    case GL_LOAD:
72    case GL_RETURN:
73       /* OK */
74       break;
75    default:
76       _mesa_error(ctx, GL_INVALID_ENUM, "glAccum(op)");
77       return;
78    }
79 
80    if (ctx->DrawBuffer->Visual.haveAccumBuffer == 0) {
81       _mesa_error(ctx, GL_INVALID_OPERATION, "glAccum(no accum buffer)");
82       return;
83    }
84 
85    if (ctx->DrawBuffer != ctx->ReadBuffer) {
86       /* See GLX_SGI_make_current_read or WGL_ARB_make_current_read,
87        * or GL_EXT_framebuffer_blit.
88        */
89       _mesa_error(ctx, GL_INVALID_OPERATION,
90                   "glAccum(different read/draw buffers)");
91       return;
92    }
93 
94    if (ctx->NewState)
95       _mesa_update_state(ctx);
96 
97    if (ctx->DrawBuffer->_Status != GL_FRAMEBUFFER_COMPLETE_EXT) {
98       _mesa_error(ctx, GL_INVALID_FRAMEBUFFER_OPERATION_EXT,
99                   "glAccum(incomplete framebuffer)");
100       return;
101    }
102 
103    if (ctx->RasterDiscard)
104       return;
105 
106    if (ctx->RenderMode == GL_RENDER) {
107       _mesa_accum(ctx, op, value);
108    }
109 }
110 
111 
112 void
_mesa_init_accum_dispatch(struct _glapi_table * disp)113 _mesa_init_accum_dispatch(struct _glapi_table *disp)
114 {
115    SET_Accum(disp, _mesa_Accum);
116    SET_ClearAccum(disp, _mesa_ClearAccum);
117 }
118 
119 
120 /**
121  * Clear the accumulation buffer by mapping the renderbuffer and
122  * writing the clear color to it.  Called by the driver's implementation
123  * of the glClear function.
124  */
125 void
_mesa_clear_accum_buffer(struct gl_context * ctx)126 _mesa_clear_accum_buffer(struct gl_context *ctx)
127 {
128    GLuint x, y, width, height;
129    GLubyte *accMap;
130    GLint accRowStride;
131    struct gl_renderbuffer *accRb;
132 
133    if (!ctx->DrawBuffer)
134       return;
135 
136    accRb = ctx->DrawBuffer->Attachment[BUFFER_ACCUM].Renderbuffer;
137    if (!accRb)
138       return;   /* missing accum buffer, not an error */
139 
140    /* bounds, with scissor */
141    x = ctx->DrawBuffer->_Xmin;
142    y = ctx->DrawBuffer->_Ymin;
143    width = ctx->DrawBuffer->_Xmax - ctx->DrawBuffer->_Xmin;
144    height = ctx->DrawBuffer->_Ymax - ctx->DrawBuffer->_Ymin;
145 
146    ctx->Driver.MapRenderbuffer(ctx, accRb, x, y, width, height,
147                                GL_MAP_WRITE_BIT, &accMap, &accRowStride);
148 
149    if (!accMap) {
150       _mesa_error(ctx, GL_OUT_OF_MEMORY, "glAccum");
151       return;
152    }
153 
154    if (accRb->Format == MESA_FORMAT_SIGNED_RGBA_16) {
155       const GLshort clearR = FLOAT_TO_SHORT(ctx->Accum.ClearColor[0]);
156       const GLshort clearG = FLOAT_TO_SHORT(ctx->Accum.ClearColor[1]);
157       const GLshort clearB = FLOAT_TO_SHORT(ctx->Accum.ClearColor[2]);
158       const GLshort clearA = FLOAT_TO_SHORT(ctx->Accum.ClearColor[3]);
159       GLuint i, j;
160 
161       for (j = 0; j < height; j++) {
162          GLshort *row = (GLshort *) accMap;
163 
164          for (i = 0; i < width; i++) {
165             row[i * 4 + 0] = clearR;
166             row[i * 4 + 1] = clearG;
167             row[i * 4 + 2] = clearB;
168             row[i * 4 + 3] = clearA;
169          }
170          accMap += accRowStride;
171       }
172    }
173    else {
174       /* other types someday? */
175       _mesa_warning(ctx, "unexpected accum buffer type");
176    }
177 
178    ctx->Driver.UnmapRenderbuffer(ctx, accRb);
179 }
180 
181 
182 /**
183  * if (bias)
184  *    Accum += value
185  * else
186  *    Accum *= value
187  */
188 static void
accum_scale_or_bias(struct gl_context * ctx,GLfloat value,GLint xpos,GLint ypos,GLint width,GLint height,GLboolean bias)189 accum_scale_or_bias(struct gl_context *ctx, GLfloat value,
190                     GLint xpos, GLint ypos, GLint width, GLint height,
191                     GLboolean bias)
192 {
193    struct gl_renderbuffer *accRb =
194       ctx->DrawBuffer->Attachment[BUFFER_ACCUM].Renderbuffer;
195    GLubyte *accMap;
196    GLint accRowStride;
197 
198    assert(accRb);
199 
200    ctx->Driver.MapRenderbuffer(ctx, accRb, xpos, ypos, width, height,
201                                GL_MAP_READ_BIT | GL_MAP_WRITE_BIT,
202                                &accMap, &accRowStride);
203 
204    if (!accMap) {
205       _mesa_error(ctx, GL_OUT_OF_MEMORY, "glAccum");
206       return;
207    }
208 
209    if (accRb->Format == MESA_FORMAT_SIGNED_RGBA_16) {
210       const GLshort incr = (GLshort) (value * 32767.0f);
211       GLuint i, j;
212       if (bias) {
213          for (j = 0; j < height; j++) {
214             GLshort *acc = (GLshort *) accMap;
215             for (i = 0; i < 4 * width; i++) {
216                acc[i] += incr;
217             }
218             accMap += accRowStride;
219          }
220       }
221       else {
222          /* scale */
223          for (j = 0; j < height; j++) {
224             GLshort *acc = (GLshort *) accMap;
225             for (i = 0; i < 4 * width; i++) {
226                acc[i] = (GLshort) (acc[i] * value);
227             }
228             accMap += accRowStride;
229          }
230       }
231    }
232    else {
233       /* other types someday? */
234    }
235 
236    ctx->Driver.UnmapRenderbuffer(ctx, accRb);
237 }
238 
239 
240 /**
241  * if (load)
242  *    Accum = ColorBuf * value
243  * else
244  *    Accum += ColorBuf * value
245  */
246 static void
accum_or_load(struct gl_context * ctx,GLfloat value,GLint xpos,GLint ypos,GLint width,GLint height,GLboolean load)247 accum_or_load(struct gl_context *ctx, GLfloat value,
248               GLint xpos, GLint ypos, GLint width, GLint height,
249               GLboolean load)
250 {
251    struct gl_renderbuffer *accRb =
252       ctx->DrawBuffer->Attachment[BUFFER_ACCUM].Renderbuffer;
253    struct gl_renderbuffer *colorRb = ctx->ReadBuffer->_ColorReadBuffer;
254    GLubyte *accMap, *colorMap;
255    GLint accRowStride, colorRowStride;
256    GLbitfield mappingFlags;
257 
258    if (!colorRb) {
259       /* no read buffer - OK */
260       return;
261    }
262 
263    assert(accRb);
264 
265    mappingFlags = GL_MAP_WRITE_BIT;
266    if (!load) /* if we're accumulating */
267       mappingFlags |= GL_MAP_READ_BIT;
268 
269    /* Map accum buffer */
270    ctx->Driver.MapRenderbuffer(ctx, accRb, xpos, ypos, width, height,
271                                mappingFlags, &accMap, &accRowStride);
272    if (!accMap) {
273       _mesa_error(ctx, GL_OUT_OF_MEMORY, "glAccum");
274       return;
275    }
276 
277    /* Map color buffer */
278    ctx->Driver.MapRenderbuffer(ctx, colorRb, xpos, ypos, width, height,
279                                GL_MAP_READ_BIT,
280                                &colorMap, &colorRowStride);
281    if (!colorMap) {
282       ctx->Driver.UnmapRenderbuffer(ctx, accRb);
283       _mesa_error(ctx, GL_OUT_OF_MEMORY, "glAccum");
284       return;
285    }
286 
287    if (accRb->Format == MESA_FORMAT_SIGNED_RGBA_16) {
288       const GLfloat scale = value * 32767.0f;
289       GLuint i, j;
290       GLfloat (*rgba)[4];
291 
292       rgba = (GLfloat (*)[4]) malloc(width * 4 * sizeof(GLfloat));
293       if (rgba) {
294          for (j = 0; j < height; j++) {
295             GLshort *acc = (GLshort *) accMap;
296 
297             /* read colors from source color buffer */
298             _mesa_unpack_rgba_row(colorRb->Format, width, colorMap, rgba);
299 
300             if (load) {
301                for (i = 0; i < width; i++) {
302                   acc[i * 4 + 0] = (GLshort) (rgba[i][RCOMP] * scale);
303                   acc[i * 4 + 1] = (GLshort) (rgba[i][GCOMP] * scale);
304                   acc[i * 4 + 2] = (GLshort) (rgba[i][BCOMP] * scale);
305                   acc[i * 4 + 3] = (GLshort) (rgba[i][ACOMP] * scale);
306                }
307             }
308             else {
309                /* accumulate */
310                for (i = 0; i < width; i++) {
311                   acc[i * 4 + 0] += (GLshort) (rgba[i][RCOMP] * scale);
312                   acc[i * 4 + 1] += (GLshort) (rgba[i][GCOMP] * scale);
313                   acc[i * 4 + 2] += (GLshort) (rgba[i][BCOMP] * scale);
314                   acc[i * 4 + 3] += (GLshort) (rgba[i][ACOMP] * scale);
315                }
316             }
317 
318             colorMap += colorRowStride;
319             accMap += accRowStride;
320          }
321 
322          free(rgba);
323       }
324       else {
325          _mesa_error(ctx, GL_OUT_OF_MEMORY, "glAccum");
326       }
327    }
328    else {
329       /* other types someday? */
330    }
331 
332    ctx->Driver.UnmapRenderbuffer(ctx, accRb);
333    ctx->Driver.UnmapRenderbuffer(ctx, colorRb);
334 }
335 
336 
337 /**
338  * ColorBuffer = Accum * value
339  */
340 static void
accum_return(struct gl_context * ctx,GLfloat value,GLint xpos,GLint ypos,GLint width,GLint height)341 accum_return(struct gl_context *ctx, GLfloat value,
342              GLint xpos, GLint ypos, GLint width, GLint height)
343 {
344    struct gl_framebuffer *fb = ctx->DrawBuffer;
345    struct gl_renderbuffer *accRb = fb->Attachment[BUFFER_ACCUM].Renderbuffer;
346    GLubyte *accMap, *colorMap;
347    GLint accRowStride, colorRowStride;
348    GLuint buffer;
349 
350    /* Map accum buffer */
351    ctx->Driver.MapRenderbuffer(ctx, accRb, xpos, ypos, width, height,
352                                GL_MAP_READ_BIT,
353                                &accMap, &accRowStride);
354    if (!accMap) {
355       _mesa_error(ctx, GL_OUT_OF_MEMORY, "glAccum");
356       return;
357    }
358 
359    /* Loop over destination buffers */
360    for (buffer = 0; buffer < fb->_NumColorDrawBuffers; buffer++) {
361       struct gl_renderbuffer *colorRb = fb->_ColorDrawBuffers[buffer];
362       const GLboolean masking = (!ctx->Color.ColorMask[buffer][RCOMP] ||
363                                  !ctx->Color.ColorMask[buffer][GCOMP] ||
364                                  !ctx->Color.ColorMask[buffer][BCOMP] ||
365                                  !ctx->Color.ColorMask[buffer][ACOMP]);
366       GLbitfield mappingFlags = GL_MAP_WRITE_BIT;
367 
368       if (masking)
369          mappingFlags |= GL_MAP_READ_BIT;
370 
371       /* Map color buffer */
372       ctx->Driver.MapRenderbuffer(ctx, colorRb, xpos, ypos, width, height,
373                                   mappingFlags, &colorMap, &colorRowStride);
374       if (!colorMap) {
375          _mesa_error(ctx, GL_OUT_OF_MEMORY, "glAccum");
376          continue;
377       }
378 
379       if (accRb->Format == MESA_FORMAT_SIGNED_RGBA_16) {
380          const GLfloat scale = value / 32767.0f;
381          GLint i, j;
382          GLfloat (*rgba)[4], (*dest)[4];
383 
384          rgba = (GLfloat (*)[4]) malloc(width * 4 * sizeof(GLfloat));
385          dest = (GLfloat (*)[4]) malloc(width * 4 * sizeof(GLfloat));
386 
387          if (rgba && dest) {
388             for (j = 0; j < height; j++) {
389                GLshort *acc = (GLshort *) accMap;
390 
391                for (i = 0; i < width; i++) {
392                   rgba[i][0] = acc[i * 4 + 0] * scale;
393                   rgba[i][1] = acc[i * 4 + 1] * scale;
394                   rgba[i][2] = acc[i * 4 + 2] * scale;
395                   rgba[i][3] = acc[i * 4 + 3] * scale;
396                }
397 
398                if (masking) {
399 
400                   /* get existing colors from dest buffer */
401                   _mesa_unpack_rgba_row(colorRb->Format, width, colorMap, dest);
402 
403                   /* use the dest colors where mask[channel] = 0 */
404                   if (ctx->Color.ColorMask[buffer][RCOMP] == 0) {
405                      for (i = 0; i < width; i++)
406                         rgba[i][RCOMP] = dest[i][RCOMP];
407                   }
408                   if (ctx->Color.ColorMask[buffer][GCOMP] == 0) {
409                      for (i = 0; i < width; i++)
410                         rgba[i][GCOMP] = dest[i][GCOMP];
411                   }
412                   if (ctx->Color.ColorMask[buffer][BCOMP] == 0) {
413                      for (i = 0; i < width; i++)
414                         rgba[i][BCOMP] = dest[i][BCOMP];
415                   }
416                   if (ctx->Color.ColorMask[buffer][ACOMP] == 0) {
417                      for (i = 0; i < width; i++)
418                         rgba[i][ACOMP] = dest[i][ACOMP];
419                   }
420                }
421 
422                _mesa_pack_float_rgba_row(colorRb->Format, width,
423                                          (const GLfloat (*)[4]) rgba, colorMap);
424 
425                accMap += accRowStride;
426                colorMap += colorRowStride;
427             }
428          }
429          else {
430             _mesa_error(ctx, GL_OUT_OF_MEMORY, "glAccum");
431          }
432          free(rgba);
433          free(dest);
434       }
435       else {
436          /* other types someday? */
437       }
438 
439       ctx->Driver.UnmapRenderbuffer(ctx, colorRb);
440    }
441 
442    ctx->Driver.UnmapRenderbuffer(ctx, accRb);
443 }
444 
445 
446 
447 /**
448  * Software fallback for glAccum.  A hardware driver that supports
449  * signed 16-bit color channels could implement hardware accumulation
450  * operations, but no driver does so at this time.
451  */
452 void
_mesa_accum(struct gl_context * ctx,GLenum op,GLfloat value)453 _mesa_accum(struct gl_context *ctx, GLenum op, GLfloat value)
454 {
455    GLint xpos, ypos, width, height;
456 
457    if (!ctx->DrawBuffer->Attachment[BUFFER_ACCUM].Renderbuffer) {
458       _mesa_warning(ctx, "Calling glAccum() without an accumulation buffer");
459       return;
460    }
461 
462    if (!_mesa_check_conditional_render(ctx))
463       return;
464 
465    xpos = ctx->DrawBuffer->_Xmin;
466    ypos = ctx->DrawBuffer->_Ymin;
467    width =  ctx->DrawBuffer->_Xmax - ctx->DrawBuffer->_Xmin;
468    height = ctx->DrawBuffer->_Ymax - ctx->DrawBuffer->_Ymin;
469 
470    switch (op) {
471    case GL_ADD:
472       if (value != 0.0F) {
473          accum_scale_or_bias(ctx, value, xpos, ypos, width, height, GL_TRUE);
474       }
475       break;
476    case GL_MULT:
477       if (value != 1.0F) {
478          accum_scale_or_bias(ctx, value, xpos, ypos, width, height, GL_FALSE);
479       }
480       break;
481    case GL_ACCUM:
482       if (value != 0.0F) {
483          accum_or_load(ctx, value, xpos, ypos, width, height, GL_FALSE);
484       }
485       break;
486    case GL_LOAD:
487       accum_or_load(ctx, value, xpos, ypos, width, height, GL_TRUE);
488       break;
489    case GL_RETURN:
490       accum_return(ctx, value, xpos, ypos, width, height);
491       break;
492    default:
493       _mesa_problem(ctx, "invalid mode in _mesa_accum()");
494       break;
495    }
496 }
497 
498 
499 #endif /* FEATURE_accum */
500 
501 
502 void
_mesa_init_accum(struct gl_context * ctx)503 _mesa_init_accum( struct gl_context *ctx )
504 {
505    /* Accumulate buffer group */
506    ASSIGN_4V( ctx->Accum.ClearColor, 0.0, 0.0, 0.0, 0.0 );
507 }
508