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
42void GLAPIENTRY
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
61static void GLAPIENTRY
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
112void
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 */
125void
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 */
188static void
189accum_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 */
246static void
247accum_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 */
340static void
341accum_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 */
452void
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
502void
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