generate GL_INVALID_OPERATION error for bad image format/type combinations
[mesa.git] / src / mesa / main / convolve.c
1 /* $Id: convolve.c,v 1.16 2000/12/10 19:23:19 brianp 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 */
55 static GLint
56 base_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
110 void
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, "glConvolutionFilter1D");
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
183 void
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, "glConvolutionFilter2D");
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
266 void
267 _mesa_ConvolutionParameterf(GLenum target, GLenum pname, GLfloat param)
268 {
269 GET_CURRENT_CONTEXT(ctx);
270 GLuint c;
271
272 ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx, "glConvolutionParameterf");
273
274 switch (target) {
275 case GL_CONVOLUTION_1D:
276 c = 0;
277 break;
278 case GL_CONVOLUTION_2D:
279 c = 1;
280 break;
281 case GL_SEPARABLE_2D:
282 c = 2;
283 break;
284 default:
285 gl_error(ctx, GL_INVALID_ENUM, "glConvolutionParameterf(target)");
286 return;
287 }
288
289 switch (pname) {
290 case GL_CONVOLUTION_BORDER_MODE:
291 if (param == (GLfloat) GL_REDUCE ||
292 param == (GLfloat) GL_CONSTANT_BORDER ||
293 param == (GLfloat) GL_REPLICATE_BORDER) {
294 ctx->Pixel.ConvolutionBorderMode[c] = (GLenum) param;
295 }
296 else {
297 gl_error(ctx, GL_INVALID_ENUM, "glConvolutionParameterf(params)");
298 return;
299 }
300 break;
301 default:
302 gl_error(ctx, GL_INVALID_ENUM, "glConvolutionParameterf(pname)");
303 return;
304 }
305
306 ctx->NewState |= _NEW_PIXEL;
307 }
308
309
310 void
311 _mesa_ConvolutionParameterfv(GLenum target, GLenum pname, const GLfloat *params)
312 {
313 GET_CURRENT_CONTEXT(ctx);
314 struct gl_convolution_attrib *conv;
315 GLuint c;
316
317 ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx, "glConvolutionParameterfv");
318
319 switch (target) {
320 case GL_CONVOLUTION_1D:
321 c = 0;
322 conv = &ctx->Convolution1D;
323 break;
324 case GL_CONVOLUTION_2D:
325 c = 1;
326 conv = &ctx->Convolution2D;
327 break;
328 case GL_SEPARABLE_2D:
329 c = 2;
330 conv = &ctx->Separable2D;
331 break;
332 default:
333 gl_error(ctx, GL_INVALID_ENUM, "glConvolutionParameterfv(target)");
334 return;
335 }
336
337 switch (pname) {
338 case GL_CONVOLUTION_BORDER_COLOR:
339 COPY_4V(ctx->Pixel.ConvolutionBorderColor[c], params);
340 break;
341 case GL_CONVOLUTION_BORDER_MODE:
342 if (params[0] == (GLfloat) GL_REDUCE ||
343 params[0] == (GLfloat) GL_CONSTANT_BORDER ||
344 params[0] == (GLfloat) GL_REPLICATE_BORDER) {
345 ctx->Pixel.ConvolutionBorderMode[c] = (GLenum) params[0];
346 }
347 else {
348 gl_error(ctx, GL_INVALID_ENUM, "glConvolutionParameterfv(params)");
349 return;
350 }
351 break;
352 case GL_CONVOLUTION_FILTER_SCALE:
353 COPY_4V(ctx->Pixel.ConvolutionFilterScale[c], params);
354 break;
355 case GL_CONVOLUTION_FILTER_BIAS:
356 COPY_4V(ctx->Pixel.ConvolutionFilterBias[c], params);
357 break;
358 default:
359 gl_error(ctx, GL_INVALID_ENUM, "glConvolutionParameterfv(pname)");
360 return;
361 }
362
363 ctx->NewState |= _NEW_PIXEL;
364 }
365
366
367 void
368 _mesa_ConvolutionParameteri(GLenum target, GLenum pname, GLint param)
369 {
370 GET_CURRENT_CONTEXT(ctx);
371 GLuint c;
372
373 ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx, "glConvolutionParameteri");
374
375 switch (target) {
376 case GL_CONVOLUTION_1D:
377 c = 0;
378 break;
379 case GL_CONVOLUTION_2D:
380 c = 1;
381 break;
382 case GL_SEPARABLE_2D:
383 c = 2;
384 break;
385 default:
386 gl_error(ctx, GL_INVALID_ENUM, "glConvolutionParameteri(target)");
387 return;
388 }
389
390 switch (pname) {
391 case GL_CONVOLUTION_BORDER_MODE:
392 if (param == (GLint) GL_REDUCE ||
393 param == (GLint) GL_CONSTANT_BORDER ||
394 param == (GLint) GL_REPLICATE_BORDER) {
395 ctx->Pixel.ConvolutionBorderMode[c] = (GLenum) param;
396 }
397 else {
398 gl_error(ctx, GL_INVALID_ENUM, "glConvolutionParameteri(params)");
399 return;
400 }
401 break;
402 default:
403 gl_error(ctx, GL_INVALID_ENUM, "glConvolutionParameteri(pname)");
404 return;
405 }
406
407 ctx->NewState |= _NEW_PIXEL;
408 }
409
410
411 void
412 _mesa_ConvolutionParameteriv(GLenum target, GLenum pname, const GLint *params)
413 {
414 GET_CURRENT_CONTEXT(ctx);
415 struct gl_convolution_attrib *conv;
416 GLuint c;
417
418 ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx, "glConvolutionParameteriv");
419
420 switch (target) {
421 case GL_CONVOLUTION_1D:
422 c = 0;
423 conv = &ctx->Convolution1D;
424 break;
425 case GL_CONVOLUTION_2D:
426 c = 1;
427 conv = &ctx->Convolution2D;
428 break;
429 case GL_SEPARABLE_2D:
430 c = 2;
431 conv = &ctx->Separable2D;
432 break;
433 default:
434 gl_error(ctx, GL_INVALID_ENUM, "glConvolutionParameteriv(target)");
435 return;
436 }
437
438 switch (pname) {
439 case GL_CONVOLUTION_BORDER_COLOR:
440 ctx->Pixel.ConvolutionBorderColor[c][0] = INT_TO_FLOAT(params[0]);
441 ctx->Pixel.ConvolutionBorderColor[c][1] = INT_TO_FLOAT(params[1]);
442 ctx->Pixel.ConvolutionBorderColor[c][2] = INT_TO_FLOAT(params[2]);
443 ctx->Pixel.ConvolutionBorderColor[c][3] = INT_TO_FLOAT(params[3]);
444 break;
445 case GL_CONVOLUTION_BORDER_MODE:
446 if (params[0] == (GLint) GL_REDUCE ||
447 params[0] == (GLint) GL_CONSTANT_BORDER ||
448 params[0] == (GLint) GL_REPLICATE_BORDER) {
449 ctx->Pixel.ConvolutionBorderMode[c] = (GLenum) params[0];
450 }
451 else {
452 gl_error(ctx, GL_INVALID_ENUM, "glConvolutionParameteriv(params)");
453 return;
454 }
455 break;
456 case GL_CONVOLUTION_FILTER_SCALE:
457 COPY_4V(ctx->Pixel.ConvolutionFilterScale[c], params);
458 break;
459 case GL_CONVOLUTION_FILTER_BIAS:
460 COPY_4V(ctx->Pixel.ConvolutionFilterBias[c], params);
461 break;
462 default:
463 gl_error(ctx, GL_INVALID_ENUM, "glConvolutionParameteriv(pname)");
464 return;
465 }
466
467 ctx->NewState |= _NEW_PIXEL;
468 }
469
470
471 void
472 _mesa_CopyConvolutionFilter1D(GLenum target, GLenum internalFormat, GLint x, GLint y, GLsizei width)
473 {
474 GLenum baseFormat;
475 GLchan rgba[MAX_CONVOLUTION_WIDTH][4];
476 GET_CURRENT_CONTEXT(ctx);
477 ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx, "glCopyConvolutionFilter1D");
478
479 if (target != GL_CONVOLUTION_1D) {
480 gl_error(ctx, GL_INVALID_ENUM, "glCopyConvolutionFilter1D(target)");
481 return;
482 }
483
484 baseFormat = base_filter_format(internalFormat);
485 if (baseFormat < 0 || baseFormat == GL_COLOR_INDEX) {
486 gl_error(ctx, GL_INVALID_ENUM, "glCopyConvolutionFilter1D(internalFormat)");
487 return;
488 }
489
490 if (width < 0 || width > MAX_CONVOLUTION_WIDTH) {
491 gl_error(ctx, GL_INVALID_VALUE, "glCopyConvolutionFilter1D(width)");
492 return;
493 }
494
495 /* read pixels from framebuffer */
496 RENDER_START(ctx);
497 gl_read_rgba_span(ctx, ctx->ReadBuffer, width, x, y, (GLchan (*)[4]) rgba);
498 RENDER_FINISH(ctx);
499
500 /* store as convolution filter */
501 _mesa_ConvolutionFilter1D(target, internalFormat, width,
502 GL_RGBA, CHAN_TYPE, rgba);
503 }
504
505
506 void
507 _mesa_CopyConvolutionFilter2D(GLenum target, GLenum internalFormat, GLint x, GLint y, GLsizei width, GLsizei height)
508 {
509 GLenum baseFormat;
510 GLint i;
511 struct gl_pixelstore_attrib packSave;
512 GLchan rgba[MAX_CONVOLUTION_HEIGHT][MAX_CONVOLUTION_WIDTH][4];
513 GET_CURRENT_CONTEXT(ctx);
514 ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx, "glCopyConvolutionFilter2D");
515
516 if (target != GL_CONVOLUTION_2D) {
517 gl_error(ctx, GL_INVALID_ENUM, "glCopyConvolutionFilter2D(target)");
518 return;
519 }
520
521 baseFormat = base_filter_format(internalFormat);
522 if (baseFormat < 0 || baseFormat == GL_COLOR_INDEX) {
523 gl_error(ctx, GL_INVALID_ENUM, "glCopyConvolutionFilter2D(internalFormat)");
524 return;
525 }
526
527 if (width < 0 || width > MAX_CONVOLUTION_WIDTH) {
528 gl_error(ctx, GL_INVALID_VALUE, "glCopyConvolutionFilter2D(width)");
529 return;
530 }
531 if (height < 0 || height > MAX_CONVOLUTION_HEIGHT) {
532 gl_error(ctx, GL_INVALID_VALUE, "glCopyConvolutionFilter2D(height)");
533 return;
534 }
535
536 /* read pixels from framebuffer */
537 RENDER_START(ctx);
538 for (i = 0; i < height; i++) {
539 gl_read_rgba_span(ctx, ctx->ReadBuffer, width, x, y + i,
540 (GLchan (*)[4]) rgba[i]);
541 }
542 RENDER_FINISH(ctx);
543
544 /*
545 * store as convolution filter
546 */
547 packSave = ctx->Unpack; /* save pixel packing params */
548
549 ctx->Unpack.Alignment = 1;
550 ctx->Unpack.RowLength = MAX_CONVOLUTION_WIDTH;
551 ctx->Unpack.SkipPixels = 0;
552 ctx->Unpack.SkipRows = 0;
553 ctx->Unpack.ImageHeight = 0;
554 ctx->Unpack.SkipImages = 0;
555 ctx->Unpack.SwapBytes = GL_FALSE;
556 ctx->Unpack.LsbFirst = GL_FALSE;
557 ctx->NewState |= _NEW_PACKUNPACK;
558
559 _mesa_ConvolutionFilter2D(target, internalFormat, width, height,
560 GL_RGBA, CHAN_TYPE, rgba);
561
562 ctx->Unpack = packSave; /* restore pixel packing params */
563 ctx->NewState |= _NEW_PACKUNPACK;
564 }
565
566
567 void
568 _mesa_GetConvolutionFilter(GLenum target, GLenum format, GLenum type, GLvoid *image)
569 {
570 const struct gl_convolution_attrib *filter;
571 GLint row;
572 GET_CURRENT_CONTEXT(ctx);
573 ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx, "glGetConvolutionFilter");
574
575 if (ctx->NewState) {
576 gl_update_state(ctx);
577 }
578
579 if (!_mesa_is_legal_format_and_type(format, type)) {
580 gl_error(ctx, GL_INVALID_OPERATION, "glGetConvolutionFilter(format or type)");
581 return;
582 }
583
584 if (format == GL_COLOR_INDEX ||
585 format == GL_STENCIL_INDEX ||
586 format == GL_DEPTH_COMPONENT ||
587 format == GL_INTENSITY ||
588 type == GL_BITMAP) {
589 gl_error(ctx, GL_INVALID_ENUM, "glGetConvolutionFilter(format or type)");
590 return;
591 }
592
593 switch (target) {
594 case GL_CONVOLUTION_1D:
595 filter = &(ctx->Convolution1D);
596 break;
597 case GL_CONVOLUTION_2D:
598 filter = &(ctx->Convolution2D);
599 break;
600 default:
601 gl_error(ctx, GL_INVALID_ENUM, "glGetConvolutionFilter(target)");
602 return;
603 }
604
605 for (row = 0; row < filter->Height; row++) {
606 GLvoid *dst = _mesa_image_address( &ctx->Pack, image, filter->Width,
607 filter->Height, format, type,
608 0, row, 0);
609 const GLfloat *src = filter->Filter + row * filter->Width * 4;
610 _mesa_pack_float_rgba_span(ctx, filter->Width,
611 (const GLfloat (*)[4]) src,
612 format, type, dst, &ctx->Pack, 0);
613 }
614 }
615
616
617 void
618 _mesa_GetConvolutionParameterfv(GLenum target, GLenum pname, GLfloat *params)
619 {
620 GET_CURRENT_CONTEXT(ctx);
621 const struct gl_convolution_attrib *conv;
622 GLuint c;
623
624 ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx, "glGetConvolutionParameterfv");
625
626 switch (target) {
627 case GL_CONVOLUTION_1D:
628 c = 0;
629 conv = &ctx->Convolution1D;
630 break;
631 case GL_CONVOLUTION_2D:
632 c = 1;
633 conv = &ctx->Convolution2D;
634 break;
635 case GL_SEPARABLE_2D:
636 c = 2;
637 conv = &ctx->Separable2D;
638 break;
639 default:
640 gl_error(ctx, GL_INVALID_ENUM, "glGetConvolutionParameterfv(target)");
641 return;
642 }
643
644 switch (pname) {
645 case GL_CONVOLUTION_BORDER_COLOR:
646 COPY_4V(params, ctx->Pixel.ConvolutionBorderColor[c]);
647 break;
648 case GL_CONVOLUTION_BORDER_MODE:
649 *params = (GLfloat) ctx->Pixel.ConvolutionBorderMode[c];
650 break;
651 case GL_CONVOLUTION_FILTER_SCALE:
652 COPY_4V(params, ctx->Pixel.ConvolutionFilterScale[c]);
653 break;
654 case GL_CONVOLUTION_FILTER_BIAS:
655 COPY_4V(params, ctx->Pixel.ConvolutionFilterBias[c]);
656 break;
657 case GL_CONVOLUTION_FORMAT:
658 *params = (GLfloat) conv->Format;
659 break;
660 case GL_CONVOLUTION_WIDTH:
661 *params = (GLfloat) conv->Width;
662 break;
663 case GL_CONVOLUTION_HEIGHT:
664 *params = (GLfloat) conv->Height;
665 break;
666 case GL_MAX_CONVOLUTION_WIDTH:
667 *params = (GLfloat) ctx->Const.MaxConvolutionWidth;
668 break;
669 case GL_MAX_CONVOLUTION_HEIGHT:
670 *params = (GLfloat) ctx->Const.MaxConvolutionHeight;
671 break;
672 default:
673 gl_error(ctx, GL_INVALID_ENUM, "glGetConvolutionParameterfv(pname)");
674 return;
675 }
676 }
677
678
679 void
680 _mesa_GetConvolutionParameteriv(GLenum target, GLenum pname, GLint *params)
681 {
682 GET_CURRENT_CONTEXT(ctx);
683 const struct gl_convolution_attrib *conv;
684 GLuint c;
685
686 ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx, "glGetConvolutionParameteriv");
687
688 switch (target) {
689 case GL_CONVOLUTION_1D:
690 c = 0;
691 conv = &ctx->Convolution1D;
692 break;
693 case GL_CONVOLUTION_2D:
694 c = 1;
695 conv = &ctx->Convolution2D;
696 break;
697 case GL_SEPARABLE_2D:
698 c = 2;
699 conv = &ctx->Separable2D;
700 break;
701 default:
702 gl_error(ctx, GL_INVALID_ENUM, "glGetConvolutionParameteriv(target)");
703 return;
704 }
705
706 switch (pname) {
707 case GL_CONVOLUTION_BORDER_COLOR:
708 params[0] = FLOAT_TO_INT(ctx->Pixel.ConvolutionBorderColor[c][0]);
709 params[1] = FLOAT_TO_INT(ctx->Pixel.ConvolutionBorderColor[c][1]);
710 params[2] = FLOAT_TO_INT(ctx->Pixel.ConvolutionBorderColor[c][2]);
711 params[3] = FLOAT_TO_INT(ctx->Pixel.ConvolutionBorderColor[c][3]);
712 break;
713 case GL_CONVOLUTION_BORDER_MODE:
714 *params = (GLint) ctx->Pixel.ConvolutionBorderMode[c];
715 break;
716 case GL_CONVOLUTION_FILTER_SCALE:
717 params[0] = (GLint) ctx->Pixel.ConvolutionFilterScale[c][0];
718 params[1] = (GLint) ctx->Pixel.ConvolutionFilterScale[c][1];
719 params[2] = (GLint) ctx->Pixel.ConvolutionFilterScale[c][2];
720 params[3] = (GLint) ctx->Pixel.ConvolutionFilterScale[c][3];
721 break;
722 case GL_CONVOLUTION_FILTER_BIAS:
723 params[0] = (GLint) ctx->Pixel.ConvolutionFilterBias[c][0];
724 params[1] = (GLint) ctx->Pixel.ConvolutionFilterBias[c][1];
725 params[2] = (GLint) ctx->Pixel.ConvolutionFilterBias[c][2];
726 params[3] = (GLint) ctx->Pixel.ConvolutionFilterBias[c][3];
727 break;
728 case GL_CONVOLUTION_FORMAT:
729 *params = (GLint) conv->Format;
730 break;
731 case GL_CONVOLUTION_WIDTH:
732 *params = (GLint) conv->Width;
733 break;
734 case GL_CONVOLUTION_HEIGHT:
735 *params = (GLint) conv->Height;
736 break;
737 case GL_MAX_CONVOLUTION_WIDTH:
738 *params = (GLint) ctx->Const.MaxConvolutionWidth;
739 break;
740 case GL_MAX_CONVOLUTION_HEIGHT:
741 *params = (GLint) ctx->Const.MaxConvolutionHeight;
742 break;
743 default:
744 gl_error(ctx, GL_INVALID_ENUM, "glGetConvolutionParameteriv(pname)");
745 return;
746 }
747 }
748
749
750 void
751 _mesa_GetSeparableFilter(GLenum target, GLenum format, GLenum type, GLvoid *row, GLvoid *column, GLvoid *span)
752 {
753 const GLint colStart = MAX_CONVOLUTION_WIDTH * 4;
754 const struct gl_convolution_attrib *filter;
755 GET_CURRENT_CONTEXT(ctx);
756 ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx, "glGetSeparableFilter");
757
758 if (ctx->NewState) {
759 gl_update_state(ctx);
760 }
761
762 if (target != GL_SEPARABLE_2D) {
763 gl_error(ctx, GL_INVALID_ENUM, "glGetSeparableFilter(target)");
764 return;
765 }
766
767 if (!_mesa_is_legal_format_and_type(format, type)) {
768 gl_error(ctx, GL_INVALID_OPERATION, "glGetConvolutionFilter(format or type)");
769 return;
770 }
771
772 if (format == GL_COLOR_INDEX ||
773 format == GL_STENCIL_INDEX ||
774 format == GL_DEPTH_COMPONENT ||
775 format == GL_INTENSITY ||
776 type == GL_BITMAP) {
777 gl_error(ctx, GL_INVALID_ENUM, "glGetConvolutionFilter(format or type)");
778 return;
779 }
780
781 filter = &ctx->Separable2D;
782
783 /* Row filter */
784 {
785 GLvoid *dst = _mesa_image_address( &ctx->Pack, row, filter->Width,
786 filter->Height, format, type,
787 0, 0, 0);
788 _mesa_pack_float_rgba_span(ctx, filter->Width,
789 (const GLfloat (*)[4]) filter->Filter,
790 format, type, dst, &ctx->Pack, 0);
791 }
792
793 /* Column filter */
794 {
795 GLvoid *dst = _mesa_image_address( &ctx->Pack, column, filter->Width,
796 1, format, type,
797 0, 0, 0);
798 const GLfloat *src = filter->Filter + colStart;
799 _mesa_pack_float_rgba_span(ctx, filter->Height,
800 (const GLfloat (*)[4]) src,
801 format, type, dst, &ctx->Pack, 0);
802 }
803
804 (void) span; /* unused at this time */
805 }
806
807
808 void
809 _mesa_SeparableFilter2D(GLenum target, GLenum internalFormat, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *row, const GLvoid *column)
810 {
811 const GLint colStart = MAX_CONVOLUTION_WIDTH * 4;
812 GLenum baseFormat;
813 GET_CURRENT_CONTEXT(ctx);
814 ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx, "glSeparableFilter2D");
815
816 if (target != GL_SEPARABLE_2D) {
817 gl_error(ctx, GL_INVALID_ENUM, "glSeparableFilter2D(target)");
818 return;
819 }
820
821 baseFormat = base_filter_format(internalFormat);
822 if (baseFormat < 0 || baseFormat == GL_COLOR_INDEX) {
823 gl_error(ctx, GL_INVALID_ENUM, "glSeparableFilter2D(internalFormat)");
824 return;
825 }
826
827 if (width < 0 || width > MAX_CONVOLUTION_WIDTH) {
828 gl_error(ctx, GL_INVALID_VALUE, "glSeparableFilter2D(width)");
829 return;
830 }
831 if (height < 0 || height > MAX_CONVOLUTION_HEIGHT) {
832 gl_error(ctx, GL_INVALID_VALUE, "glSeparableFilter2D(height)");
833 return;
834 }
835
836 if (!_mesa_is_legal_format_and_type(format, type)) {
837 gl_error(ctx, GL_INVALID_OPERATION, "glSeparableFilter2D(format or type)");
838 return;
839 }
840
841 if (format == GL_COLOR_INDEX ||
842 format == GL_STENCIL_INDEX ||
843 format == GL_DEPTH_COMPONENT ||
844 format == GL_INTENSITY ||
845 type == GL_BITMAP) {
846 gl_error(ctx, GL_INVALID_ENUM, "glSeparableFilter2D(format or type)");
847 return;
848 }
849
850 ctx->Separable2D.Format = format;
851 ctx->Separable2D.InternalFormat = internalFormat;
852 ctx->Separable2D.Width = width;
853 ctx->Separable2D.Height = height;
854
855 /* unpack row filter */
856 _mesa_unpack_float_color_span(ctx, width, GL_RGBA,
857 ctx->Separable2D.Filter,
858 format, type, row, &ctx->Unpack,
859 0, GL_FALSE);
860
861 /* apply scale and bias */
862 {
863 const GLfloat *scale = ctx->Pixel.ConvolutionFilterScale[2];
864 const GLfloat *bias = ctx->Pixel.ConvolutionFilterBias[2];
865 GLint i;
866 for (i = 0; i < width; i++) {
867 GLfloat r = ctx->Separable2D.Filter[i * 4 + 0];
868 GLfloat g = ctx->Separable2D.Filter[i * 4 + 1];
869 GLfloat b = ctx->Separable2D.Filter[i * 4 + 2];
870 GLfloat a = ctx->Separable2D.Filter[i * 4 + 3];
871 r = r * scale[0] + bias[0];
872 g = g * scale[1] + bias[1];
873 b = b * scale[2] + bias[2];
874 a = a * scale[3] + bias[3];
875 ctx->Separable2D.Filter[i * 4 + 0] = r;
876 ctx->Separable2D.Filter[i * 4 + 1] = g;
877 ctx->Separable2D.Filter[i * 4 + 2] = b;
878 ctx->Separable2D.Filter[i * 4 + 3] = a;
879 }
880 }
881
882 /* unpack column filter */
883 _mesa_unpack_float_color_span(ctx, width, GL_RGBA,
884 &ctx->Separable2D.Filter[colStart],
885 format, type, column, &ctx->Unpack,
886 0, GL_FALSE);
887
888 /* apply scale and bias */
889 {
890 const GLfloat *scale = ctx->Pixel.ConvolutionFilterScale[2];
891 const GLfloat *bias = ctx->Pixel.ConvolutionFilterBias[2];
892 GLint i;
893 for (i = 0; i < width; i++) {
894 GLfloat r = ctx->Separable2D.Filter[i * 4 + 0 + colStart];
895 GLfloat g = ctx->Separable2D.Filter[i * 4 + 1 + colStart];
896 GLfloat b = ctx->Separable2D.Filter[i * 4 + 2 + colStart];
897 GLfloat a = ctx->Separable2D.Filter[i * 4 + 3 + colStart];
898 r = r * scale[0] + bias[0];
899 g = g * scale[1] + bias[1];
900 b = b * scale[2] + bias[2];
901 a = a * scale[3] + bias[3];
902 ctx->Separable2D.Filter[i * 4 + 0 + colStart] = r;
903 ctx->Separable2D.Filter[i * 4 + 1 + colStart] = g;
904 ctx->Separable2D.Filter[i * 4 + 2 + colStart] = b;
905 ctx->Separable2D.Filter[i * 4 + 3 + colStart] = a;
906 }
907 }
908
909 ctx->NewState |= _NEW_PIXEL;
910 }
911
912
913 /**********************************************************************/
914 /*** image convolution functions ***/
915 /**********************************************************************/
916
917 static void
918 convolve_1d_reduce(GLint srcWidth, const GLfloat src[][4],
919 GLint filterWidth, const GLfloat filter[][4],
920 GLfloat dest[][4])
921 {
922 GLint dstWidth;
923 GLint i, n;
924
925 if (filterWidth >= 1)
926 dstWidth = srcWidth - (filterWidth - 1);
927 else
928 dstWidth = srcWidth;
929
930 if (dstWidth <= 0)
931 return; /* null result */
932
933 for (i = 0; i < dstWidth; i++) {
934 GLfloat sumR = 0.0;
935 GLfloat sumG = 0.0;
936 GLfloat sumB = 0.0;
937 GLfloat sumA = 0.0;
938 for (n = 0; n < filterWidth; n++) {
939 sumR += src[i + n][RCOMP] * filter[n][RCOMP];
940 sumG += src[i + n][GCOMP] * filter[n][GCOMP];
941 sumB += src[i + n][BCOMP] * filter[n][BCOMP];
942 sumA += src[i + n][ACOMP] * filter[n][ACOMP];
943 }
944 dest[i][RCOMP] = sumR;
945 dest[i][GCOMP] = sumG;
946 dest[i][BCOMP] = sumB;
947 dest[i][ACOMP] = sumA;
948 }
949 }
950
951
952 static void
953 convolve_1d_constant(GLint srcWidth, const GLfloat src[][4],
954 GLint filterWidth, const GLfloat filter[][4],
955 GLfloat dest[][4],
956 const GLfloat borderColor[4])
957 {
958 const GLint halfFilterWidth = filterWidth / 2;
959 GLint i, n;
960
961 for (i = 0; i < srcWidth; i++) {
962 GLfloat sumR = 0.0;
963 GLfloat sumG = 0.0;
964 GLfloat sumB = 0.0;
965 GLfloat sumA = 0.0;
966 for (n = 0; n < filterWidth; n++) {
967 if (i + n < halfFilterWidth || i + n - halfFilterWidth >= srcWidth) {
968 sumR += borderColor[RCOMP] * filter[n][RCOMP];
969 sumG += borderColor[GCOMP] * filter[n][GCOMP];
970 sumB += borderColor[BCOMP] * filter[n][BCOMP];
971 sumA += borderColor[ACOMP] * filter[n][ACOMP];
972 }
973 else {
974 sumR += src[i + n - halfFilterWidth][RCOMP] * filter[n][RCOMP];
975 sumG += src[i + n - halfFilterWidth][GCOMP] * filter[n][GCOMP];
976 sumB += src[i + n - halfFilterWidth][BCOMP] * filter[n][BCOMP];
977 sumA += src[i + n - halfFilterWidth][ACOMP] * filter[n][ACOMP];
978 }
979 }
980 dest[i][RCOMP] = sumR;
981 dest[i][GCOMP] = sumG;
982 dest[i][BCOMP] = sumB;
983 dest[i][ACOMP] = sumA;
984 }
985 }
986
987
988 static void
989 convolve_1d_replicate(GLint srcWidth, const GLfloat src[][4],
990 GLint filterWidth, const GLfloat filter[][4],
991 GLfloat dest[][4])
992 {
993 const GLint halfFilterWidth = filterWidth / 2;
994 GLint i, n;
995
996 for (i = 0; i < srcWidth; i++) {
997 GLfloat sumR = 0.0;
998 GLfloat sumG = 0.0;
999 GLfloat sumB = 0.0;
1000 GLfloat sumA = 0.0;
1001 for (n = 0; n < filterWidth; n++) {
1002 if (i + n < halfFilterWidth) {
1003 sumR += src[0][RCOMP] * filter[n][RCOMP];
1004 sumG += src[0][GCOMP] * filter[n][GCOMP];
1005 sumB += src[0][BCOMP] * filter[n][BCOMP];
1006 sumA += src[0][ACOMP] * filter[n][ACOMP];
1007 }
1008 else if (i + n - halfFilterWidth >= srcWidth) {
1009 sumR += src[srcWidth - 1][RCOMP] * filter[n][RCOMP];
1010 sumG += src[srcWidth - 1][GCOMP] * filter[n][GCOMP];
1011 sumB += src[srcWidth - 1][BCOMP] * filter[n][BCOMP];
1012 sumA += src[srcWidth - 1][ACOMP] * filter[n][ACOMP];
1013 }
1014 else {
1015 sumR += src[i + n - halfFilterWidth][RCOMP] * filter[n][RCOMP];
1016 sumG += src[i + n - halfFilterWidth][GCOMP] * filter[n][GCOMP];
1017 sumB += src[i + n - halfFilterWidth][BCOMP] * filter[n][BCOMP];
1018 sumA += src[i + n - halfFilterWidth][ACOMP] * filter[n][ACOMP];
1019 }
1020 }
1021 dest[i][RCOMP] = sumR;
1022 dest[i][GCOMP] = sumG;
1023 dest[i][BCOMP] = sumB;
1024 dest[i][ACOMP] = sumA;
1025 }
1026 }
1027
1028
1029 static void
1030 convolve_2d_reduce(GLint srcWidth, GLint srcHeight,
1031 const GLfloat src[][4],
1032 GLint filterWidth, GLint filterHeight,
1033 const GLfloat filter[][4],
1034 GLfloat dest[][4])
1035 {
1036 GLint dstWidth, dstHeight;
1037 GLint i, j, n, m;
1038
1039 if (filterWidth >= 1)
1040 dstWidth = srcWidth - (filterWidth - 1);
1041 else
1042 dstWidth = srcWidth;
1043
1044 if (filterHeight >= 1)
1045 dstHeight = srcHeight - (filterHeight - 1);
1046 else
1047 dstHeight = srcHeight;
1048
1049 if (dstWidth <= 0 || dstHeight <= 0)
1050 return;
1051
1052 for (j = 0; j < dstHeight; j++) {
1053 for (i = 0; i < dstWidth; 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 k = (j + m) * srcWidth + i + n;
1061 const GLint f = m * filterWidth + n;
1062 sumR += src[k][RCOMP] * filter[f][RCOMP];
1063 sumG += src[k][GCOMP] * filter[f][GCOMP];
1064 sumB += src[k][BCOMP] * filter[f][BCOMP];
1065 sumA += src[k][ACOMP] * filter[f][ACOMP];
1066 }
1067 }
1068 dest[j * dstWidth + i][RCOMP] = sumR;
1069 dest[j * dstWidth + i][GCOMP] = sumG;
1070 dest[j * dstWidth + i][BCOMP] = sumB;
1071 dest[j * dstWidth + i][ACOMP] = sumA;
1072 }
1073 }
1074 }
1075
1076
1077 static void
1078 convolve_2d_constant(GLint srcWidth, GLint srcHeight,
1079 const GLfloat src[][4],
1080 GLint filterWidth, GLint filterHeight,
1081 const GLfloat filter[][4],
1082 GLfloat dest[][4],
1083 const GLfloat borderColor[4])
1084 {
1085 const GLint halfFilterWidth = filterWidth / 2;
1086 const GLint halfFilterHeight = filterHeight / 2;
1087 GLint i, j, n, m;
1088
1089 for (j = 0; j < srcHeight; j++) {
1090 for (i = 0; i < srcWidth; i++) {
1091 GLfloat sumR = 0.0;
1092 GLfloat sumG = 0.0;
1093 GLfloat sumB = 0.0;
1094 GLfloat sumA = 0.0;
1095 for (m = 0; m < filterHeight; m++) {
1096 for (n = 0; n < filterWidth; n++) {
1097 const GLint f = m * filterWidth + n;
1098 const GLint is = i + n - halfFilterWidth;
1099 const GLint js = j + m - halfFilterHeight;
1100 if (is < 0 || is >= srcWidth ||
1101 js < 0 || js >= srcHeight) {
1102 sumR += borderColor[RCOMP] * filter[f][RCOMP];
1103 sumG += borderColor[GCOMP] * filter[f][GCOMP];
1104 sumB += borderColor[BCOMP] * filter[f][BCOMP];
1105 sumA += borderColor[ACOMP] * filter[f][ACOMP];
1106 }
1107 else {
1108 const GLint k = js * srcWidth + is;
1109 sumR += src[k][RCOMP] * filter[f][RCOMP];
1110 sumG += src[k][GCOMP] * filter[f][GCOMP];
1111 sumB += src[k][BCOMP] * filter[f][BCOMP];
1112 sumA += src[k][ACOMP] * filter[f][ACOMP];
1113 }
1114 }
1115 }
1116 dest[j * srcWidth + i][RCOMP] = sumR;
1117 dest[j * srcWidth + i][GCOMP] = sumG;
1118 dest[j * srcWidth + i][BCOMP] = sumB;
1119 dest[j * srcWidth + i][ACOMP] = sumA;
1120 }
1121 }
1122 }
1123
1124
1125 static void
1126 convolve_2d_replicate(GLint srcWidth, GLint srcHeight,
1127 const GLfloat src[][4],
1128 GLint filterWidth, GLint filterHeight,
1129 const GLfloat filter[][4],
1130 GLfloat dest[][4])
1131 {
1132 const GLint halfFilterWidth = filterWidth / 2;
1133 const GLint halfFilterHeight = filterHeight / 2;
1134 GLint i, j, n, m;
1135
1136 for (j = 0; j < srcHeight; j++) {
1137 for (i = 0; i < srcWidth; i++) {
1138 GLfloat sumR = 0.0;
1139 GLfloat sumG = 0.0;
1140 GLfloat sumB = 0.0;
1141 GLfloat sumA = 0.0;
1142 for (m = 0; m < filterHeight; m++) {
1143 for (n = 0; n < filterWidth; n++) {
1144 const GLint f = m * filterWidth + n;
1145 GLint is = i + n - halfFilterWidth;
1146 GLint js = j + m - halfFilterHeight;
1147 GLint k;
1148 if (is < 0)
1149 is = 0;
1150 else if (is >= srcWidth)
1151 is = srcWidth - 1;
1152 if (js < 0)
1153 js = 0;
1154 else if (js >= srcHeight)
1155 js = srcHeight - 1;
1156 k = js * srcWidth + is;
1157 sumR += src[k][RCOMP] * filter[f][RCOMP];
1158 sumG += src[k][GCOMP] * filter[f][GCOMP];
1159 sumB += src[k][BCOMP] * filter[f][BCOMP];
1160 sumA += src[k][ACOMP] * filter[f][ACOMP];
1161 }
1162 }
1163 dest[j * srcWidth + i][RCOMP] = sumR;
1164 dest[j * srcWidth + i][GCOMP] = sumG;
1165 dest[j * srcWidth + i][BCOMP] = sumB;
1166 dest[j * srcWidth + i][ACOMP] = sumA;
1167 }
1168 }
1169 }
1170
1171
1172 static void
1173 convolve_sep_reduce(GLint srcWidth, GLint srcHeight,
1174 const GLfloat src[][4],
1175 GLint filterWidth, GLint filterHeight,
1176 const GLfloat rowFilt[][4],
1177 const GLfloat colFilt[][4],
1178 GLfloat dest[][4])
1179 {
1180 GLint dstWidth, dstHeight;
1181 GLint i, j, n, m;
1182
1183 if (filterWidth >= 1)
1184 dstWidth = srcWidth - (filterWidth - 1);
1185 else
1186 dstWidth = srcWidth;
1187
1188 if (filterHeight >= 1)
1189 dstHeight = srcHeight - (filterHeight - 1);
1190 else
1191 dstHeight = srcHeight;
1192
1193 if (dstWidth <= 0 || dstHeight <= 0)
1194 return;
1195
1196 for (j = 0; j < dstHeight; j++) {
1197 for (i = 0; i < dstWidth; 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 GLint k = (j + m) * srcWidth + i + n;
1205 sumR += src[k][RCOMP] * rowFilt[n][RCOMP] * colFilt[m][RCOMP];
1206 sumG += src[k][GCOMP] * rowFilt[n][GCOMP] * colFilt[m][GCOMP];
1207 sumB += src[k][BCOMP] * rowFilt[n][BCOMP] * colFilt[m][BCOMP];
1208 sumA += src[k][ACOMP] * rowFilt[n][ACOMP] * colFilt[m][ACOMP];
1209 }
1210 }
1211 dest[j * dstWidth + i][RCOMP] = sumR;
1212 dest[j * dstWidth + i][GCOMP] = sumG;
1213 dest[j * dstWidth + i][BCOMP] = sumB;
1214 dest[j * dstWidth + i][ACOMP] = sumA;
1215 }
1216 }
1217 }
1218
1219
1220 static void
1221 convolve_sep_constant(GLint srcWidth, GLint srcHeight,
1222 const GLfloat src[][4],
1223 GLint filterWidth, GLint filterHeight,
1224 const GLfloat rowFilt[][4],
1225 const GLfloat colFilt[][4],
1226 GLfloat dest[][4],
1227 const GLfloat borderColor[4])
1228 {
1229 const GLint halfFilterWidth = filterWidth / 2;
1230 const GLint halfFilterHeight = filterHeight / 2;
1231 GLint i, j, n, m;
1232
1233 for (j = 0; j < srcHeight; j++) {
1234 for (i = 0; i < srcWidth; i++) {
1235 GLfloat sumR = 0.0;
1236 GLfloat sumG = 0.0;
1237 GLfloat sumB = 0.0;
1238 GLfloat sumA = 0.0;
1239 for (m = 0; m < filterHeight; m++) {
1240 for (n = 0; n < filterWidth; n++) {
1241 const GLint is = i + n - halfFilterWidth;
1242 const GLint js = j + m - halfFilterHeight;
1243 if (is < 0 || is >= srcWidth ||
1244 js < 0 || js >= srcHeight) {
1245 sumR += borderColor[RCOMP] * rowFilt[n][RCOMP] * colFilt[m][RCOMP];
1246 sumG += borderColor[GCOMP] * rowFilt[n][GCOMP] * colFilt[m][GCOMP];
1247 sumB += borderColor[BCOMP] * rowFilt[n][BCOMP] * colFilt[m][BCOMP];
1248 sumA += borderColor[ACOMP] * rowFilt[n][ACOMP] * colFilt[m][ACOMP];
1249 }
1250 else {
1251 GLint k = js * srcWidth + is;
1252 sumR += src[k][RCOMP] * rowFilt[n][RCOMP] * colFilt[m][RCOMP];
1253 sumG += src[k][GCOMP] * rowFilt[n][GCOMP] * colFilt[m][GCOMP];
1254 sumB += src[k][BCOMP] * rowFilt[n][BCOMP] * colFilt[m][BCOMP];
1255 sumA += src[k][ACOMP] * rowFilt[n][ACOMP] * colFilt[m][ACOMP];
1256 }
1257
1258 }
1259 }
1260 dest[j * srcWidth + i][RCOMP] = sumR;
1261 dest[j * srcWidth + i][GCOMP] = sumG;
1262 dest[j * srcWidth + i][BCOMP] = sumB;
1263 dest[j * srcWidth + i][ACOMP] = sumA;
1264 }
1265 }
1266 }
1267
1268
1269 static void
1270 convolve_sep_replicate(GLint srcWidth, GLint srcHeight,
1271 const GLfloat src[][4],
1272 GLint filterWidth, GLint filterHeight,
1273 const GLfloat rowFilt[][4],
1274 const GLfloat colFilt[][4],
1275 GLfloat dest[][4])
1276 {
1277 const GLint halfFilterWidth = filterWidth / 2;
1278 const GLint halfFilterHeight = filterHeight / 2;
1279 GLint i, j, n, m;
1280
1281 for (j = 0; j < srcHeight; j++) {
1282 for (i = 0; i < srcWidth; i++) {
1283 GLfloat sumR = 0.0;
1284 GLfloat sumG = 0.0;
1285 GLfloat sumB = 0.0;
1286 GLfloat sumA = 0.0;
1287 for (m = 0; m < filterHeight; m++) {
1288 for (n = 0; n < filterWidth; n++) {
1289 GLint is = i + n - halfFilterWidth;
1290 GLint js = j + m - halfFilterHeight;
1291 GLint k;
1292 if (is < 0)
1293 is = 0;
1294 else if (is >= srcWidth)
1295 is = srcWidth - 1;
1296 if (js < 0)
1297 js = 0;
1298 else if (js >= srcHeight)
1299 js = srcHeight - 1;
1300 k = js * srcWidth + is;
1301 sumR += src[k][RCOMP] * rowFilt[n][RCOMP] * colFilt[m][RCOMP];
1302 sumG += src[k][GCOMP] * rowFilt[n][GCOMP] * colFilt[m][GCOMP];
1303 sumB += src[k][BCOMP] * rowFilt[n][BCOMP] * colFilt[m][BCOMP];
1304 sumA += src[k][ACOMP] * rowFilt[n][ACOMP] * colFilt[m][ACOMP];
1305 }
1306 }
1307 dest[j * srcWidth + i][RCOMP] = sumR;
1308 dest[j * srcWidth + i][GCOMP] = sumG;
1309 dest[j * srcWidth + i][BCOMP] = sumB;
1310 dest[j * srcWidth + i][ACOMP] = sumA;
1311 }
1312 }
1313 }
1314
1315
1316
1317 void
1318 _mesa_convolve_1d_image(const GLcontext *ctx, GLsizei *width,
1319 const GLfloat *srcImage, GLfloat *dstImage)
1320 {
1321 switch (ctx->Pixel.ConvolutionBorderMode[0]) {
1322 case GL_REDUCE:
1323 convolve_1d_reduce(*width, (const GLfloat (*)[4]) srcImage,
1324 ctx->Convolution1D.Width,
1325 (const GLfloat (*)[4]) ctx->Convolution1D.Filter,
1326 (GLfloat (*)[4]) dstImage);
1327 *width = *width - (MAX2(ctx->Convolution1D.Width, 1) - 1);
1328 break;
1329 case GL_CONSTANT_BORDER:
1330 convolve_1d_constant(*width, (const GLfloat (*)[4]) srcImage,
1331 ctx->Convolution1D.Width,
1332 (const GLfloat (*)[4]) ctx->Convolution1D.Filter,
1333 (GLfloat (*)[4]) dstImage,
1334 ctx->Pixel.ConvolutionBorderColor[0]);
1335 break;
1336 case GL_REPLICATE_BORDER:
1337 convolve_1d_replicate(*width, (const GLfloat (*)[4]) srcImage,
1338 ctx->Convolution1D.Width,
1339 (const GLfloat (*)[4]) ctx->Convolution1D.Filter,
1340 (GLfloat (*)[4]) dstImage);
1341 break;
1342 default:
1343 ;
1344 }
1345 }
1346
1347
1348 void
1349 _mesa_convolve_2d_image(const GLcontext *ctx, GLsizei *width, GLsizei *height,
1350 const GLfloat *srcImage, GLfloat *dstImage)
1351 {
1352 switch (ctx->Pixel.ConvolutionBorderMode[1]) {
1353 case GL_REDUCE:
1354 convolve_2d_reduce(*width, *height,
1355 (const GLfloat (*)[4]) srcImage,
1356 ctx->Convolution2D.Width,
1357 ctx->Convolution2D.Height,
1358 (const GLfloat (*)[4]) ctx->Convolution2D.Filter,
1359 (GLfloat (*)[4]) dstImage);
1360 *width = *width - (MAX2(ctx->Convolution2D.Width, 1) - 1);
1361 *height = *height - (MAX2(ctx->Convolution2D.Height, 1) - 1);
1362 break;
1363 case GL_CONSTANT_BORDER:
1364 convolve_2d_constant(*width, *height,
1365 (const GLfloat (*)[4]) srcImage,
1366 ctx->Convolution2D.Width,
1367 ctx->Convolution2D.Height,
1368 (const GLfloat (*)[4]) ctx->Convolution2D.Filter,
1369 (GLfloat (*)[4]) dstImage,
1370 ctx->Pixel.ConvolutionBorderColor[1]);
1371 break;
1372 case GL_REPLICATE_BORDER:
1373 convolve_2d_replicate(*width, *height,
1374 (const GLfloat (*)[4]) srcImage,
1375 ctx->Convolution2D.Width,
1376 ctx->Convolution2D.Height,
1377 (const GLfloat (*)[4])ctx->Convolution2D.Filter,
1378 (GLfloat (*)[4]) dstImage);
1379 break;
1380 default:
1381 ;
1382 }
1383 }
1384
1385
1386 void
1387 _mesa_convolve_sep_image(const GLcontext *ctx,
1388 GLsizei *width, GLsizei *height,
1389 const GLfloat *srcImage, GLfloat *dstImage)
1390 {
1391 const GLfloat *rowFilter = ctx->Separable2D.Filter;
1392 const GLfloat *colFilter = rowFilter + 4 * MAX_CONVOLUTION_WIDTH;
1393
1394 switch (ctx->Pixel.ConvolutionBorderMode[2]) {
1395 case GL_REDUCE:
1396 convolve_sep_reduce(*width, *height,
1397 (const GLfloat (*)[4]) srcImage,
1398 ctx->Separable2D.Width,
1399 ctx->Separable2D.Height,
1400 (const GLfloat (*)[4]) rowFilter,
1401 (const GLfloat (*)[4]) colFilter,
1402 (GLfloat (*)[4]) dstImage);
1403 *width = *width - (MAX2(ctx->Separable2D.Width, 1) - 1);
1404 *height = *height - (MAX2(ctx->Separable2D.Height, 1) - 1);
1405 break;
1406 case GL_CONSTANT_BORDER:
1407 convolve_sep_constant(*width, *height,
1408 (const GLfloat (*)[4]) srcImage,
1409 ctx->Separable2D.Width,
1410 ctx->Separable2D.Height,
1411 (const GLfloat (*)[4]) rowFilter,
1412 (const GLfloat (*)[4]) colFilter,
1413 (GLfloat (*)[4]) dstImage,
1414 ctx->Pixel.ConvolutionBorderColor[2]);
1415 break;
1416 case GL_REPLICATE_BORDER:
1417 convolve_sep_replicate(*width, *height,
1418 (const GLfloat (*)[4]) srcImage,
1419 ctx->Separable2D.Width,
1420 ctx->Separable2D.Height,
1421 (const GLfloat (*)[4]) rowFilter,
1422 (const GLfloat (*)[4]) colFilter,
1423 (GLfloat (*)[4]) dstImage);
1424 break;
1425 default:
1426 ;
1427 }
1428 }