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