convolve.c revision cdc920a0
1/*
2 * Mesa 3-D graphics library
3 * Version:  6.5.2
4 *
5 * Copyright (C) 1999-2006  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
26/*
27 * Image convolution functions.
28 *
29 * Notes: filter kernel elements are indexed by <n> and <m> as in
30 * the GL spec.
31 */
32
33
34#include "glheader.h"
35#include "bufferobj.h"
36#include "colormac.h"
37#include "convolve.h"
38#include "context.h"
39#include "image.h"
40#include "mtypes.h"
41#include "state.h"
42#include "main/dispatch.h"
43
44
45#if FEATURE_convolve
46
47
48/*
49 * Given an internalFormat token passed to glConvolutionFilter
50 * or glSeparableFilter, return the corresponding base format.
51 * Return -1 if invalid token.
52 */
53static GLint
54base_filter_format( GLenum format )
55{
56   switch (format) {
57      case GL_ALPHA:
58      case GL_ALPHA4:
59      case GL_ALPHA8:
60      case GL_ALPHA12:
61      case GL_ALPHA16:
62         return GL_ALPHA;
63      case GL_LUMINANCE:
64      case GL_LUMINANCE4:
65      case GL_LUMINANCE8:
66      case GL_LUMINANCE12:
67      case GL_LUMINANCE16:
68         return GL_LUMINANCE;
69      case GL_LUMINANCE_ALPHA:
70      case GL_LUMINANCE4_ALPHA4:
71      case GL_LUMINANCE6_ALPHA2:
72      case GL_LUMINANCE8_ALPHA8:
73      case GL_LUMINANCE12_ALPHA4:
74      case GL_LUMINANCE12_ALPHA12:
75      case GL_LUMINANCE16_ALPHA16:
76         return GL_LUMINANCE_ALPHA;
77      case GL_INTENSITY:
78      case GL_INTENSITY4:
79      case GL_INTENSITY8:
80      case GL_INTENSITY12:
81      case GL_INTENSITY16:
82         return GL_INTENSITY;
83      case GL_RGB:
84      case GL_R3_G3_B2:
85      case GL_RGB4:
86      case GL_RGB5:
87      case GL_RGB8:
88      case GL_RGB10:
89      case GL_RGB12:
90      case GL_RGB16:
91         return GL_RGB;
92      case 4:
93      case GL_RGBA:
94      case GL_RGBA2:
95      case GL_RGBA4:
96      case GL_RGB5_A1:
97      case GL_RGBA8:
98      case GL_RGB10_A2:
99      case GL_RGBA12:
100      case GL_RGBA16:
101         return GL_RGBA;
102      default:
103         return -1;  /* error */
104   }
105}
106
107
108void GLAPIENTRY
109_mesa_ConvolutionFilter1D(GLenum target, GLenum internalFormat, GLsizei width, GLenum format, GLenum type, const GLvoid *image)
110{
111   GLint baseFormat;
112   GET_CURRENT_CONTEXT(ctx);
113   ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx);
114
115   if (target != GL_CONVOLUTION_1D) {
116      _mesa_error(ctx, GL_INVALID_ENUM, "glConvolutionFilter1D(target)");
117      return;
118   }
119
120   baseFormat = base_filter_format(internalFormat);
121   if (baseFormat < 0 || baseFormat == GL_COLOR_INDEX) {
122      _mesa_error(ctx, GL_INVALID_ENUM, "glConvolutionFilter1D(internalFormat)");
123      return;
124   }
125
126   if (width < 0 || width > MAX_CONVOLUTION_WIDTH) {
127      _mesa_error(ctx, GL_INVALID_VALUE, "glConvolutionFilter1D(width)");
128      return;
129   }
130
131   if (!_mesa_is_legal_format_and_type(ctx, format, type)) {
132      _mesa_error(ctx, GL_INVALID_OPERATION, "glConvolutionFilter1D(format or type)");
133      return;
134   }
135
136   if (format == GL_COLOR_INDEX ||
137       format == GL_STENCIL_INDEX ||
138       format == GL_DEPTH_COMPONENT ||
139       format == GL_INTENSITY ||
140       type == GL_BITMAP) {
141      _mesa_error(ctx, GL_INVALID_ENUM, "glConvolutionFilter1D(format or type)");
142      return;
143   }
144
145   ctx->Convolution1D.Format = format;
146   ctx->Convolution1D.InternalFormat = internalFormat;
147   ctx->Convolution1D.Width = width;
148   ctx->Convolution1D.Height = 1;
149
150   image = _mesa_map_validate_pbo_source(ctx,
151                                        1, &ctx->Unpack, width, 1, 1,
152                                        format, type, image,
153                                        "glConvolutionFilter1D");
154   if (!image)
155      return;
156
157   _mesa_unpack_color_span_float(ctx, width, GL_RGBA,
158                                 ctx->Convolution1D.Filter,
159                                 format, type, image, &ctx->Unpack,
160                                 0); /* transferOps */
161
162   _mesa_unmap_pbo_source(ctx, &ctx->Unpack);
163
164   _mesa_scale_and_bias_rgba(width,
165                             (GLfloat (*)[4]) ctx->Convolution1D.Filter,
166                             ctx->Pixel.ConvolutionFilterScale[0][0],
167                             ctx->Pixel.ConvolutionFilterScale[0][1],
168                             ctx->Pixel.ConvolutionFilterScale[0][2],
169                             ctx->Pixel.ConvolutionFilterScale[0][3],
170                             ctx->Pixel.ConvolutionFilterBias[0][0],
171                             ctx->Pixel.ConvolutionFilterBias[0][1],
172                             ctx->Pixel.ConvolutionFilterBias[0][2],
173                             ctx->Pixel.ConvolutionFilterBias[0][3]);
174
175   ctx->NewState |= _NEW_PIXEL;
176}
177
178
179void GLAPIENTRY
180_mesa_ConvolutionFilter2D(GLenum target, GLenum internalFormat, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *image)
181{
182   GLint baseFormat;
183   GLint i;
184   GET_CURRENT_CONTEXT(ctx);
185   ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx);
186
187   if (target != GL_CONVOLUTION_2D) {
188      _mesa_error(ctx, GL_INVALID_ENUM, "glConvolutionFilter2D(target)");
189      return;
190   }
191
192   baseFormat = base_filter_format(internalFormat);
193   if (baseFormat < 0 || baseFormat == GL_COLOR_INDEX) {
194      _mesa_error(ctx, GL_INVALID_ENUM, "glConvolutionFilter2D(internalFormat)");
195      return;
196   }
197
198   if (width < 0 || width > MAX_CONVOLUTION_WIDTH) {
199      _mesa_error(ctx, GL_INVALID_VALUE, "glConvolutionFilter2D(width)");
200      return;
201   }
202   if (height < 0 || height > MAX_CONVOLUTION_HEIGHT) {
203      _mesa_error(ctx, GL_INVALID_VALUE, "glConvolutionFilter2D(height)");
204      return;
205   }
206
207   if (!_mesa_is_legal_format_and_type(ctx, format, type)) {
208      _mesa_error(ctx, GL_INVALID_OPERATION, "glConvolutionFilter2D(format or type)");
209      return;
210   }
211   if (format == GL_COLOR_INDEX ||
212       format == GL_STENCIL_INDEX ||
213       format == GL_DEPTH_COMPONENT ||
214       format == GL_INTENSITY ||
215       type == GL_BITMAP) {
216      _mesa_error(ctx, GL_INVALID_ENUM, "glConvolutionFilter2D(format or type)");
217      return;
218   }
219
220   /* this should have been caught earlier */
221   assert(_mesa_components_in_format(format));
222
223   ctx->Convolution2D.Format = format;
224   ctx->Convolution2D.InternalFormat = internalFormat;
225   ctx->Convolution2D.Width = width;
226   ctx->Convolution2D.Height = height;
227
228   image = _mesa_map_validate_pbo_source(ctx,
229                                         2, &ctx->Unpack, width, height, 1,
230                                         format, type, image,
231                                         "glConvolutionFilter2D");
232   if (!image)
233      return;
234
235   /* Unpack filter image.  We always store filters in RGBA format. */
236   for (i = 0; i < height; i++) {
237      const GLvoid *src = _mesa_image_address2d(&ctx->Unpack, image, width,
238                                                height, format, type, i, 0);
239      GLfloat *dst = ctx->Convolution2D.Filter + i * width * 4;
240      _mesa_unpack_color_span_float(ctx, width, GL_RGBA, dst,
241                                    format, type, src, &ctx->Unpack,
242                                    0); /* transferOps */
243   }
244
245   _mesa_unmap_pbo_source(ctx, &ctx->Unpack);
246
247   _mesa_scale_and_bias_rgba(width * height,
248                             (GLfloat (*)[4]) ctx->Convolution2D.Filter,
249                             ctx->Pixel.ConvolutionFilterScale[1][0],
250                             ctx->Pixel.ConvolutionFilterScale[1][1],
251                             ctx->Pixel.ConvolutionFilterScale[1][2],
252                             ctx->Pixel.ConvolutionFilterScale[1][3],
253                             ctx->Pixel.ConvolutionFilterBias[1][0],
254                             ctx->Pixel.ConvolutionFilterBias[1][1],
255                             ctx->Pixel.ConvolutionFilterBias[1][2],
256                             ctx->Pixel.ConvolutionFilterBias[1][3]);
257
258   ctx->NewState |= _NEW_PIXEL;
259}
260
261
262static void GLAPIENTRY
263_mesa_ConvolutionParameterf(GLenum target, GLenum pname, GLfloat param)
264{
265   GET_CURRENT_CONTEXT(ctx);
266   GLuint c;
267   ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx);
268
269   switch (target) {
270      case GL_CONVOLUTION_1D:
271         c = 0;
272         break;
273      case GL_CONVOLUTION_2D:
274         c = 1;
275         break;
276      case GL_SEPARABLE_2D:
277         c = 2;
278         break;
279      default:
280         _mesa_error(ctx, GL_INVALID_ENUM, "glConvolutionParameterf(target)");
281         return;
282   }
283
284   switch (pname) {
285      case GL_CONVOLUTION_BORDER_MODE:
286         if (param == (GLfloat) GL_REDUCE ||
287             param == (GLfloat) GL_CONSTANT_BORDER ||
288             param == (GLfloat) GL_REPLICATE_BORDER) {
289            ctx->Pixel.ConvolutionBorderMode[c] = (GLenum) param;
290         }
291         else {
292            _mesa_error(ctx, GL_INVALID_ENUM, "glConvolutionParameterf(params)");
293            return;
294         }
295         break;
296      default:
297         _mesa_error(ctx, GL_INVALID_ENUM, "glConvolutionParameterf(pname)");
298         return;
299   }
300
301   ctx->NewState |= _NEW_PIXEL;
302}
303
304
305static void GLAPIENTRY
306_mesa_ConvolutionParameterfv(GLenum target, GLenum pname, const GLfloat *params)
307{
308   GET_CURRENT_CONTEXT(ctx);
309   GLuint c;
310   ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx);
311
312   switch (target) {
313      case GL_CONVOLUTION_1D:
314         c = 0;
315         break;
316      case GL_CONVOLUTION_2D:
317         c = 1;
318         break;
319      case GL_SEPARABLE_2D:
320         c = 2;
321         break;
322      default:
323         _mesa_error(ctx, GL_INVALID_ENUM, "glConvolutionParameterfv(target)");
324         return;
325   }
326
327   switch (pname) {
328      case GL_CONVOLUTION_BORDER_COLOR:
329         COPY_4V(ctx->Pixel.ConvolutionBorderColor[c], params);
330         break;
331      case GL_CONVOLUTION_BORDER_MODE:
332         if (params[0] == (GLfloat) GL_REDUCE ||
333             params[0] == (GLfloat) GL_CONSTANT_BORDER ||
334             params[0] == (GLfloat) GL_REPLICATE_BORDER) {
335            ctx->Pixel.ConvolutionBorderMode[c] = (GLenum) params[0];
336         }
337         else {
338            _mesa_error(ctx, GL_INVALID_ENUM, "glConvolutionParameterfv(params)");
339            return;
340         }
341         break;
342      case GL_CONVOLUTION_FILTER_SCALE:
343         COPY_4V(ctx->Pixel.ConvolutionFilterScale[c], params);
344         break;
345      case GL_CONVOLUTION_FILTER_BIAS:
346         COPY_4V(ctx->Pixel.ConvolutionFilterBias[c], params);
347         break;
348      default:
349         _mesa_error(ctx, GL_INVALID_ENUM, "glConvolutionParameterfv(pname)");
350         return;
351   }
352
353   ctx->NewState |= _NEW_PIXEL;
354}
355
356
357static void GLAPIENTRY
358_mesa_ConvolutionParameteri(GLenum target, GLenum pname, GLint param)
359{
360   GET_CURRENT_CONTEXT(ctx);
361   GLuint c;
362   ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx);
363
364   switch (target) {
365      case GL_CONVOLUTION_1D:
366         c = 0;
367         break;
368      case GL_CONVOLUTION_2D:
369         c = 1;
370         break;
371      case GL_SEPARABLE_2D:
372         c = 2;
373         break;
374      default:
375         _mesa_error(ctx, GL_INVALID_ENUM, "glConvolutionParameteri(target)");
376         return;
377   }
378
379   switch (pname) {
380      case GL_CONVOLUTION_BORDER_MODE:
381         if (param == (GLint) GL_REDUCE ||
382             param == (GLint) GL_CONSTANT_BORDER ||
383             param == (GLint) GL_REPLICATE_BORDER) {
384            ctx->Pixel.ConvolutionBorderMode[c] = (GLenum) param;
385         }
386         else {
387            _mesa_error(ctx, GL_INVALID_ENUM, "glConvolutionParameteri(params)");
388            return;
389         }
390         break;
391      default:
392         _mesa_error(ctx, GL_INVALID_ENUM, "glConvolutionParameteri(pname)");
393         return;
394   }
395
396   ctx->NewState |= _NEW_PIXEL;
397}
398
399
400static void GLAPIENTRY
401_mesa_ConvolutionParameteriv(GLenum target, GLenum pname, const GLint *params)
402{
403   GET_CURRENT_CONTEXT(ctx);
404   GLuint c;
405   ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx);
406
407   switch (target) {
408      case GL_CONVOLUTION_1D:
409         c = 0;
410         break;
411      case GL_CONVOLUTION_2D:
412         c = 1;
413         break;
414      case GL_SEPARABLE_2D:
415         c = 2;
416         break;
417      default:
418         _mesa_error(ctx, GL_INVALID_ENUM, "glConvolutionParameteriv(target)");
419         return;
420   }
421
422   switch (pname) {
423      case GL_CONVOLUTION_BORDER_COLOR:
424	 ctx->Pixel.ConvolutionBorderColor[c][0] = INT_TO_FLOAT(params[0]);
425	 ctx->Pixel.ConvolutionBorderColor[c][1] = INT_TO_FLOAT(params[1]);
426	 ctx->Pixel.ConvolutionBorderColor[c][2] = INT_TO_FLOAT(params[2]);
427	 ctx->Pixel.ConvolutionBorderColor[c][3] = INT_TO_FLOAT(params[3]);
428         break;
429      case GL_CONVOLUTION_BORDER_MODE:
430         if (params[0] == (GLint) GL_REDUCE ||
431             params[0] == (GLint) GL_CONSTANT_BORDER ||
432             params[0] == (GLint) GL_REPLICATE_BORDER) {
433            ctx->Pixel.ConvolutionBorderMode[c] = (GLenum) params[0];
434         }
435         else {
436            _mesa_error(ctx, GL_INVALID_ENUM, "glConvolutionParameteriv(params)");
437            return;
438         }
439         break;
440      case GL_CONVOLUTION_FILTER_SCALE:
441	 /* COPY_4V(ctx->Pixel.ConvolutionFilterScale[c], params); */
442	 /* need cast to prevent compiler warnings */
443	 ctx->Pixel.ConvolutionFilterScale[c][0] = (GLfloat) params[0];
444	 ctx->Pixel.ConvolutionFilterScale[c][1] = (GLfloat) params[1];
445	 ctx->Pixel.ConvolutionFilterScale[c][2] = (GLfloat) params[2];
446	 ctx->Pixel.ConvolutionFilterScale[c][3] = (GLfloat) params[3];
447         break;
448      case GL_CONVOLUTION_FILTER_BIAS:
449	 /* COPY_4V(ctx->Pixel.ConvolutionFilterBias[c], params); */
450	 /* need cast to prevent compiler warnings */
451	 ctx->Pixel.ConvolutionFilterBias[c][0] = (GLfloat) params[0];
452	 ctx->Pixel.ConvolutionFilterBias[c][1] = (GLfloat) params[1];
453	 ctx->Pixel.ConvolutionFilterBias[c][2] = (GLfloat) params[2];
454	 ctx->Pixel.ConvolutionFilterBias[c][3] = (GLfloat) params[3];
455         break;
456      default:
457         _mesa_error(ctx, GL_INVALID_ENUM, "glConvolutionParameteriv(pname)");
458         return;
459   }
460
461   ctx->NewState |= _NEW_PIXEL;
462}
463
464
465static void GLAPIENTRY
466_mesa_CopyConvolutionFilter1D(GLenum target, GLenum internalFormat, GLint x, GLint y, GLsizei width)
467{
468   GLint baseFormat;
469   GET_CURRENT_CONTEXT(ctx);
470   ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx);
471
472   if (target != GL_CONVOLUTION_1D) {
473      _mesa_error(ctx, GL_INVALID_ENUM, "glCopyConvolutionFilter1D(target)");
474      return;
475   }
476
477   baseFormat = base_filter_format(internalFormat);
478   if (baseFormat < 0 || baseFormat == GL_COLOR_INDEX) {
479      _mesa_error(ctx, GL_INVALID_ENUM, "glCopyConvolutionFilter1D(internalFormat)");
480      return;
481   }
482
483   if (width < 0 || width > MAX_CONVOLUTION_WIDTH) {
484      _mesa_error(ctx, GL_INVALID_VALUE, "glCopyConvolutionFilter1D(width)");
485      return;
486   }
487
488   if (!ctx->ReadBuffer->_ColorReadBuffer) {
489      return;      /* no readbuffer - OK */
490   }
491
492   ctx->Driver.CopyConvolutionFilter1D( ctx, target,
493					internalFormat, x, y, width);
494}
495
496
497static void GLAPIENTRY
498_mesa_CopyConvolutionFilter2D(GLenum target, GLenum internalFormat, GLint x, GLint y, GLsizei width, GLsizei height)
499{
500   GLint baseFormat;
501   GET_CURRENT_CONTEXT(ctx);
502   ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx);
503
504   if (target != GL_CONVOLUTION_2D) {
505      _mesa_error(ctx, GL_INVALID_ENUM, "glCopyConvolutionFilter2D(target)");
506      return;
507   }
508
509   baseFormat = base_filter_format(internalFormat);
510   if (baseFormat < 0 || baseFormat == GL_COLOR_INDEX) {
511      _mesa_error(ctx, GL_INVALID_ENUM, "glCopyConvolutionFilter2D(internalFormat)");
512      return;
513   }
514
515   if (width < 0 || width > MAX_CONVOLUTION_WIDTH) {
516      _mesa_error(ctx, GL_INVALID_VALUE, "glCopyConvolutionFilter2D(width)");
517      return;
518   }
519   if (height < 0 || height > MAX_CONVOLUTION_HEIGHT) {
520      _mesa_error(ctx, GL_INVALID_VALUE, "glCopyConvolutionFilter2D(height)");
521      return;
522   }
523
524   if (!ctx->ReadBuffer->_ColorReadBuffer) {
525      return;      /* no readbuffer - OK */
526   }
527
528   ctx->Driver.CopyConvolutionFilter2D( ctx, target, internalFormat, x, y,
529					width, height );
530}
531
532
533static void GLAPIENTRY
534_mesa_GetConvolutionFilter(GLenum target, GLenum format, GLenum type,
535                           GLvoid *image)
536{
537   struct gl_convolution_attrib *filter;
538   GLuint row;
539   GET_CURRENT_CONTEXT(ctx);
540   ASSERT_OUTSIDE_BEGIN_END(ctx);
541
542   if (ctx->NewState) {
543      _mesa_update_state(ctx);
544   }
545
546   if (!_mesa_is_legal_format_and_type(ctx, format, type)) {
547      _mesa_error(ctx, GL_INVALID_OPERATION, "glGetConvolutionFilter(format or type)");
548      return;
549   }
550
551   if (format == GL_COLOR_INDEX ||
552       format == GL_STENCIL_INDEX ||
553       format == GL_DEPTH_COMPONENT ||
554       format == GL_INTENSITY ||
555       type == GL_BITMAP) {
556      _mesa_error(ctx, GL_INVALID_ENUM, "glGetConvolutionFilter(format or type)");
557      return;
558   }
559
560   switch (target) {
561      case GL_CONVOLUTION_1D:
562         filter = &(ctx->Convolution1D);
563         break;
564      case GL_CONVOLUTION_2D:
565         filter = &(ctx->Convolution2D);
566         break;
567      default:
568         _mesa_error(ctx, GL_INVALID_ENUM, "glGetConvolutionFilter(target)");
569         return;
570   }
571
572   image = _mesa_map_validate_pbo_dest(ctx, 2, &ctx->Pack,
573                                       filter->Width, filter->Height, 1,
574                                       format, type, image,
575                                       "glGetConvolutionFilter");
576   if (!image)
577      return;
578
579   for (row = 0; row < filter->Height; row++) {
580      GLvoid *dst = _mesa_image_address2d(&ctx->Pack, image, filter->Width,
581                                          filter->Height, format, type,
582                                          row, 0);
583      GLfloat (*src)[4] = (GLfloat (*)[4]) (filter->Filter + row * filter->Width * 4);
584      _mesa_pack_rgba_span_float(ctx, filter->Width, src,
585                                 format, type, dst, &ctx->Pack, 0x0);
586   }
587
588   _mesa_unmap_pbo_dest(ctx, &ctx->Pack);
589}
590
591
592static void GLAPIENTRY
593_mesa_GetConvolutionParameterfv(GLenum target, GLenum pname, GLfloat *params)
594{
595   GET_CURRENT_CONTEXT(ctx);
596   const struct gl_convolution_attrib *conv;
597   GLuint c;
598   ASSERT_OUTSIDE_BEGIN_END(ctx);
599
600   switch (target) {
601      case GL_CONVOLUTION_1D:
602         c = 0;
603         conv = &ctx->Convolution1D;
604         break;
605      case GL_CONVOLUTION_2D:
606         c = 1;
607         conv = &ctx->Convolution2D;
608         break;
609      case GL_SEPARABLE_2D:
610         c = 2;
611         conv = &ctx->Separable2D;
612         break;
613      default:
614         _mesa_error(ctx, GL_INVALID_ENUM, "glGetConvolutionParameterfv(target)");
615         return;
616   }
617
618   switch (pname) {
619      case GL_CONVOLUTION_BORDER_COLOR:
620         COPY_4V(params, ctx->Pixel.ConvolutionBorderColor[c]);
621         break;
622      case GL_CONVOLUTION_BORDER_MODE:
623         *params = (GLfloat) ctx->Pixel.ConvolutionBorderMode[c];
624         break;
625      case GL_CONVOLUTION_FILTER_SCALE:
626         COPY_4V(params, ctx->Pixel.ConvolutionFilterScale[c]);
627         break;
628      case GL_CONVOLUTION_FILTER_BIAS:
629         COPY_4V(params, ctx->Pixel.ConvolutionFilterBias[c]);
630         break;
631      case GL_CONVOLUTION_FORMAT:
632         *params = (GLfloat) conv->Format;
633         break;
634      case GL_CONVOLUTION_WIDTH:
635         *params = (GLfloat) conv->Width;
636         break;
637      case GL_CONVOLUTION_HEIGHT:
638         *params = (GLfloat) conv->Height;
639         break;
640      case GL_MAX_CONVOLUTION_WIDTH:
641         *params = (GLfloat) ctx->Const.MaxConvolutionWidth;
642         break;
643      case GL_MAX_CONVOLUTION_HEIGHT:
644         *params = (GLfloat) ctx->Const.MaxConvolutionHeight;
645         break;
646      default:
647         _mesa_error(ctx, GL_INVALID_ENUM, "glGetConvolutionParameterfv(pname)");
648         return;
649   }
650}
651
652
653static void GLAPIENTRY
654_mesa_GetConvolutionParameteriv(GLenum target, GLenum pname, GLint *params)
655{
656   GET_CURRENT_CONTEXT(ctx);
657   const struct gl_convolution_attrib *conv;
658   GLuint c;
659   ASSERT_OUTSIDE_BEGIN_END(ctx);
660
661   switch (target) {
662      case GL_CONVOLUTION_1D:
663         c = 0;
664         conv = &ctx->Convolution1D;
665         break;
666      case GL_CONVOLUTION_2D:
667         c = 1;
668         conv = &ctx->Convolution2D;
669         break;
670      case GL_SEPARABLE_2D:
671         c = 2;
672         conv = &ctx->Separable2D;
673         break;
674      default:
675         _mesa_error(ctx, GL_INVALID_ENUM, "glGetConvolutionParameteriv(target)");
676         return;
677   }
678
679   switch (pname) {
680      case GL_CONVOLUTION_BORDER_COLOR:
681         params[0] = FLOAT_TO_INT(ctx->Pixel.ConvolutionBorderColor[c][0]);
682         params[1] = FLOAT_TO_INT(ctx->Pixel.ConvolutionBorderColor[c][1]);
683         params[2] = FLOAT_TO_INT(ctx->Pixel.ConvolutionBorderColor[c][2]);
684         params[3] = FLOAT_TO_INT(ctx->Pixel.ConvolutionBorderColor[c][3]);
685         break;
686      case GL_CONVOLUTION_BORDER_MODE:
687         *params = (GLint) ctx->Pixel.ConvolutionBorderMode[c];
688         break;
689      case GL_CONVOLUTION_FILTER_SCALE:
690         params[0] = (GLint) ctx->Pixel.ConvolutionFilterScale[c][0];
691         params[1] = (GLint) ctx->Pixel.ConvolutionFilterScale[c][1];
692         params[2] = (GLint) ctx->Pixel.ConvolutionFilterScale[c][2];
693         params[3] = (GLint) ctx->Pixel.ConvolutionFilterScale[c][3];
694         break;
695      case GL_CONVOLUTION_FILTER_BIAS:
696         params[0] = (GLint) ctx->Pixel.ConvolutionFilterBias[c][0];
697         params[1] = (GLint) ctx->Pixel.ConvolutionFilterBias[c][1];
698         params[2] = (GLint) ctx->Pixel.ConvolutionFilterBias[c][2];
699         params[3] = (GLint) ctx->Pixel.ConvolutionFilterBias[c][3];
700         break;
701      case GL_CONVOLUTION_FORMAT:
702         *params = (GLint) conv->Format;
703         break;
704      case GL_CONVOLUTION_WIDTH:
705         *params = (GLint) conv->Width;
706         break;
707      case GL_CONVOLUTION_HEIGHT:
708         *params = (GLint) conv->Height;
709         break;
710      case GL_MAX_CONVOLUTION_WIDTH:
711         *params = (GLint) ctx->Const.MaxConvolutionWidth;
712         break;
713      case GL_MAX_CONVOLUTION_HEIGHT:
714         *params = (GLint) ctx->Const.MaxConvolutionHeight;
715         break;
716      default:
717         _mesa_error(ctx, GL_INVALID_ENUM, "glGetConvolutionParameteriv(pname)");
718         return;
719   }
720}
721
722
723static void GLAPIENTRY
724_mesa_GetSeparableFilter(GLenum target, GLenum format, GLenum type,
725                         GLvoid *row, GLvoid *column, GLvoid *span)
726{
727   const GLint colStart = MAX_CONVOLUTION_WIDTH * 4;
728   struct gl_convolution_attrib *filter;
729   GET_CURRENT_CONTEXT(ctx);
730   ASSERT_OUTSIDE_BEGIN_END(ctx);
731
732   if (ctx->NewState) {
733      _mesa_update_state(ctx);
734   }
735
736   if (target != GL_SEPARABLE_2D) {
737      _mesa_error(ctx, GL_INVALID_ENUM, "glGetSeparableFilter(target)");
738      return;
739   }
740
741   if (!_mesa_is_legal_format_and_type(ctx, format, type)) {
742      _mesa_error(ctx, GL_INVALID_OPERATION,
743                  "glGetConvolutionFilter(format or type)");
744      return;
745   }
746
747   if (format == GL_COLOR_INDEX ||
748       format == GL_STENCIL_INDEX ||
749       format == GL_DEPTH_COMPONENT ||
750       format == GL_INTENSITY ||
751       type == GL_BITMAP) {
752      _mesa_error(ctx, GL_INVALID_ENUM, "glGetConvolutionFilter(format or type)");
753      return;
754   }
755
756   filter = &ctx->Separable2D;
757
758   /* Get row filter */
759   row = _mesa_map_validate_pbo_dest(ctx, 1, &ctx->Pack,
760                                     filter->Width, 1, 1,
761                                     format, type, row,
762                                     "glGetConvolutionFilter");
763   if (row) {
764      GLvoid *dst = _mesa_image_address1d(&ctx->Pack, row, filter->Width,
765                                          format, type, 0);
766      _mesa_pack_rgba_span_float(ctx, filter->Width,
767                                 (GLfloat (*)[4]) filter->Filter,
768                                 format, type, dst, &ctx->Pack, 0x0);
769      _mesa_unmap_pbo_dest(ctx, &ctx->Pack);
770   }
771
772   /* get column filter */
773   column = _mesa_map_validate_pbo_dest(ctx, 1, &ctx->Pack,
774                                        filter->Height, 1, 1,
775                                        format, type, column,
776                                        "glGetConvolutionFilter");
777   if (column) {
778      GLvoid *dst = _mesa_image_address1d(&ctx->Pack, column, filter->Height,
779                                          format, type, 0);
780      GLfloat (*src)[4] = (GLfloat (*)[4]) (filter->Filter + colStart);
781      _mesa_pack_rgba_span_float(ctx, filter->Height, src,
782                                 format, type, dst, &ctx->Pack, 0x0);
783      _mesa_unmap_pbo_dest(ctx, &ctx->Pack);
784   }
785
786   (void) span;  /* unused at this time */
787}
788
789
790static void GLAPIENTRY
791_mesa_SeparableFilter2D(GLenum target, GLenum internalFormat, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *row, const GLvoid *column)
792{
793   const GLint colStart = MAX_CONVOLUTION_WIDTH * 4;
794   GLint baseFormat;
795   GET_CURRENT_CONTEXT(ctx);
796   ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx);
797
798   if (target != GL_SEPARABLE_2D) {
799      _mesa_error(ctx, GL_INVALID_ENUM, "glSeparableFilter2D(target)");
800      return;
801   }
802
803   baseFormat = base_filter_format(internalFormat);
804   if (baseFormat < 0 || baseFormat == GL_COLOR_INDEX) {
805      _mesa_error(ctx, GL_INVALID_ENUM, "glSeparableFilter2D(internalFormat)");
806      return;
807   }
808
809   if (width < 0 || width > MAX_CONVOLUTION_WIDTH) {
810      _mesa_error(ctx, GL_INVALID_VALUE, "glSeparableFilter2D(width)");
811      return;
812   }
813   if (height < 0 || height > MAX_CONVOLUTION_HEIGHT) {
814      _mesa_error(ctx, GL_INVALID_VALUE, "glSeparableFilter2D(height)");
815      return;
816   }
817
818   if (!_mesa_is_legal_format_and_type(ctx, format, type)) {
819      _mesa_error(ctx, GL_INVALID_OPERATION, "glSeparableFilter2D(format or type)");
820      return;
821   }
822
823   if (format == GL_COLOR_INDEX ||
824       format == GL_STENCIL_INDEX ||
825       format == GL_DEPTH_COMPONENT ||
826       format == GL_INTENSITY ||
827       type == GL_BITMAP) {
828      _mesa_error(ctx, GL_INVALID_ENUM, "glSeparableFilter2D(format or type)");
829      return;
830   }
831
832   ctx->Separable2D.Format = format;
833   ctx->Separable2D.InternalFormat = internalFormat;
834   ctx->Separable2D.Width = width;
835   ctx->Separable2D.Height = height;
836
837   /* unpack row filter */
838   row = _mesa_map_validate_pbo_source(ctx, 1, &ctx->Unpack,
839                                       width, 1, 1,
840                                       format, type, row,
841                                       "glSeparableFilter2D");
842   if (row) {
843      _mesa_unpack_color_span_float(ctx, width, GL_RGBA,
844                                    ctx->Separable2D.Filter,
845                                    format, type, row, &ctx->Unpack,
846                                    0x0);  /* transferOps */
847      _mesa_scale_and_bias_rgba(width,
848                             (GLfloat (*)[4]) ctx->Separable2D.Filter,
849                             ctx->Pixel.ConvolutionFilterScale[2][0],
850                             ctx->Pixel.ConvolutionFilterScale[2][1],
851                             ctx->Pixel.ConvolutionFilterScale[2][2],
852                             ctx->Pixel.ConvolutionFilterScale[2][3],
853                             ctx->Pixel.ConvolutionFilterBias[2][0],
854                             ctx->Pixel.ConvolutionFilterBias[2][1],
855                             ctx->Pixel.ConvolutionFilterBias[2][2],
856                             ctx->Pixel.ConvolutionFilterBias[2][3]);
857      _mesa_unmap_pbo_source(ctx, &ctx->Unpack);
858   }
859
860   /* unpack column filter */
861   column = _mesa_map_validate_pbo_source(ctx, 1, &ctx->Unpack,
862                                          height, 1, 1,
863                                          format, type, column,
864                                          "glSeparableFilter2D");
865   if (column) {
866      _mesa_unpack_color_span_float(ctx, height, GL_RGBA,
867                                    &ctx->Separable2D.Filter[colStart],
868                                    format, type, column, &ctx->Unpack,
869                                    0); /* transferOps */
870
871      _mesa_scale_and_bias_rgba(height,
872                       (GLfloat (*)[4]) (ctx->Separable2D.Filter + colStart),
873                       ctx->Pixel.ConvolutionFilterScale[2][0],
874                       ctx->Pixel.ConvolutionFilterScale[2][1],
875                       ctx->Pixel.ConvolutionFilterScale[2][2],
876                       ctx->Pixel.ConvolutionFilterScale[2][3],
877                       ctx->Pixel.ConvolutionFilterBias[2][0],
878                       ctx->Pixel.ConvolutionFilterBias[2][1],
879                       ctx->Pixel.ConvolutionFilterBias[2][2],
880                       ctx->Pixel.ConvolutionFilterBias[2][3]);
881      _mesa_unmap_pbo_source(ctx, &ctx->Unpack);
882   }
883
884   if (_mesa_is_bufferobj(ctx->Unpack.BufferObj)) {
885      ctx->Driver.UnmapBuffer(ctx, GL_PIXEL_UNPACK_BUFFER_EXT,
886                              ctx->Unpack.BufferObj);
887   }
888
889   ctx->NewState |= _NEW_PIXEL;
890}
891
892
893/**********************************************************************/
894/***                   image convolution functions                  ***/
895/**********************************************************************/
896
897static void
898convolve_1d_reduce(GLint srcWidth, const GLfloat src[][4],
899                   GLint filterWidth, const GLfloat filter[][4],
900                   GLfloat dest[][4])
901{
902   GLint dstWidth;
903   GLint i, n;
904
905   if (filterWidth >= 1)
906      dstWidth = srcWidth - (filterWidth - 1);
907   else
908      dstWidth = srcWidth;
909
910   if (dstWidth <= 0)
911      return;  /* null result */
912
913   for (i = 0; i < dstWidth; i++) {
914      GLfloat sumR = 0.0;
915      GLfloat sumG = 0.0;
916      GLfloat sumB = 0.0;
917      GLfloat sumA = 0.0;
918      for (n = 0; n < filterWidth; n++) {
919         sumR += src[i + n][RCOMP] * filter[n][RCOMP];
920         sumG += src[i + n][GCOMP] * filter[n][GCOMP];
921         sumB += src[i + n][BCOMP] * filter[n][BCOMP];
922         sumA += src[i + n][ACOMP] * filter[n][ACOMP];
923      }
924      dest[i][RCOMP] = sumR;
925      dest[i][GCOMP] = sumG;
926      dest[i][BCOMP] = sumB;
927      dest[i][ACOMP] = sumA;
928   }
929}
930
931
932static void
933convolve_1d_constant(GLint srcWidth, const GLfloat src[][4],
934                     GLint filterWidth, const GLfloat filter[][4],
935                     GLfloat dest[][4],
936                     const GLfloat borderColor[4])
937{
938   const GLint halfFilterWidth = filterWidth / 2;
939   GLint i, n;
940
941   for (i = 0; i < srcWidth; i++) {
942      GLfloat sumR = 0.0;
943      GLfloat sumG = 0.0;
944      GLfloat sumB = 0.0;
945      GLfloat sumA = 0.0;
946      for (n = 0; n < filterWidth; n++) {
947         if (i + n < halfFilterWidth || i + n - halfFilterWidth >= srcWidth) {
948            sumR += borderColor[RCOMP] * filter[n][RCOMP];
949            sumG += borderColor[GCOMP] * filter[n][GCOMP];
950            sumB += borderColor[BCOMP] * filter[n][BCOMP];
951            sumA += borderColor[ACOMP] * filter[n][ACOMP];
952         }
953         else {
954            sumR += src[i + n - halfFilterWidth][RCOMP] * filter[n][RCOMP];
955            sumG += src[i + n - halfFilterWidth][GCOMP] * filter[n][GCOMP];
956            sumB += src[i + n - halfFilterWidth][BCOMP] * filter[n][BCOMP];
957            sumA += src[i + n - halfFilterWidth][ACOMP] * filter[n][ACOMP];
958         }
959      }
960      dest[i][RCOMP] = sumR;
961      dest[i][GCOMP] = sumG;
962      dest[i][BCOMP] = sumB;
963      dest[i][ACOMP] = sumA;
964   }
965}
966
967
968static void
969convolve_1d_replicate(GLint srcWidth, const GLfloat src[][4],
970                      GLint filterWidth, const GLfloat filter[][4],
971                      GLfloat dest[][4])
972{
973   const GLint halfFilterWidth = filterWidth / 2;
974   GLint i, n;
975
976   for (i = 0; i < srcWidth; i++) {
977      GLfloat sumR = 0.0;
978      GLfloat sumG = 0.0;
979      GLfloat sumB = 0.0;
980      GLfloat sumA = 0.0;
981      for (n = 0; n < filterWidth; n++) {
982         if (i + n < halfFilterWidth) {
983            sumR += src[0][RCOMP] * filter[n][RCOMP];
984            sumG += src[0][GCOMP] * filter[n][GCOMP];
985            sumB += src[0][BCOMP] * filter[n][BCOMP];
986            sumA += src[0][ACOMP] * filter[n][ACOMP];
987         }
988         else if (i + n - halfFilterWidth >= srcWidth) {
989            sumR += src[srcWidth - 1][RCOMP] * filter[n][RCOMP];
990            sumG += src[srcWidth - 1][GCOMP] * filter[n][GCOMP];
991            sumB += src[srcWidth - 1][BCOMP] * filter[n][BCOMP];
992            sumA += src[srcWidth - 1][ACOMP] * filter[n][ACOMP];
993         }
994         else {
995            sumR += src[i + n - halfFilterWidth][RCOMP] * filter[n][RCOMP];
996            sumG += src[i + n - halfFilterWidth][GCOMP] * filter[n][GCOMP];
997            sumB += src[i + n - halfFilterWidth][BCOMP] * filter[n][BCOMP];
998            sumA += src[i + n - halfFilterWidth][ACOMP] * filter[n][ACOMP];
999         }
1000      }
1001      dest[i][RCOMP] = sumR;
1002      dest[i][GCOMP] = sumG;
1003      dest[i][BCOMP] = sumB;
1004      dest[i][ACOMP] = sumA;
1005   }
1006}
1007
1008
1009static void
1010convolve_2d_reduce(GLint srcWidth, GLint srcHeight,
1011                   const GLfloat src[][4],
1012                   GLint filterWidth, GLint filterHeight,
1013                   const GLfloat filter[][4],
1014                   GLfloat dest[][4])
1015{
1016   GLint dstWidth, dstHeight;
1017   GLint i, j, n, m;
1018
1019   if (filterWidth >= 1)
1020      dstWidth = srcWidth - (filterWidth - 1);
1021   else
1022      dstWidth = srcWidth;
1023
1024   if (filterHeight >= 1)
1025      dstHeight = srcHeight - (filterHeight - 1);
1026   else
1027      dstHeight = srcHeight;
1028
1029   if (dstWidth <= 0 || dstHeight <= 0)
1030      return;
1031
1032   for (j = 0; j < dstHeight; j++) {
1033      for (i = 0; i < dstWidth; i++) {
1034         GLfloat sumR = 0.0;
1035         GLfloat sumG = 0.0;
1036         GLfloat sumB = 0.0;
1037         GLfloat sumA = 0.0;
1038         for (m = 0; m < filterHeight; m++) {
1039            for (n = 0; n < filterWidth; n++) {
1040               const GLint k = (j + m) * srcWidth + i + n;
1041               const GLint f = m * filterWidth + n;
1042               sumR += src[k][RCOMP] * filter[f][RCOMP];
1043               sumG += src[k][GCOMP] * filter[f][GCOMP];
1044               sumB += src[k][BCOMP] * filter[f][BCOMP];
1045               sumA += src[k][ACOMP] * filter[f][ACOMP];
1046            }
1047         }
1048         dest[j * dstWidth + i][RCOMP] = sumR;
1049         dest[j * dstWidth + i][GCOMP] = sumG;
1050         dest[j * dstWidth + i][BCOMP] = sumB;
1051         dest[j * dstWidth + i][ACOMP] = sumA;
1052      }
1053   }
1054}
1055
1056
1057static void
1058convolve_2d_constant(GLint srcWidth, GLint srcHeight,
1059                     const GLfloat src[][4],
1060                     GLint filterWidth, GLint filterHeight,
1061                     const GLfloat filter[][4],
1062                     GLfloat dest[][4],
1063                     const GLfloat borderColor[4])
1064{
1065   const GLint halfFilterWidth = filterWidth / 2;
1066   const GLint halfFilterHeight = filterHeight / 2;
1067   GLint i, j, n, m;
1068
1069   for (j = 0; j < srcHeight; j++) {
1070      for (i = 0; i < srcWidth; i++) {
1071         GLfloat sumR = 0.0;
1072         GLfloat sumG = 0.0;
1073         GLfloat sumB = 0.0;
1074         GLfloat sumA = 0.0;
1075         for (m = 0; m < filterHeight; m++) {
1076            for (n = 0; n < filterWidth; n++) {
1077               const GLint f = m * filterWidth + n;
1078               const GLint is = i + n - halfFilterWidth;
1079               const GLint js = j + m - halfFilterHeight;
1080               if (is < 0 || is >= srcWidth ||
1081                   js < 0 || js >= srcHeight) {
1082                  sumR += borderColor[RCOMP] * filter[f][RCOMP];
1083                  sumG += borderColor[GCOMP] * filter[f][GCOMP];
1084                  sumB += borderColor[BCOMP] * filter[f][BCOMP];
1085                  sumA += borderColor[ACOMP] * filter[f][ACOMP];
1086               }
1087               else {
1088                  const GLint k = js * srcWidth + is;
1089                  sumR += src[k][RCOMP] * filter[f][RCOMP];
1090                  sumG += src[k][GCOMP] * filter[f][GCOMP];
1091                  sumB += src[k][BCOMP] * filter[f][BCOMP];
1092                  sumA += src[k][ACOMP] * filter[f][ACOMP];
1093               }
1094            }
1095         }
1096         dest[j * srcWidth + i][RCOMP] = sumR;
1097         dest[j * srcWidth + i][GCOMP] = sumG;
1098         dest[j * srcWidth + i][BCOMP] = sumB;
1099         dest[j * srcWidth + i][ACOMP] = sumA;
1100      }
1101   }
1102}
1103
1104
1105static void
1106convolve_2d_replicate(GLint srcWidth, GLint srcHeight,
1107                      const GLfloat src[][4],
1108                      GLint filterWidth, GLint filterHeight,
1109                      const GLfloat filter[][4],
1110                      GLfloat dest[][4])
1111{
1112   const GLint halfFilterWidth = filterWidth / 2;
1113   const GLint halfFilterHeight = filterHeight / 2;
1114   GLint i, j, n, m;
1115
1116   for (j = 0; j < srcHeight; j++) {
1117      for (i = 0; i < srcWidth; i++) {
1118         GLfloat sumR = 0.0;
1119         GLfloat sumG = 0.0;
1120         GLfloat sumB = 0.0;
1121         GLfloat sumA = 0.0;
1122         for (m = 0; m < filterHeight; m++) {
1123            for (n = 0; n < filterWidth; n++) {
1124               const GLint f = m * filterWidth + n;
1125               GLint is = i + n - halfFilterWidth;
1126               GLint js = j + m - halfFilterHeight;
1127               GLint k;
1128               if (is < 0)
1129                  is = 0;
1130               else if (is >= srcWidth)
1131                  is = srcWidth - 1;
1132               if (js < 0)
1133                  js = 0;
1134               else if (js >= srcHeight)
1135                  js = srcHeight - 1;
1136               k = js * srcWidth + is;
1137               sumR += src[k][RCOMP] * filter[f][RCOMP];
1138               sumG += src[k][GCOMP] * filter[f][GCOMP];
1139               sumB += src[k][BCOMP] * filter[f][BCOMP];
1140               sumA += src[k][ACOMP] * filter[f][ACOMP];
1141            }
1142         }
1143         dest[j * srcWidth + i][RCOMP] = sumR;
1144         dest[j * srcWidth + i][GCOMP] = sumG;
1145         dest[j * srcWidth + i][BCOMP] = sumB;
1146         dest[j * srcWidth + i][ACOMP] = sumA;
1147      }
1148   }
1149}
1150
1151
1152static void
1153convolve_sep_reduce(GLint srcWidth, GLint srcHeight,
1154                    const GLfloat src[][4],
1155                    GLint filterWidth, GLint filterHeight,
1156                    const GLfloat rowFilt[][4],
1157                    const GLfloat colFilt[][4],
1158                    GLfloat dest[][4])
1159{
1160   GLint dstWidth, dstHeight;
1161   GLint i, j, n, m;
1162
1163   if (filterWidth >= 1)
1164      dstWidth = srcWidth - (filterWidth - 1);
1165   else
1166      dstWidth = srcWidth;
1167
1168   if (filterHeight >= 1)
1169      dstHeight = srcHeight - (filterHeight - 1);
1170   else
1171      dstHeight = srcHeight;
1172
1173   if (dstWidth <= 0 || dstHeight <= 0)
1174      return;
1175
1176   for (j = 0; j < dstHeight; j++) {
1177      for (i = 0; i < dstWidth; i++) {
1178         GLfloat sumR = 0.0;
1179         GLfloat sumG = 0.0;
1180         GLfloat sumB = 0.0;
1181         GLfloat sumA = 0.0;
1182         for (m = 0; m < filterHeight; m++) {
1183            for (n = 0; n < filterWidth; n++) {
1184               GLint k = (j + m) * srcWidth + i + n;
1185               sumR += src[k][RCOMP] * rowFilt[n][RCOMP] * colFilt[m][RCOMP];
1186               sumG += src[k][GCOMP] * rowFilt[n][GCOMP] * colFilt[m][GCOMP];
1187               sumB += src[k][BCOMP] * rowFilt[n][BCOMP] * colFilt[m][BCOMP];
1188               sumA += src[k][ACOMP] * rowFilt[n][ACOMP] * colFilt[m][ACOMP];
1189            }
1190         }
1191         dest[j * dstWidth + i][RCOMP] = sumR;
1192         dest[j * dstWidth + i][GCOMP] = sumG;
1193         dest[j * dstWidth + i][BCOMP] = sumB;
1194         dest[j * dstWidth + i][ACOMP] = sumA;
1195      }
1196   }
1197}
1198
1199
1200static void
1201convolve_sep_constant(GLint srcWidth, GLint srcHeight,
1202                      const GLfloat src[][4],
1203                      GLint filterWidth, GLint filterHeight,
1204                      const GLfloat rowFilt[][4],
1205                      const GLfloat colFilt[][4],
1206                      GLfloat dest[][4],
1207                      const GLfloat borderColor[4])
1208{
1209   const GLint halfFilterWidth = filterWidth / 2;
1210   const GLint halfFilterHeight = filterHeight / 2;
1211   GLint i, j, n, m;
1212
1213   for (j = 0; j < srcHeight; j++) {
1214      for (i = 0; i < srcWidth; i++) {
1215         GLfloat sumR = 0.0;
1216         GLfloat sumG = 0.0;
1217         GLfloat sumB = 0.0;
1218         GLfloat sumA = 0.0;
1219         for (m = 0; m < filterHeight; m++) {
1220            for (n = 0; n < filterWidth; n++) {
1221               const GLint is = i + n - halfFilterWidth;
1222               const GLint js = j + m - halfFilterHeight;
1223               if (is < 0 || is >= srcWidth ||
1224                   js < 0 || js >= srcHeight) {
1225                  sumR += borderColor[RCOMP] * rowFilt[n][RCOMP] * colFilt[m][RCOMP];
1226                  sumG += borderColor[GCOMP] * rowFilt[n][GCOMP] * colFilt[m][GCOMP];
1227                  sumB += borderColor[BCOMP] * rowFilt[n][BCOMP] * colFilt[m][BCOMP];
1228                  sumA += borderColor[ACOMP] * rowFilt[n][ACOMP] * colFilt[m][ACOMP];
1229               }
1230               else {
1231                  GLint k = js * srcWidth + is;
1232                  sumR += src[k][RCOMP] * rowFilt[n][RCOMP] * colFilt[m][RCOMP];
1233                  sumG += src[k][GCOMP] * rowFilt[n][GCOMP] * colFilt[m][GCOMP];
1234                  sumB += src[k][BCOMP] * rowFilt[n][BCOMP] * colFilt[m][BCOMP];
1235                  sumA += src[k][ACOMP] * rowFilt[n][ACOMP] * colFilt[m][ACOMP];
1236               }
1237
1238            }
1239         }
1240         dest[j * srcWidth + i][RCOMP] = sumR;
1241         dest[j * srcWidth + i][GCOMP] = sumG;
1242         dest[j * srcWidth + i][BCOMP] = sumB;
1243         dest[j * srcWidth + i][ACOMP] = sumA;
1244      }
1245   }
1246}
1247
1248
1249static void
1250convolve_sep_replicate(GLint srcWidth, GLint srcHeight,
1251                       const GLfloat src[][4],
1252                       GLint filterWidth, GLint filterHeight,
1253                       const GLfloat rowFilt[][4],
1254                       const GLfloat colFilt[][4],
1255                       GLfloat dest[][4])
1256{
1257   const GLint halfFilterWidth = filterWidth / 2;
1258   const GLint halfFilterHeight = filterHeight / 2;
1259   GLint i, j, n, m;
1260
1261   for (j = 0; j < srcHeight; j++) {
1262      for (i = 0; i < srcWidth; i++) {
1263         GLfloat sumR = 0.0;
1264         GLfloat sumG = 0.0;
1265         GLfloat sumB = 0.0;
1266         GLfloat sumA = 0.0;
1267         for (m = 0; m < filterHeight; m++) {
1268            for (n = 0; n < filterWidth; n++) {
1269               GLint is = i + n - halfFilterWidth;
1270               GLint js = j + m - halfFilterHeight;
1271               GLint k;
1272               if (is < 0)
1273                  is = 0;
1274               else if (is >= srcWidth)
1275                  is = srcWidth - 1;
1276               if (js < 0)
1277                  js = 0;
1278               else if (js >= srcHeight)
1279                  js = srcHeight - 1;
1280               k = js * srcWidth + is;
1281               sumR += src[k][RCOMP] * rowFilt[n][RCOMP] * colFilt[m][RCOMP];
1282               sumG += src[k][GCOMP] * rowFilt[n][GCOMP] * colFilt[m][GCOMP];
1283               sumB += src[k][BCOMP] * rowFilt[n][BCOMP] * colFilt[m][BCOMP];
1284               sumA += src[k][ACOMP] * rowFilt[n][ACOMP] * colFilt[m][ACOMP];
1285            }
1286         }
1287         dest[j * srcWidth + i][RCOMP] = sumR;
1288         dest[j * srcWidth + i][GCOMP] = sumG;
1289         dest[j * srcWidth + i][BCOMP] = sumB;
1290         dest[j * srcWidth + i][ACOMP] = sumA;
1291      }
1292   }
1293}
1294
1295
1296
1297void
1298_mesa_convolve_1d_image(const GLcontext *ctx, GLsizei *width,
1299                        const GLfloat *srcImage, GLfloat *dstImage)
1300{
1301   switch (ctx->Pixel.ConvolutionBorderMode[0]) {
1302      case GL_REDUCE:
1303         convolve_1d_reduce(*width, (const GLfloat (*)[4]) srcImage,
1304                            ctx->Convolution1D.Width,
1305                            (const GLfloat (*)[4]) ctx->Convolution1D.Filter,
1306                            (GLfloat (*)[4]) dstImage);
1307         *width = *width - (MAX2(ctx->Convolution1D.Width, 1) - 1);
1308         break;
1309      case GL_CONSTANT_BORDER:
1310         convolve_1d_constant(*width, (const GLfloat (*)[4]) srcImage,
1311                              ctx->Convolution1D.Width,
1312                              (const GLfloat (*)[4]) ctx->Convolution1D.Filter,
1313                              (GLfloat (*)[4]) dstImage,
1314                              ctx->Pixel.ConvolutionBorderColor[0]);
1315         break;
1316      case GL_REPLICATE_BORDER:
1317         convolve_1d_replicate(*width, (const GLfloat (*)[4]) srcImage,
1318                              ctx->Convolution1D.Width,
1319                              (const GLfloat (*)[4]) ctx->Convolution1D.Filter,
1320                              (GLfloat (*)[4]) dstImage);
1321         break;
1322      default:
1323         ;
1324   }
1325}
1326
1327
1328void
1329_mesa_convolve_2d_image(const GLcontext *ctx, GLsizei *width, GLsizei *height,
1330                        const GLfloat *srcImage, GLfloat *dstImage)
1331{
1332   switch (ctx->Pixel.ConvolutionBorderMode[1]) {
1333      case GL_REDUCE:
1334         convolve_2d_reduce(*width, *height,
1335                            (const GLfloat (*)[4]) srcImage,
1336                            ctx->Convolution2D.Width,
1337                            ctx->Convolution2D.Height,
1338                            (const GLfloat (*)[4]) ctx->Convolution2D.Filter,
1339                            (GLfloat (*)[4]) dstImage);
1340         *width = *width - (MAX2(ctx->Convolution2D.Width, 1) - 1);
1341         *height = *height - (MAX2(ctx->Convolution2D.Height, 1) - 1);
1342         break;
1343      case GL_CONSTANT_BORDER:
1344         convolve_2d_constant(*width, *height,
1345                              (const GLfloat (*)[4]) srcImage,
1346                              ctx->Convolution2D.Width,
1347                              ctx->Convolution2D.Height,
1348                              (const GLfloat (*)[4]) ctx->Convolution2D.Filter,
1349                              (GLfloat (*)[4]) dstImage,
1350                              ctx->Pixel.ConvolutionBorderColor[1]);
1351         break;
1352      case GL_REPLICATE_BORDER:
1353         convolve_2d_replicate(*width, *height,
1354                               (const GLfloat (*)[4]) srcImage,
1355                               ctx->Convolution2D.Width,
1356                               ctx->Convolution2D.Height,
1357                               (const GLfloat (*)[4])ctx->Convolution2D.Filter,
1358                               (GLfloat (*)[4]) dstImage);
1359         break;
1360      default:
1361         ;
1362      }
1363}
1364
1365
1366void
1367_mesa_convolve_sep_image(const GLcontext *ctx,
1368                         GLsizei *width, GLsizei *height,
1369                         const GLfloat *srcImage, GLfloat *dstImage)
1370{
1371   const GLfloat *rowFilter = ctx->Separable2D.Filter;
1372   const GLfloat *colFilter = rowFilter + 4 * MAX_CONVOLUTION_WIDTH;
1373
1374   switch (ctx->Pixel.ConvolutionBorderMode[2]) {
1375      case GL_REDUCE:
1376         convolve_sep_reduce(*width, *height,
1377                             (const GLfloat (*)[4]) srcImage,
1378                             ctx->Separable2D.Width,
1379                             ctx->Separable2D.Height,
1380                             (const GLfloat (*)[4]) rowFilter,
1381                             (const GLfloat (*)[4]) colFilter,
1382                             (GLfloat (*)[4]) dstImage);
1383         *width = *width - (MAX2(ctx->Separable2D.Width, 1) - 1);
1384         *height = *height - (MAX2(ctx->Separable2D.Height, 1) - 1);
1385         break;
1386      case GL_CONSTANT_BORDER:
1387         convolve_sep_constant(*width, *height,
1388                               (const GLfloat (*)[4]) srcImage,
1389                               ctx->Separable2D.Width,
1390                               ctx->Separable2D.Height,
1391                               (const GLfloat (*)[4]) rowFilter,
1392                               (const GLfloat (*)[4]) colFilter,
1393                               (GLfloat (*)[4]) dstImage,
1394                               ctx->Pixel.ConvolutionBorderColor[2]);
1395         break;
1396      case GL_REPLICATE_BORDER:
1397         convolve_sep_replicate(*width, *height,
1398                                (const GLfloat (*)[4]) srcImage,
1399                                ctx->Separable2D.Width,
1400                                ctx->Separable2D.Height,
1401                                (const GLfloat (*)[4]) rowFilter,
1402                                (const GLfloat (*)[4]) colFilter,
1403                                (GLfloat (*)[4]) dstImage);
1404         break;
1405      default:
1406         ;
1407   }
1408}
1409
1410
1411
1412/*
1413 * This function computes an image's size after convolution.
1414 * If the convolution border mode is GL_REDUCE, the post-convolution
1415 * image will be smaller than the original.
1416 */
1417void
1418_mesa_adjust_image_for_convolution(const GLcontext *ctx, GLuint dimensions,
1419                                   GLsizei *width, GLsizei *height)
1420{
1421   if (ctx->Pixel.Convolution1DEnabled
1422       && dimensions == 1
1423       && ctx->Pixel.ConvolutionBorderMode[0] == GL_REDUCE) {
1424      *width = *width - (MAX2(ctx->Convolution1D.Width, 1) - 1);
1425   }
1426   else if (ctx->Pixel.Convolution2DEnabled
1427            && dimensions > 1
1428            && ctx->Pixel.ConvolutionBorderMode[1] == GL_REDUCE) {
1429      *width = *width - (MAX2(ctx->Convolution2D.Width, 1) - 1);
1430      *height = *height - (MAX2(ctx->Convolution2D.Height, 1) - 1);
1431   }
1432   else if (ctx->Pixel.Separable2DEnabled
1433            && dimensions > 1
1434            && ctx->Pixel.ConvolutionBorderMode[2] == GL_REDUCE) {
1435      *width = *width - (MAX2(ctx->Separable2D.Width, 1) - 1);
1436      *height = *height - (MAX2(ctx->Separable2D.Height, 1) - 1);
1437   }
1438}
1439
1440
1441void
1442_mesa_init_convolve_dispatch(struct _glapi_table *disp)
1443{
1444   SET_ConvolutionFilter1D(disp, _mesa_ConvolutionFilter1D);
1445   SET_ConvolutionFilter2D(disp, _mesa_ConvolutionFilter2D);
1446   SET_ConvolutionParameterf(disp, _mesa_ConvolutionParameterf);
1447   SET_ConvolutionParameterfv(disp, _mesa_ConvolutionParameterfv);
1448   SET_ConvolutionParameteri(disp, _mesa_ConvolutionParameteri);
1449   SET_ConvolutionParameteriv(disp, _mesa_ConvolutionParameteriv);
1450   SET_CopyConvolutionFilter1D(disp, _mesa_CopyConvolutionFilter1D);
1451   SET_CopyConvolutionFilter2D(disp, _mesa_CopyConvolutionFilter2D);
1452   SET_GetConvolutionFilter(disp, _mesa_GetConvolutionFilter);
1453   SET_GetConvolutionParameterfv(disp, _mesa_GetConvolutionParameterfv);
1454   SET_GetConvolutionParameteriv(disp, _mesa_GetConvolutionParameteriv);
1455   SET_SeparableFilter2D(disp, _mesa_SeparableFilter2D);
1456   SET_GetSeparableFilter(disp, _mesa_GetSeparableFilter);
1457}
1458
1459
1460#endif /* FEATURE_convolve */
1461