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