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