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