• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "imports.h"
32 #include "macros.h"
33 #include "state.h"
34 #include "mtypes.h"
35 #include "main/dispatch.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 void GLAPIENTRY
_mesa_Accum(GLenum op,GLfloat value)57 _mesa_Accum( GLenum op, GLfloat value )
58 {
59    GET_CURRENT_CONTEXT(ctx);
60    FLUSH_VERTICES(ctx, 0);
61 
62    switch (op) {
63    case GL_ADD:
64    case GL_MULT:
65    case GL_ACCUM:
66    case GL_LOAD:
67    case GL_RETURN:
68       /* OK */
69       break;
70    default:
71       _mesa_error(ctx, GL_INVALID_ENUM, "glAccum(op)");
72       return;
73    }
74 
75    if (ctx->DrawBuffer->Visual.haveAccumBuffer == 0) {
76       _mesa_error(ctx, GL_INVALID_OPERATION, "glAccum(no accum buffer)");
77       return;
78    }
79 
80    if (ctx->DrawBuffer != ctx->ReadBuffer) {
81       /* See GLX_SGI_make_current_read or WGL_ARB_make_current_read,
82        * or GL_EXT_framebuffer_blit.
83        */
84       _mesa_error(ctx, GL_INVALID_OPERATION,
85                   "glAccum(different read/draw buffers)");
86       return;
87    }
88 
89    if (ctx->NewState)
90       _mesa_update_state(ctx);
91 
92    if (ctx->DrawBuffer->_Status != GL_FRAMEBUFFER_COMPLETE_EXT) {
93       _mesa_error(ctx, GL_INVALID_FRAMEBUFFER_OPERATION_EXT,
94                   "glAccum(incomplete framebuffer)");
95       return;
96    }
97 
98    if (ctx->RasterDiscard)
99       return;
100 
101    if (ctx->RenderMode == GL_RENDER) {
102       _mesa_accum(ctx, op, value);
103    }
104 }
105 
106 
107 /**
108  * Clear the accumulation buffer by mapping the renderbuffer and
109  * writing the clear color to it.  Called by the driver's implementation
110  * of the glClear function.
111  */
112 void
_mesa_clear_accum_buffer(struct gl_context * ctx)113 _mesa_clear_accum_buffer(struct gl_context *ctx)
114 {
115    GLuint x, y, width, height;
116    GLubyte *accMap;
117    GLint accRowStride;
118    struct gl_renderbuffer *accRb;
119 
120    if (!ctx->DrawBuffer)
121       return;
122 
123    accRb = ctx->DrawBuffer->Attachment[BUFFER_ACCUM].Renderbuffer;
124    if (!accRb)
125       return;   /* missing accum buffer, not an error */
126 
127    /* bounds, with scissor */
128    x = ctx->DrawBuffer->_Xmin;
129    y = ctx->DrawBuffer->_Ymin;
130    width = ctx->DrawBuffer->_Xmax - ctx->DrawBuffer->_Xmin;
131    height = ctx->DrawBuffer->_Ymax - ctx->DrawBuffer->_Ymin;
132 
133    ctx->Driver.MapRenderbuffer(ctx, accRb, x, y, width, height,
134                                GL_MAP_WRITE_BIT, &accMap, &accRowStride);
135 
136    if (!accMap) {
137       _mesa_error(ctx, GL_OUT_OF_MEMORY, "glAccum");
138       return;
139    }
140 
141    if (accRb->Format == MESA_FORMAT_RGBA_SNORM16) {
142       const GLshort clearR = FLOAT_TO_SHORT(ctx->Accum.ClearColor[0]);
143       const GLshort clearG = FLOAT_TO_SHORT(ctx->Accum.ClearColor[1]);
144       const GLshort clearB = FLOAT_TO_SHORT(ctx->Accum.ClearColor[2]);
145       const GLshort clearA = FLOAT_TO_SHORT(ctx->Accum.ClearColor[3]);
146       GLuint i, j;
147 
148       for (j = 0; j < height; j++) {
149          GLshort *row = (GLshort *) accMap;
150 
151          for (i = 0; i < width; i++) {
152             row[i * 4 + 0] = clearR;
153             row[i * 4 + 1] = clearG;
154             row[i * 4 + 2] = clearB;
155             row[i * 4 + 3] = clearA;
156          }
157          accMap += accRowStride;
158       }
159    }
160    else {
161       /* other types someday? */
162       _mesa_warning(ctx, "unexpected accum buffer type");
163    }
164 
165    ctx->Driver.UnmapRenderbuffer(ctx, accRb);
166 }
167 
168 
169 /**
170  * if (bias)
171  *    Accum += value
172  * else
173  *    Accum *= value
174  */
175 static void
accum_scale_or_bias(struct gl_context * ctx,GLfloat value,GLint xpos,GLint ypos,GLint width,GLint height,GLboolean bias)176 accum_scale_or_bias(struct gl_context *ctx, GLfloat value,
177                     GLint xpos, GLint ypos, GLint width, GLint height,
178                     GLboolean bias)
179 {
180    struct gl_renderbuffer *accRb =
181       ctx->DrawBuffer->Attachment[BUFFER_ACCUM].Renderbuffer;
182    GLubyte *accMap;
183    GLint accRowStride;
184 
185    assert(accRb);
186 
187    ctx->Driver.MapRenderbuffer(ctx, accRb, xpos, ypos, width, height,
188                                GL_MAP_READ_BIT | GL_MAP_WRITE_BIT,
189                                &accMap, &accRowStride);
190 
191    if (!accMap) {
192       _mesa_error(ctx, GL_OUT_OF_MEMORY, "glAccum");
193       return;
194    }
195 
196    if (accRb->Format == MESA_FORMAT_RGBA_SNORM16) {
197       const GLshort incr = (GLshort) (value * 32767.0f);
198       GLint i, j;
199       if (bias) {
200          for (j = 0; j < height; j++) {
201             GLshort *acc = (GLshort *) accMap;
202             for (i = 0; i < 4 * width; i++) {
203                acc[i] += incr;
204             }
205             accMap += accRowStride;
206          }
207       }
208       else {
209          /* scale */
210          for (j = 0; j < height; j++) {
211             GLshort *acc = (GLshort *) accMap;
212             for (i = 0; i < 4 * width; i++) {
213                acc[i] = (GLshort) (acc[i] * value);
214             }
215             accMap += accRowStride;
216          }
217       }
218    }
219    else {
220       /* other types someday? */
221    }
222 
223    ctx->Driver.UnmapRenderbuffer(ctx, accRb);
224 }
225 
226 
227 /**
228  * if (load)
229  *    Accum = ColorBuf * value
230  * else
231  *    Accum += ColorBuf * value
232  */
233 static void
accum_or_load(struct gl_context * ctx,GLfloat value,GLint xpos,GLint ypos,GLint width,GLint height,GLboolean load)234 accum_or_load(struct gl_context *ctx, GLfloat value,
235               GLint xpos, GLint ypos, GLint width, GLint height,
236               GLboolean load)
237 {
238    struct gl_renderbuffer *accRb =
239       ctx->DrawBuffer->Attachment[BUFFER_ACCUM].Renderbuffer;
240    struct gl_renderbuffer *colorRb = ctx->ReadBuffer->_ColorReadBuffer;
241    GLubyte *accMap, *colorMap;
242    GLint accRowStride, colorRowStride;
243    GLbitfield mappingFlags;
244 
245    if (!colorRb) {
246       /* no read buffer - OK */
247       return;
248    }
249 
250    assert(accRb);
251 
252    mappingFlags = GL_MAP_WRITE_BIT;
253    if (!load) /* if we're accumulating */
254       mappingFlags |= GL_MAP_READ_BIT;
255 
256    /* Map accum buffer */
257    ctx->Driver.MapRenderbuffer(ctx, accRb, xpos, ypos, width, height,
258                                mappingFlags, &accMap, &accRowStride);
259    if (!accMap) {
260       _mesa_error(ctx, GL_OUT_OF_MEMORY, "glAccum");
261       return;
262    }
263 
264    /* Map color buffer */
265    ctx->Driver.MapRenderbuffer(ctx, colorRb, xpos, ypos, width, height,
266                                GL_MAP_READ_BIT,
267                                &colorMap, &colorRowStride);
268    if (!colorMap) {
269       ctx->Driver.UnmapRenderbuffer(ctx, accRb);
270       _mesa_error(ctx, GL_OUT_OF_MEMORY, "glAccum");
271       return;
272    }
273 
274    if (accRb->Format == MESA_FORMAT_RGBA_SNORM16) {
275       const GLfloat scale = value * 32767.0f;
276       GLint i, j;
277       GLfloat (*rgba)[4];
278 
279       rgba = malloc(width * 4 * sizeof(GLfloat));
280       if (rgba) {
281          for (j = 0; j < height; j++) {
282             GLshort *acc = (GLshort *) accMap;
283 
284             /* read colors from source color buffer */
285             _mesa_unpack_rgba_row(colorRb->Format, width, colorMap, rgba);
286 
287             if (load) {
288                for (i = 0; i < width; i++) {
289                   acc[i * 4 + 0] = (GLshort) (rgba[i][RCOMP] * scale);
290                   acc[i * 4 + 1] = (GLshort) (rgba[i][GCOMP] * scale);
291                   acc[i * 4 + 2] = (GLshort) (rgba[i][BCOMP] * scale);
292                   acc[i * 4 + 3] = (GLshort) (rgba[i][ACOMP] * scale);
293                }
294             }
295             else {
296                /* accumulate */
297                for (i = 0; i < width; i++) {
298                   acc[i * 4 + 0] += (GLshort) (rgba[i][RCOMP] * scale);
299                   acc[i * 4 + 1] += (GLshort) (rgba[i][GCOMP] * scale);
300                   acc[i * 4 + 2] += (GLshort) (rgba[i][BCOMP] * scale);
301                   acc[i * 4 + 3] += (GLshort) (rgba[i][ACOMP] * scale);
302                }
303             }
304 
305             colorMap += colorRowStride;
306             accMap += accRowStride;
307          }
308 
309          free(rgba);
310       }
311       else {
312          _mesa_error(ctx, GL_OUT_OF_MEMORY, "glAccum");
313       }
314    }
315    else {
316       /* other types someday? */
317    }
318 
319    ctx->Driver.UnmapRenderbuffer(ctx, accRb);
320    ctx->Driver.UnmapRenderbuffer(ctx, colorRb);
321 }
322 
323 
324 /**
325  * ColorBuffer = Accum * value
326  */
327 static void
accum_return(struct gl_context * ctx,GLfloat value,GLint xpos,GLint ypos,GLint width,GLint height)328 accum_return(struct gl_context *ctx, GLfloat value,
329              GLint xpos, GLint ypos, GLint width, GLint height)
330 {
331    struct gl_framebuffer *fb = ctx->DrawBuffer;
332    struct gl_renderbuffer *accRb = fb->Attachment[BUFFER_ACCUM].Renderbuffer;
333    GLubyte *accMap, *colorMap;
334    GLint accRowStride, colorRowStride;
335    GLuint buffer;
336 
337    /* Map accum buffer */
338    ctx->Driver.MapRenderbuffer(ctx, accRb, xpos, ypos, width, height,
339                                GL_MAP_READ_BIT,
340                                &accMap, &accRowStride);
341    if (!accMap) {
342       _mesa_error(ctx, GL_OUT_OF_MEMORY, "glAccum");
343       return;
344    }
345 
346    /* Loop over destination buffers */
347    for (buffer = 0; buffer < fb->_NumColorDrawBuffers; buffer++) {
348       struct gl_renderbuffer *colorRb = fb->_ColorDrawBuffers[buffer];
349       const GLboolean masking = (!ctx->Color.ColorMask[buffer][RCOMP] ||
350                                  !ctx->Color.ColorMask[buffer][GCOMP] ||
351                                  !ctx->Color.ColorMask[buffer][BCOMP] ||
352                                  !ctx->Color.ColorMask[buffer][ACOMP]);
353       GLbitfield mappingFlags = GL_MAP_WRITE_BIT;
354 
355       if (masking)
356          mappingFlags |= GL_MAP_READ_BIT;
357 
358       /* Map color buffer */
359       ctx->Driver.MapRenderbuffer(ctx, colorRb, xpos, ypos, width, height,
360                                   mappingFlags, &colorMap, &colorRowStride);
361       if (!colorMap) {
362          _mesa_error(ctx, GL_OUT_OF_MEMORY, "glAccum");
363          continue;
364       }
365 
366       if (accRb->Format == MESA_FORMAT_RGBA_SNORM16) {
367          const GLfloat scale = value / 32767.0f;
368          GLint i, j;
369          GLfloat (*rgba)[4], (*dest)[4];
370 
371          rgba = malloc(width * 4 * sizeof(GLfloat));
372          dest = malloc(width * 4 * sizeof(GLfloat));
373 
374          if (rgba && dest) {
375             for (j = 0; j < height; j++) {
376                GLshort *acc = (GLshort *) accMap;
377 
378                for (i = 0; i < width; i++) {
379                   rgba[i][0] = acc[i * 4 + 0] * scale;
380                   rgba[i][1] = acc[i * 4 + 1] * scale;
381                   rgba[i][2] = acc[i * 4 + 2] * scale;
382                   rgba[i][3] = acc[i * 4 + 3] * scale;
383                }
384 
385                if (masking) {
386 
387                   /* get existing colors from dest buffer */
388                   _mesa_unpack_rgba_row(colorRb->Format, width, colorMap, dest);
389 
390                   /* use the dest colors where mask[channel] = 0 */
391                   if (ctx->Color.ColorMask[buffer][RCOMP] == 0) {
392                      for (i = 0; i < width; i++)
393                         rgba[i][RCOMP] = dest[i][RCOMP];
394                   }
395                   if (ctx->Color.ColorMask[buffer][GCOMP] == 0) {
396                      for (i = 0; i < width; i++)
397                         rgba[i][GCOMP] = dest[i][GCOMP];
398                   }
399                   if (ctx->Color.ColorMask[buffer][BCOMP] == 0) {
400                      for (i = 0; i < width; i++)
401                         rgba[i][BCOMP] = dest[i][BCOMP];
402                   }
403                   if (ctx->Color.ColorMask[buffer][ACOMP] == 0) {
404                      for (i = 0; i < width; i++)
405                         rgba[i][ACOMP] = dest[i][ACOMP];
406                   }
407                }
408 
409                _mesa_pack_float_rgba_row(colorRb->Format, width,
410                                          (const GLfloat (*)[4]) rgba, colorMap);
411 
412                accMap += accRowStride;
413                colorMap += colorRowStride;
414             }
415          }
416          else {
417             _mesa_error(ctx, GL_OUT_OF_MEMORY, "glAccum");
418          }
419          free(rgba);
420          free(dest);
421       }
422       else {
423          /* other types someday? */
424       }
425 
426       ctx->Driver.UnmapRenderbuffer(ctx, colorRb);
427    }
428 
429    ctx->Driver.UnmapRenderbuffer(ctx, accRb);
430 }
431 
432 
433 
434 /**
435  * Software fallback for glAccum.  A hardware driver that supports
436  * signed 16-bit color channels could implement hardware accumulation
437  * operations, but no driver does so at this time.
438  */
439 void
_mesa_accum(struct gl_context * ctx,GLenum op,GLfloat value)440 _mesa_accum(struct gl_context *ctx, GLenum op, GLfloat value)
441 {
442    GLint xpos, ypos, width, height;
443 
444    if (!ctx->DrawBuffer->Attachment[BUFFER_ACCUM].Renderbuffer) {
445       _mesa_warning(ctx, "Calling glAccum() without an accumulation buffer");
446       return;
447    }
448 
449    if (!_mesa_check_conditional_render(ctx))
450       return;
451 
452    xpos = ctx->DrawBuffer->_Xmin;
453    ypos = ctx->DrawBuffer->_Ymin;
454    width =  ctx->DrawBuffer->_Xmax - ctx->DrawBuffer->_Xmin;
455    height = ctx->DrawBuffer->_Ymax - ctx->DrawBuffer->_Ymin;
456 
457    switch (op) {
458    case GL_ADD:
459       if (value != 0.0F) {
460          accum_scale_or_bias(ctx, value, xpos, ypos, width, height, GL_TRUE);
461       }
462       break;
463    case GL_MULT:
464       if (value != 1.0F) {
465          accum_scale_or_bias(ctx, value, xpos, ypos, width, height, GL_FALSE);
466       }
467       break;
468    case GL_ACCUM:
469       if (value != 0.0F) {
470          accum_or_load(ctx, value, xpos, ypos, width, height, GL_FALSE);
471       }
472       break;
473    case GL_LOAD:
474       accum_or_load(ctx, value, xpos, ypos, width, height, GL_TRUE);
475       break;
476    case GL_RETURN:
477       accum_return(ctx, value, xpos, ypos, width, height);
478       break;
479    default:
480       _mesa_problem(ctx, "invalid mode in _mesa_accum()");
481       break;
482    }
483 }
484 
485 
486 void
_mesa_init_accum(struct gl_context * ctx)487 _mesa_init_accum( struct gl_context *ctx )
488 {
489    /* Accumulate buffer group */
490    ASSIGN_4V( ctx->Accum.ClearColor, 0.0, 0.0, 0.0, 0.0 );
491 }
492