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