Merge branch 'mesa_7_6_branch'
[mesa.git] / src / mesa / main / convolve.c
1 /*
2 * Mesa 3-D graphics library
3 * Version: 6.5.2
4 *
5 * Copyright (C) 1999-2006 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 "bufferobj.h"
36 #include "colormac.h"
37 #include "convolve.h"
38 #include "context.h"
39 #include "image.h"
40 #include "mtypes.h"
41 #include "pixel.h"
42 #include "state.h"
43
44
45 /*
46 * Given an internalFormat token passed to glConvolutionFilter
47 * or glSeparableFilter, return the corresponding base format.
48 * Return -1 if invalid token.
49 */
50 static GLint
51 base_filter_format( GLenum format )
52 {
53 switch (format) {
54 case GL_ALPHA:
55 case GL_ALPHA4:
56 case GL_ALPHA8:
57 case GL_ALPHA12:
58 case GL_ALPHA16:
59 return GL_ALPHA;
60 case GL_LUMINANCE:
61 case GL_LUMINANCE4:
62 case GL_LUMINANCE8:
63 case GL_LUMINANCE12:
64 case GL_LUMINANCE16:
65 return GL_LUMINANCE;
66 case GL_LUMINANCE_ALPHA:
67 case GL_LUMINANCE4_ALPHA4:
68 case GL_LUMINANCE6_ALPHA2:
69 case GL_LUMINANCE8_ALPHA8:
70 case GL_LUMINANCE12_ALPHA4:
71 case GL_LUMINANCE12_ALPHA12:
72 case GL_LUMINANCE16_ALPHA16:
73 return GL_LUMINANCE_ALPHA;
74 case GL_INTENSITY:
75 case GL_INTENSITY4:
76 case GL_INTENSITY8:
77 case GL_INTENSITY12:
78 case GL_INTENSITY16:
79 return GL_INTENSITY;
80 case GL_RGB:
81 case GL_R3_G3_B2:
82 case GL_RGB4:
83 case GL_RGB5:
84 case GL_RGB8:
85 case GL_RGB10:
86 case GL_RGB12:
87 case GL_RGB16:
88 return GL_RGB;
89 case 4:
90 case GL_RGBA:
91 case GL_RGBA2:
92 case GL_RGBA4:
93 case GL_RGB5_A1:
94 case GL_RGBA8:
95 case GL_RGB10_A2:
96 case GL_RGBA12:
97 case GL_RGBA16:
98 return GL_RGBA;
99 default:
100 return -1; /* error */
101 }
102 }
103
104
105 void GLAPIENTRY
106 _mesa_ConvolutionFilter1D(GLenum target, GLenum internalFormat, GLsizei width, GLenum format, GLenum type, const GLvoid *image)
107 {
108 GLint baseFormat;
109 GET_CURRENT_CONTEXT(ctx);
110 ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx);
111
112 if (target != GL_CONVOLUTION_1D) {
113 _mesa_error(ctx, GL_INVALID_ENUM, "glConvolutionFilter1D(target)");
114 return;
115 }
116
117 baseFormat = base_filter_format(internalFormat);
118 if (baseFormat < 0 || baseFormat == GL_COLOR_INDEX) {
119 _mesa_error(ctx, GL_INVALID_ENUM, "glConvolutionFilter1D(internalFormat)");
120 return;
121 }
122
123 if (width < 0 || width > MAX_CONVOLUTION_WIDTH) {
124 _mesa_error(ctx, GL_INVALID_VALUE, "glConvolutionFilter1D(width)");
125 return;
126 }
127
128 if (!_mesa_is_legal_format_and_type(ctx, format, type)) {
129 _mesa_error(ctx, GL_INVALID_OPERATION, "glConvolutionFilter1D(format or type)");
130 return;
131 }
132
133 if (format == GL_COLOR_INDEX ||
134 format == GL_STENCIL_INDEX ||
135 format == GL_DEPTH_COMPONENT ||
136 format == GL_INTENSITY ||
137 type == GL_BITMAP) {
138 _mesa_error(ctx, GL_INVALID_ENUM, "glConvolutionFilter1D(format or type)");
139 return;
140 }
141
142 ctx->Convolution1D.Format = format;
143 ctx->Convolution1D.InternalFormat = internalFormat;
144 ctx->Convolution1D.Width = width;
145 ctx->Convolution1D.Height = 1;
146
147 image = _mesa_map_validate_pbo_source(ctx,
148 1, &ctx->Unpack, width, 1, 1,
149 format, type, image,
150 "glConvolutionFilter1D");
151 if (!image)
152 return;
153
154 _mesa_unpack_color_span_float(ctx, width, GL_RGBA,
155 ctx->Convolution1D.Filter,
156 format, type, image, &ctx->Unpack,
157 0); /* transferOps */
158
159 _mesa_unmap_pbo_source(ctx, &ctx->Unpack);
160
161 _mesa_scale_and_bias_rgba(width,
162 (GLfloat (*)[4]) ctx->Convolution1D.Filter,
163 ctx->Pixel.ConvolutionFilterScale[0][0],
164 ctx->Pixel.ConvolutionFilterScale[0][1],
165 ctx->Pixel.ConvolutionFilterScale[0][2],
166 ctx->Pixel.ConvolutionFilterScale[0][3],
167 ctx->Pixel.ConvolutionFilterBias[0][0],
168 ctx->Pixel.ConvolutionFilterBias[0][1],
169 ctx->Pixel.ConvolutionFilterBias[0][2],
170 ctx->Pixel.ConvolutionFilterBias[0][3]);
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(ctx, 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 image = _mesa_map_validate_pbo_source(ctx,
226 2, &ctx->Unpack, width, height, 1,
227 format, type, image,
228 "glConvolutionFilter2D");
229 if (!image)
230 return;
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_address2d(&ctx->Unpack, image, width,
235 height, format, type, i, 0);
236 GLfloat *dst = ctx->Convolution2D.Filter + i * width * 4;
237 _mesa_unpack_color_span_float(ctx, width, GL_RGBA, dst,
238 format, type, src, &ctx->Unpack,
239 0); /* transferOps */
240 }
241
242 _mesa_unmap_pbo_source(ctx, &ctx->Unpack);
243
244 _mesa_scale_and_bias_rgba(width * height,
245 (GLfloat (*)[4]) ctx->Convolution2D.Filter,
246 ctx->Pixel.ConvolutionFilterScale[1][0],
247 ctx->Pixel.ConvolutionFilterScale[1][1],
248 ctx->Pixel.ConvolutionFilterScale[1][2],
249 ctx->Pixel.ConvolutionFilterScale[1][3],
250 ctx->Pixel.ConvolutionFilterBias[1][0],
251 ctx->Pixel.ConvolutionFilterBias[1][1],
252 ctx->Pixel.ConvolutionFilterBias[1][2],
253 ctx->Pixel.ConvolutionFilterBias[1][3]);
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 if (!ctx->ReadBuffer->_ColorReadBuffer) {
486 return; /* no readbuffer - OK */
487 }
488
489 ctx->Driver.CopyConvolutionFilter1D( ctx, target,
490 internalFormat, x, y, width);
491 }
492
493
494 void GLAPIENTRY
495 _mesa_CopyConvolutionFilter2D(GLenum target, GLenum internalFormat, GLint x, GLint y, GLsizei width, GLsizei height)
496 {
497 GLint baseFormat;
498 GET_CURRENT_CONTEXT(ctx);
499 ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx);
500
501 if (target != GL_CONVOLUTION_2D) {
502 _mesa_error(ctx, GL_INVALID_ENUM, "glCopyConvolutionFilter2D(target)");
503 return;
504 }
505
506 baseFormat = base_filter_format(internalFormat);
507 if (baseFormat < 0 || baseFormat == GL_COLOR_INDEX) {
508 _mesa_error(ctx, GL_INVALID_ENUM, "glCopyConvolutionFilter2D(internalFormat)");
509 return;
510 }
511
512 if (width < 0 || width > MAX_CONVOLUTION_WIDTH) {
513 _mesa_error(ctx, GL_INVALID_VALUE, "glCopyConvolutionFilter2D(width)");
514 return;
515 }
516 if (height < 0 || height > MAX_CONVOLUTION_HEIGHT) {
517 _mesa_error(ctx, GL_INVALID_VALUE, "glCopyConvolutionFilter2D(height)");
518 return;
519 }
520
521 if (!ctx->ReadBuffer->_ColorReadBuffer) {
522 return; /* no readbuffer - OK */
523 }
524
525 ctx->Driver.CopyConvolutionFilter2D( ctx, target, internalFormat, x, y,
526 width, height );
527 }
528
529
530 void GLAPIENTRY
531 _mesa_GetConvolutionFilter(GLenum target, GLenum format, GLenum type,
532 GLvoid *image)
533 {
534 struct gl_convolution_attrib *filter;
535 GLuint row;
536 GET_CURRENT_CONTEXT(ctx);
537 ASSERT_OUTSIDE_BEGIN_END(ctx);
538
539 if (ctx->NewState) {
540 _mesa_update_state(ctx);
541 }
542
543 if (!_mesa_is_legal_format_and_type(ctx, format, type)) {
544 _mesa_error(ctx, GL_INVALID_OPERATION, "glGetConvolutionFilter(format or type)");
545 return;
546 }
547
548 if (format == GL_COLOR_INDEX ||
549 format == GL_STENCIL_INDEX ||
550 format == GL_DEPTH_COMPONENT ||
551 format == GL_INTENSITY ||
552 type == GL_BITMAP) {
553 _mesa_error(ctx, GL_INVALID_ENUM, "glGetConvolutionFilter(format or type)");
554 return;
555 }
556
557 switch (target) {
558 case GL_CONVOLUTION_1D:
559 filter = &(ctx->Convolution1D);
560 break;
561 case GL_CONVOLUTION_2D:
562 filter = &(ctx->Convolution2D);
563 break;
564 default:
565 _mesa_error(ctx, GL_INVALID_ENUM, "glGetConvolutionFilter(target)");
566 return;
567 }
568
569 image = _mesa_map_validate_pbo_dest(ctx, 2, &ctx->Pack,
570 filter->Width, filter->Height, 1,
571 format, type, image,
572 "glGetConvolutionFilter");
573 if (!image)
574 return;
575
576 for (row = 0; row < filter->Height; row++) {
577 GLvoid *dst = _mesa_image_address2d(&ctx->Pack, image, filter->Width,
578 filter->Height, format, type,
579 row, 0);
580 GLfloat (*src)[4] = (GLfloat (*)[4]) (filter->Filter + row * filter->Width * 4);
581 _mesa_pack_rgba_span_float(ctx, filter->Width, src,
582 format, type, dst, &ctx->Pack, 0x0);
583 }
584
585 _mesa_unmap_pbo_dest(ctx, &ctx->Pack);
586 }
587
588
589 void GLAPIENTRY
590 _mesa_GetConvolutionParameterfv(GLenum target, GLenum pname, GLfloat *params)
591 {
592 GET_CURRENT_CONTEXT(ctx);
593 const struct gl_convolution_attrib *conv;
594 GLuint c;
595 ASSERT_OUTSIDE_BEGIN_END(ctx);
596
597 switch (target) {
598 case GL_CONVOLUTION_1D:
599 c = 0;
600 conv = &ctx->Convolution1D;
601 break;
602 case GL_CONVOLUTION_2D:
603 c = 1;
604 conv = &ctx->Convolution2D;
605 break;
606 case GL_SEPARABLE_2D:
607 c = 2;
608 conv = &ctx->Separable2D;
609 break;
610 default:
611 _mesa_error(ctx, GL_INVALID_ENUM, "glGetConvolutionParameterfv(target)");
612 return;
613 }
614
615 switch (pname) {
616 case GL_CONVOLUTION_BORDER_COLOR:
617 COPY_4V(params, ctx->Pixel.ConvolutionBorderColor[c]);
618 break;
619 case GL_CONVOLUTION_BORDER_MODE:
620 *params = (GLfloat) ctx->Pixel.ConvolutionBorderMode[c];
621 break;
622 case GL_CONVOLUTION_FILTER_SCALE:
623 COPY_4V(params, ctx->Pixel.ConvolutionFilterScale[c]);
624 break;
625 case GL_CONVOLUTION_FILTER_BIAS:
626 COPY_4V(params, ctx->Pixel.ConvolutionFilterBias[c]);
627 break;
628 case GL_CONVOLUTION_FORMAT:
629 *params = (GLfloat) conv->Format;
630 break;
631 case GL_CONVOLUTION_WIDTH:
632 *params = (GLfloat) conv->Width;
633 break;
634 case GL_CONVOLUTION_HEIGHT:
635 *params = (GLfloat) conv->Height;
636 break;
637 case GL_MAX_CONVOLUTION_WIDTH:
638 *params = (GLfloat) ctx->Const.MaxConvolutionWidth;
639 break;
640 case GL_MAX_CONVOLUTION_HEIGHT:
641 *params = (GLfloat) ctx->Const.MaxConvolutionHeight;
642 break;
643 default:
644 _mesa_error(ctx, GL_INVALID_ENUM, "glGetConvolutionParameterfv(pname)");
645 return;
646 }
647 }
648
649
650 void GLAPIENTRY
651 _mesa_GetConvolutionParameteriv(GLenum target, GLenum pname, GLint *params)
652 {
653 GET_CURRENT_CONTEXT(ctx);
654 const struct gl_convolution_attrib *conv;
655 GLuint c;
656 ASSERT_OUTSIDE_BEGIN_END(ctx);
657
658 switch (target) {
659 case GL_CONVOLUTION_1D:
660 c = 0;
661 conv = &ctx->Convolution1D;
662 break;
663 case GL_CONVOLUTION_2D:
664 c = 1;
665 conv = &ctx->Convolution2D;
666 break;
667 case GL_SEPARABLE_2D:
668 c = 2;
669 conv = &ctx->Separable2D;
670 break;
671 default:
672 _mesa_error(ctx, GL_INVALID_ENUM, "glGetConvolutionParameteriv(target)");
673 return;
674 }
675
676 switch (pname) {
677 case GL_CONVOLUTION_BORDER_COLOR:
678 params[0] = FLOAT_TO_INT(ctx->Pixel.ConvolutionBorderColor[c][0]);
679 params[1] = FLOAT_TO_INT(ctx->Pixel.ConvolutionBorderColor[c][1]);
680 params[2] = FLOAT_TO_INT(ctx->Pixel.ConvolutionBorderColor[c][2]);
681 params[3] = FLOAT_TO_INT(ctx->Pixel.ConvolutionBorderColor[c][3]);
682 break;
683 case GL_CONVOLUTION_BORDER_MODE:
684 *params = (GLint) ctx->Pixel.ConvolutionBorderMode[c];
685 break;
686 case GL_CONVOLUTION_FILTER_SCALE:
687 params[0] = (GLint) ctx->Pixel.ConvolutionFilterScale[c][0];
688 params[1] = (GLint) ctx->Pixel.ConvolutionFilterScale[c][1];
689 params[2] = (GLint) ctx->Pixel.ConvolutionFilterScale[c][2];
690 params[3] = (GLint) ctx->Pixel.ConvolutionFilterScale[c][3];
691 break;
692 case GL_CONVOLUTION_FILTER_BIAS:
693 params[0] = (GLint) ctx->Pixel.ConvolutionFilterBias[c][0];
694 params[1] = (GLint) ctx->Pixel.ConvolutionFilterBias[c][1];
695 params[2] = (GLint) ctx->Pixel.ConvolutionFilterBias[c][2];
696 params[3] = (GLint) ctx->Pixel.ConvolutionFilterBias[c][3];
697 break;
698 case GL_CONVOLUTION_FORMAT:
699 *params = (GLint) conv->Format;
700 break;
701 case GL_CONVOLUTION_WIDTH:
702 *params = (GLint) conv->Width;
703 break;
704 case GL_CONVOLUTION_HEIGHT:
705 *params = (GLint) conv->Height;
706 break;
707 case GL_MAX_CONVOLUTION_WIDTH:
708 *params = (GLint) ctx->Const.MaxConvolutionWidth;
709 break;
710 case GL_MAX_CONVOLUTION_HEIGHT:
711 *params = (GLint) ctx->Const.MaxConvolutionHeight;
712 break;
713 default:
714 _mesa_error(ctx, GL_INVALID_ENUM, "glGetConvolutionParameteriv(pname)");
715 return;
716 }
717 }
718
719
720 void GLAPIENTRY
721 _mesa_GetSeparableFilter(GLenum target, GLenum format, GLenum type,
722 GLvoid *row, GLvoid *column, GLvoid *span)
723 {
724 const GLint colStart = MAX_CONVOLUTION_WIDTH * 4;
725 struct gl_convolution_attrib *filter;
726 GET_CURRENT_CONTEXT(ctx);
727 ASSERT_OUTSIDE_BEGIN_END(ctx);
728
729 if (ctx->NewState) {
730 _mesa_update_state(ctx);
731 }
732
733 if (target != GL_SEPARABLE_2D) {
734 _mesa_error(ctx, GL_INVALID_ENUM, "glGetSeparableFilter(target)");
735 return;
736 }
737
738 if (!_mesa_is_legal_format_and_type(ctx, format, type)) {
739 _mesa_error(ctx, GL_INVALID_OPERATION,
740 "glGetConvolutionFilter(format or type)");
741 return;
742 }
743
744 if (format == GL_COLOR_INDEX ||
745 format == GL_STENCIL_INDEX ||
746 format == GL_DEPTH_COMPONENT ||
747 format == GL_INTENSITY ||
748 type == GL_BITMAP) {
749 _mesa_error(ctx, GL_INVALID_ENUM, "glGetConvolutionFilter(format or type)");
750 return;
751 }
752
753 filter = &ctx->Separable2D;
754
755 /* Get row filter */
756 row = _mesa_map_validate_pbo_dest(ctx, 1, &ctx->Pack,
757 filter->Width, 1, 1,
758 format, type, row,
759 "glGetConvolutionFilter");
760 if (row) {
761 GLvoid *dst = _mesa_image_address1d(&ctx->Pack, row, filter->Width,
762 format, type, 0);
763 _mesa_pack_rgba_span_float(ctx, filter->Width,
764 (GLfloat (*)[4]) filter->Filter,
765 format, type, dst, &ctx->Pack, 0x0);
766 _mesa_unmap_pbo_dest(ctx, &ctx->Pack);
767 }
768
769 /* get column filter */
770 column = _mesa_map_validate_pbo_dest(ctx, 1, &ctx->Pack,
771 filter->Height, 1, 1,
772 format, type, column,
773 "glGetConvolutionFilter");
774 if (column) {
775 GLvoid *dst = _mesa_image_address1d(&ctx->Pack, column, filter->Height,
776 format, type, 0);
777 GLfloat (*src)[4] = (GLfloat (*)[4]) (filter->Filter + colStart);
778 _mesa_pack_rgba_span_float(ctx, filter->Height, src,
779 format, type, dst, &ctx->Pack, 0x0);
780 _mesa_unmap_pbo_dest(ctx, &ctx->Pack);
781 }
782
783 (void) span; /* unused at this time */
784 }
785
786
787 void GLAPIENTRY
788 _mesa_SeparableFilter2D(GLenum target, GLenum internalFormat, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *row, const GLvoid *column)
789 {
790 const GLint colStart = MAX_CONVOLUTION_WIDTH * 4;
791 GLint baseFormat;
792 GET_CURRENT_CONTEXT(ctx);
793 ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx);
794
795 if (target != GL_SEPARABLE_2D) {
796 _mesa_error(ctx, GL_INVALID_ENUM, "glSeparableFilter2D(target)");
797 return;
798 }
799
800 baseFormat = base_filter_format(internalFormat);
801 if (baseFormat < 0 || baseFormat == GL_COLOR_INDEX) {
802 _mesa_error(ctx, GL_INVALID_ENUM, "glSeparableFilter2D(internalFormat)");
803 return;
804 }
805
806 if (width < 0 || width > MAX_CONVOLUTION_WIDTH) {
807 _mesa_error(ctx, GL_INVALID_VALUE, "glSeparableFilter2D(width)");
808 return;
809 }
810 if (height < 0 || height > MAX_CONVOLUTION_HEIGHT) {
811 _mesa_error(ctx, GL_INVALID_VALUE, "glSeparableFilter2D(height)");
812 return;
813 }
814
815 if (!_mesa_is_legal_format_and_type(ctx, format, type)) {
816 _mesa_error(ctx, GL_INVALID_OPERATION, "glSeparableFilter2D(format or type)");
817 return;
818 }
819
820 if (format == GL_COLOR_INDEX ||
821 format == GL_STENCIL_INDEX ||
822 format == GL_DEPTH_COMPONENT ||
823 format == GL_INTENSITY ||
824 type == GL_BITMAP) {
825 _mesa_error(ctx, GL_INVALID_ENUM, "glSeparableFilter2D(format or type)");
826 return;
827 }
828
829 ctx->Separable2D.Format = format;
830 ctx->Separable2D.InternalFormat = internalFormat;
831 ctx->Separable2D.Width = width;
832 ctx->Separable2D.Height = height;
833
834 /* unpack row filter */
835 row = _mesa_map_validate_pbo_source(ctx, 1, &ctx->Unpack,
836 width, 1, 1,
837 format, type, row,
838 "glSeparableFilter2D");
839 if (row) {
840 _mesa_unpack_color_span_float(ctx, width, GL_RGBA,
841 ctx->Separable2D.Filter,
842 format, type, row, &ctx->Unpack,
843 0x0); /* transferOps */
844 _mesa_scale_and_bias_rgba(width,
845 (GLfloat (*)[4]) ctx->Separable2D.Filter,
846 ctx->Pixel.ConvolutionFilterScale[2][0],
847 ctx->Pixel.ConvolutionFilterScale[2][1],
848 ctx->Pixel.ConvolutionFilterScale[2][2],
849 ctx->Pixel.ConvolutionFilterScale[2][3],
850 ctx->Pixel.ConvolutionFilterBias[2][0],
851 ctx->Pixel.ConvolutionFilterBias[2][1],
852 ctx->Pixel.ConvolutionFilterBias[2][2],
853 ctx->Pixel.ConvolutionFilterBias[2][3]);
854 _mesa_unmap_pbo_source(ctx, &ctx->Unpack);
855 }
856
857 /* unpack column filter */
858 column = _mesa_map_validate_pbo_source(ctx, 1, &ctx->Unpack,
859 height, 1, 1,
860 format, type, column,
861 "glSeparableFilter2D");
862 if (column) {
863 _mesa_unpack_color_span_float(ctx, height, GL_RGBA,
864 &ctx->Separable2D.Filter[colStart],
865 format, type, column, &ctx->Unpack,
866 0); /* transferOps */
867
868 _mesa_scale_and_bias_rgba(height,
869 (GLfloat (*)[4]) (ctx->Separable2D.Filter + colStart),
870 ctx->Pixel.ConvolutionFilterScale[2][0],
871 ctx->Pixel.ConvolutionFilterScale[2][1],
872 ctx->Pixel.ConvolutionFilterScale[2][2],
873 ctx->Pixel.ConvolutionFilterScale[2][3],
874 ctx->Pixel.ConvolutionFilterBias[2][0],
875 ctx->Pixel.ConvolutionFilterBias[2][1],
876 ctx->Pixel.ConvolutionFilterBias[2][2],
877 ctx->Pixel.ConvolutionFilterBias[2][3]);
878 _mesa_unmap_pbo_source(ctx, &ctx->Unpack);
879 }
880
881 if (_mesa_is_bufferobj(ctx->Unpack.BufferObj)) {
882 ctx->Driver.UnmapBuffer(ctx, GL_PIXEL_UNPACK_BUFFER_EXT,
883 ctx->Unpack.BufferObj);
884 }
885
886 ctx->NewState |= _NEW_PIXEL;
887 }
888
889
890 /**********************************************************************/
891 /*** image convolution functions ***/
892 /**********************************************************************/
893
894 static void
895 convolve_1d_reduce(GLint srcWidth, const GLfloat src[][4],
896 GLint filterWidth, const GLfloat filter[][4],
897 GLfloat dest[][4])
898 {
899 GLint dstWidth;
900 GLint i, n;
901
902 if (filterWidth >= 1)
903 dstWidth = srcWidth - (filterWidth - 1);
904 else
905 dstWidth = srcWidth;
906
907 if (dstWidth <= 0)
908 return; /* null result */
909
910 for (i = 0; i < dstWidth; i++) {
911 GLfloat sumR = 0.0;
912 GLfloat sumG = 0.0;
913 GLfloat sumB = 0.0;
914 GLfloat sumA = 0.0;
915 for (n = 0; n < filterWidth; n++) {
916 sumR += src[i + n][RCOMP] * filter[n][RCOMP];
917 sumG += src[i + n][GCOMP] * filter[n][GCOMP];
918 sumB += src[i + n][BCOMP] * filter[n][BCOMP];
919 sumA += src[i + n][ACOMP] * filter[n][ACOMP];
920 }
921 dest[i][RCOMP] = sumR;
922 dest[i][GCOMP] = sumG;
923 dest[i][BCOMP] = sumB;
924 dest[i][ACOMP] = sumA;
925 }
926 }
927
928
929 static void
930 convolve_1d_constant(GLint srcWidth, const GLfloat src[][4],
931 GLint filterWidth, const GLfloat filter[][4],
932 GLfloat dest[][4],
933 const GLfloat borderColor[4])
934 {
935 const GLint halfFilterWidth = filterWidth / 2;
936 GLint i, n;
937
938 for (i = 0; i < srcWidth; i++) {
939 GLfloat sumR = 0.0;
940 GLfloat sumG = 0.0;
941 GLfloat sumB = 0.0;
942 GLfloat sumA = 0.0;
943 for (n = 0; n < filterWidth; n++) {
944 if (i + n < halfFilterWidth || i + n - halfFilterWidth >= srcWidth) {
945 sumR += borderColor[RCOMP] * filter[n][RCOMP];
946 sumG += borderColor[GCOMP] * filter[n][GCOMP];
947 sumB += borderColor[BCOMP] * filter[n][BCOMP];
948 sumA += borderColor[ACOMP] * filter[n][ACOMP];
949 }
950 else {
951 sumR += src[i + n - halfFilterWidth][RCOMP] * filter[n][RCOMP];
952 sumG += src[i + n - halfFilterWidth][GCOMP] * filter[n][GCOMP];
953 sumB += src[i + n - halfFilterWidth][BCOMP] * filter[n][BCOMP];
954 sumA += src[i + n - halfFilterWidth][ACOMP] * filter[n][ACOMP];
955 }
956 }
957 dest[i][RCOMP] = sumR;
958 dest[i][GCOMP] = sumG;
959 dest[i][BCOMP] = sumB;
960 dest[i][ACOMP] = sumA;
961 }
962 }
963
964
965 static void
966 convolve_1d_replicate(GLint srcWidth, const GLfloat src[][4],
967 GLint filterWidth, const GLfloat filter[][4],
968 GLfloat dest[][4])
969 {
970 const GLint halfFilterWidth = filterWidth / 2;
971 GLint i, n;
972
973 for (i = 0; i < srcWidth; i++) {
974 GLfloat sumR = 0.0;
975 GLfloat sumG = 0.0;
976 GLfloat sumB = 0.0;
977 GLfloat sumA = 0.0;
978 for (n = 0; n < filterWidth; n++) {
979 if (i + n < halfFilterWidth) {
980 sumR += src[0][RCOMP] * filter[n][RCOMP];
981 sumG += src[0][GCOMP] * filter[n][GCOMP];
982 sumB += src[0][BCOMP] * filter[n][BCOMP];
983 sumA += src[0][ACOMP] * filter[n][ACOMP];
984 }
985 else if (i + n - halfFilterWidth >= srcWidth) {
986 sumR += src[srcWidth - 1][RCOMP] * filter[n][RCOMP];
987 sumG += src[srcWidth - 1][GCOMP] * filter[n][GCOMP];
988 sumB += src[srcWidth - 1][BCOMP] * filter[n][BCOMP];
989 sumA += src[srcWidth - 1][ACOMP] * filter[n][ACOMP];
990 }
991 else {
992 sumR += src[i + n - halfFilterWidth][RCOMP] * filter[n][RCOMP];
993 sumG += src[i + n - halfFilterWidth][GCOMP] * filter[n][GCOMP];
994 sumB += src[i + n - halfFilterWidth][BCOMP] * filter[n][BCOMP];
995 sumA += src[i + n - halfFilterWidth][ACOMP] * filter[n][ACOMP];
996 }
997 }
998 dest[i][RCOMP] = sumR;
999 dest[i][GCOMP] = sumG;
1000 dest[i][BCOMP] = sumB;
1001 dest[i][ACOMP] = sumA;
1002 }
1003 }
1004
1005
1006 static void
1007 convolve_2d_reduce(GLint srcWidth, GLint srcHeight,
1008 const GLfloat src[][4],
1009 GLint filterWidth, GLint filterHeight,
1010 const GLfloat filter[][4],
1011 GLfloat dest[][4])
1012 {
1013 GLint dstWidth, dstHeight;
1014 GLint i, j, n, m;
1015
1016 if (filterWidth >= 1)
1017 dstWidth = srcWidth - (filterWidth - 1);
1018 else
1019 dstWidth = srcWidth;
1020
1021 if (filterHeight >= 1)
1022 dstHeight = srcHeight - (filterHeight - 1);
1023 else
1024 dstHeight = srcHeight;
1025
1026 if (dstWidth <= 0 || dstHeight <= 0)
1027 return;
1028
1029 for (j = 0; j < dstHeight; j++) {
1030 for (i = 0; i < dstWidth; i++) {
1031 GLfloat sumR = 0.0;
1032 GLfloat sumG = 0.0;
1033 GLfloat sumB = 0.0;
1034 GLfloat sumA = 0.0;
1035 for (m = 0; m < filterHeight; m++) {
1036 for (n = 0; n < filterWidth; n++) {
1037 const GLint k = (j + m) * srcWidth + i + n;
1038 const GLint f = m * filterWidth + n;
1039 sumR += src[k][RCOMP] * filter[f][RCOMP];
1040 sumG += src[k][GCOMP] * filter[f][GCOMP];
1041 sumB += src[k][BCOMP] * filter[f][BCOMP];
1042 sumA += src[k][ACOMP] * filter[f][ACOMP];
1043 }
1044 }
1045 dest[j * dstWidth + i][RCOMP] = sumR;
1046 dest[j * dstWidth + i][GCOMP] = sumG;
1047 dest[j * dstWidth + i][BCOMP] = sumB;
1048 dest[j * dstWidth + i][ACOMP] = sumA;
1049 }
1050 }
1051 }
1052
1053
1054 static void
1055 convolve_2d_constant(GLint srcWidth, GLint srcHeight,
1056 const GLfloat src[][4],
1057 GLint filterWidth, GLint filterHeight,
1058 const GLfloat filter[][4],
1059 GLfloat dest[][4],
1060 const GLfloat borderColor[4])
1061 {
1062 const GLint halfFilterWidth = filterWidth / 2;
1063 const GLint halfFilterHeight = filterHeight / 2;
1064 GLint i, j, n, m;
1065
1066 for (j = 0; j < srcHeight; j++) {
1067 for (i = 0; i < srcWidth; i++) {
1068 GLfloat sumR = 0.0;
1069 GLfloat sumG = 0.0;
1070 GLfloat sumB = 0.0;
1071 GLfloat sumA = 0.0;
1072 for (m = 0; m < filterHeight; m++) {
1073 for (n = 0; n < filterWidth; n++) {
1074 const GLint f = m * filterWidth + n;
1075 const GLint is = i + n - halfFilterWidth;
1076 const GLint js = j + m - halfFilterHeight;
1077 if (is < 0 || is >= srcWidth ||
1078 js < 0 || js >= srcHeight) {
1079 sumR += borderColor[RCOMP] * filter[f][RCOMP];
1080 sumG += borderColor[GCOMP] * filter[f][GCOMP];
1081 sumB += borderColor[BCOMP] * filter[f][BCOMP];
1082 sumA += borderColor[ACOMP] * filter[f][ACOMP];
1083 }
1084 else {
1085 const GLint k = js * srcWidth + is;
1086 sumR += src[k][RCOMP] * filter[f][RCOMP];
1087 sumG += src[k][GCOMP] * filter[f][GCOMP];
1088 sumB += src[k][BCOMP] * filter[f][BCOMP];
1089 sumA += src[k][ACOMP] * filter[f][ACOMP];
1090 }
1091 }
1092 }
1093 dest[j * srcWidth + i][RCOMP] = sumR;
1094 dest[j * srcWidth + i][GCOMP] = sumG;
1095 dest[j * srcWidth + i][BCOMP] = sumB;
1096 dest[j * srcWidth + i][ACOMP] = sumA;
1097 }
1098 }
1099 }
1100
1101
1102 static void
1103 convolve_2d_replicate(GLint srcWidth, GLint srcHeight,
1104 const GLfloat src[][4],
1105 GLint filterWidth, GLint filterHeight,
1106 const GLfloat filter[][4],
1107 GLfloat dest[][4])
1108 {
1109 const GLint halfFilterWidth = filterWidth / 2;
1110 const GLint halfFilterHeight = filterHeight / 2;
1111 GLint i, j, n, m;
1112
1113 for (j = 0; j < srcHeight; j++) {
1114 for (i = 0; i < srcWidth; i++) {
1115 GLfloat sumR = 0.0;
1116 GLfloat sumG = 0.0;
1117 GLfloat sumB = 0.0;
1118 GLfloat sumA = 0.0;
1119 for (m = 0; m < filterHeight; m++) {
1120 for (n = 0; n < filterWidth; n++) {
1121 const GLint f = m * filterWidth + n;
1122 GLint is = i + n - halfFilterWidth;
1123 GLint js = j + m - halfFilterHeight;
1124 GLint k;
1125 if (is < 0)
1126 is = 0;
1127 else if (is >= srcWidth)
1128 is = srcWidth - 1;
1129 if (js < 0)
1130 js = 0;
1131 else if (js >= srcHeight)
1132 js = srcHeight - 1;
1133 k = js * srcWidth + is;
1134 sumR += src[k][RCOMP] * filter[f][RCOMP];
1135 sumG += src[k][GCOMP] * filter[f][GCOMP];
1136 sumB += src[k][BCOMP] * filter[f][BCOMP];
1137 sumA += src[k][ACOMP] * filter[f][ACOMP];
1138 }
1139 }
1140 dest[j * srcWidth + i][RCOMP] = sumR;
1141 dest[j * srcWidth + i][GCOMP] = sumG;
1142 dest[j * srcWidth + i][BCOMP] = sumB;
1143 dest[j * srcWidth + i][ACOMP] = sumA;
1144 }
1145 }
1146 }
1147
1148
1149 static void
1150 convolve_sep_reduce(GLint srcWidth, GLint srcHeight,
1151 const GLfloat src[][4],
1152 GLint filterWidth, GLint filterHeight,
1153 const GLfloat rowFilt[][4],
1154 const GLfloat colFilt[][4],
1155 GLfloat dest[][4])
1156 {
1157 GLint dstWidth, dstHeight;
1158 GLint i, j, n, m;
1159
1160 if (filterWidth >= 1)
1161 dstWidth = srcWidth - (filterWidth - 1);
1162 else
1163 dstWidth = srcWidth;
1164
1165 if (filterHeight >= 1)
1166 dstHeight = srcHeight - (filterHeight - 1);
1167 else
1168 dstHeight = srcHeight;
1169
1170 if (dstWidth <= 0 || dstHeight <= 0)
1171 return;
1172
1173 for (j = 0; j < dstHeight; j++) {
1174 for (i = 0; i < dstWidth; i++) {
1175 GLfloat sumR = 0.0;
1176 GLfloat sumG = 0.0;
1177 GLfloat sumB = 0.0;
1178 GLfloat sumA = 0.0;
1179 for (m = 0; m < filterHeight; m++) {
1180 for (n = 0; n < filterWidth; n++) {
1181 GLint k = (j + m) * srcWidth + i + n;
1182 sumR += src[k][RCOMP] * rowFilt[n][RCOMP] * colFilt[m][RCOMP];
1183 sumG += src[k][GCOMP] * rowFilt[n][GCOMP] * colFilt[m][GCOMP];
1184 sumB += src[k][BCOMP] * rowFilt[n][BCOMP] * colFilt[m][BCOMP];
1185 sumA += src[k][ACOMP] * rowFilt[n][ACOMP] * colFilt[m][ACOMP];
1186 }
1187 }
1188 dest[j * dstWidth + i][RCOMP] = sumR;
1189 dest[j * dstWidth + i][GCOMP] = sumG;
1190 dest[j * dstWidth + i][BCOMP] = sumB;
1191 dest[j * dstWidth + i][ACOMP] = sumA;
1192 }
1193 }
1194 }
1195
1196
1197 static void
1198 convolve_sep_constant(GLint srcWidth, GLint srcHeight,
1199 const GLfloat src[][4],
1200 GLint filterWidth, GLint filterHeight,
1201 const GLfloat rowFilt[][4],
1202 const GLfloat colFilt[][4],
1203 GLfloat dest[][4],
1204 const GLfloat borderColor[4])
1205 {
1206 const GLint halfFilterWidth = filterWidth / 2;
1207 const GLint halfFilterHeight = filterHeight / 2;
1208 GLint i, j, n, m;
1209
1210 for (j = 0; j < srcHeight; j++) {
1211 for (i = 0; i < srcWidth; i++) {
1212 GLfloat sumR = 0.0;
1213 GLfloat sumG = 0.0;
1214 GLfloat sumB = 0.0;
1215 GLfloat sumA = 0.0;
1216 for (m = 0; m < filterHeight; m++) {
1217 for (n = 0; n < filterWidth; n++) {
1218 const GLint is = i + n - halfFilterWidth;
1219 const GLint js = j + m - halfFilterHeight;
1220 if (is < 0 || is >= srcWidth ||
1221 js < 0 || js >= srcHeight) {
1222 sumR += borderColor[RCOMP] * rowFilt[n][RCOMP] * colFilt[m][RCOMP];
1223 sumG += borderColor[GCOMP] * rowFilt[n][GCOMP] * colFilt[m][GCOMP];
1224 sumB += borderColor[BCOMP] * rowFilt[n][BCOMP] * colFilt[m][BCOMP];
1225 sumA += borderColor[ACOMP] * rowFilt[n][ACOMP] * colFilt[m][ACOMP];
1226 }
1227 else {
1228 GLint k = js * srcWidth + is;
1229 sumR += src[k][RCOMP] * rowFilt[n][RCOMP] * colFilt[m][RCOMP];
1230 sumG += src[k][GCOMP] * rowFilt[n][GCOMP] * colFilt[m][GCOMP];
1231 sumB += src[k][BCOMP] * rowFilt[n][BCOMP] * colFilt[m][BCOMP];
1232 sumA += src[k][ACOMP] * rowFilt[n][ACOMP] * colFilt[m][ACOMP];
1233 }
1234
1235 }
1236 }
1237 dest[j * srcWidth + i][RCOMP] = sumR;
1238 dest[j * srcWidth + i][GCOMP] = sumG;
1239 dest[j * srcWidth + i][BCOMP] = sumB;
1240 dest[j * srcWidth + i][ACOMP] = sumA;
1241 }
1242 }
1243 }
1244
1245
1246 static void
1247 convolve_sep_replicate(GLint srcWidth, GLint srcHeight,
1248 const GLfloat src[][4],
1249 GLint filterWidth, GLint filterHeight,
1250 const GLfloat rowFilt[][4],
1251 const GLfloat colFilt[][4],
1252 GLfloat dest[][4])
1253 {
1254 const GLint halfFilterWidth = filterWidth / 2;
1255 const GLint halfFilterHeight = filterHeight / 2;
1256 GLint i, j, n, m;
1257
1258 for (j = 0; j < srcHeight; j++) {
1259 for (i = 0; i < srcWidth; i++) {
1260 GLfloat sumR = 0.0;
1261 GLfloat sumG = 0.0;
1262 GLfloat sumB = 0.0;
1263 GLfloat sumA = 0.0;
1264 for (m = 0; m < filterHeight; m++) {
1265 for (n = 0; n < filterWidth; n++) {
1266 GLint is = i + n - halfFilterWidth;
1267 GLint js = j + m - halfFilterHeight;
1268 GLint k;
1269 if (is < 0)
1270 is = 0;
1271 else if (is >= srcWidth)
1272 is = srcWidth - 1;
1273 if (js < 0)
1274 js = 0;
1275 else if (js >= srcHeight)
1276 js = srcHeight - 1;
1277 k = js * srcWidth + is;
1278 sumR += src[k][RCOMP] * rowFilt[n][RCOMP] * colFilt[m][RCOMP];
1279 sumG += src[k][GCOMP] * rowFilt[n][GCOMP] * colFilt[m][GCOMP];
1280 sumB += src[k][BCOMP] * rowFilt[n][BCOMP] * colFilt[m][BCOMP];
1281 sumA += src[k][ACOMP] * rowFilt[n][ACOMP] * colFilt[m][ACOMP];
1282 }
1283 }
1284 dest[j * srcWidth + i][RCOMP] = sumR;
1285 dest[j * srcWidth + i][GCOMP] = sumG;
1286 dest[j * srcWidth + i][BCOMP] = sumB;
1287 dest[j * srcWidth + i][ACOMP] = sumA;
1288 }
1289 }
1290 }
1291
1292
1293
1294 void
1295 _mesa_convolve_1d_image(const GLcontext *ctx, GLsizei *width,
1296 const GLfloat *srcImage, GLfloat *dstImage)
1297 {
1298 switch (ctx->Pixel.ConvolutionBorderMode[0]) {
1299 case GL_REDUCE:
1300 convolve_1d_reduce(*width, (const GLfloat (*)[4]) srcImage,
1301 ctx->Convolution1D.Width,
1302 (const GLfloat (*)[4]) ctx->Convolution1D.Filter,
1303 (GLfloat (*)[4]) dstImage);
1304 *width = *width - (MAX2(ctx->Convolution1D.Width, 1) - 1);
1305 break;
1306 case GL_CONSTANT_BORDER:
1307 convolve_1d_constant(*width, (const GLfloat (*)[4]) srcImage,
1308 ctx->Convolution1D.Width,
1309 (const GLfloat (*)[4]) ctx->Convolution1D.Filter,
1310 (GLfloat (*)[4]) dstImage,
1311 ctx->Pixel.ConvolutionBorderColor[0]);
1312 break;
1313 case GL_REPLICATE_BORDER:
1314 convolve_1d_replicate(*width, (const GLfloat (*)[4]) srcImage,
1315 ctx->Convolution1D.Width,
1316 (const GLfloat (*)[4]) ctx->Convolution1D.Filter,
1317 (GLfloat (*)[4]) dstImage);
1318 break;
1319 default:
1320 ;
1321 }
1322 }
1323
1324
1325 void
1326 _mesa_convolve_2d_image(const GLcontext *ctx, GLsizei *width, GLsizei *height,
1327 const GLfloat *srcImage, GLfloat *dstImage)
1328 {
1329 switch (ctx->Pixel.ConvolutionBorderMode[1]) {
1330 case GL_REDUCE:
1331 convolve_2d_reduce(*width, *height,
1332 (const GLfloat (*)[4]) srcImage,
1333 ctx->Convolution2D.Width,
1334 ctx->Convolution2D.Height,
1335 (const GLfloat (*)[4]) ctx->Convolution2D.Filter,
1336 (GLfloat (*)[4]) dstImage);
1337 *width = *width - (MAX2(ctx->Convolution2D.Width, 1) - 1);
1338 *height = *height - (MAX2(ctx->Convolution2D.Height, 1) - 1);
1339 break;
1340 case GL_CONSTANT_BORDER:
1341 convolve_2d_constant(*width, *height,
1342 (const GLfloat (*)[4]) srcImage,
1343 ctx->Convolution2D.Width,
1344 ctx->Convolution2D.Height,
1345 (const GLfloat (*)[4]) ctx->Convolution2D.Filter,
1346 (GLfloat (*)[4]) dstImage,
1347 ctx->Pixel.ConvolutionBorderColor[1]);
1348 break;
1349 case GL_REPLICATE_BORDER:
1350 convolve_2d_replicate(*width, *height,
1351 (const GLfloat (*)[4]) srcImage,
1352 ctx->Convolution2D.Width,
1353 ctx->Convolution2D.Height,
1354 (const GLfloat (*)[4])ctx->Convolution2D.Filter,
1355 (GLfloat (*)[4]) dstImage);
1356 break;
1357 default:
1358 ;
1359 }
1360 }
1361
1362
1363 void
1364 _mesa_convolve_sep_image(const GLcontext *ctx,
1365 GLsizei *width, GLsizei *height,
1366 const GLfloat *srcImage, GLfloat *dstImage)
1367 {
1368 const GLfloat *rowFilter = ctx->Separable2D.Filter;
1369 const GLfloat *colFilter = rowFilter + 4 * MAX_CONVOLUTION_WIDTH;
1370
1371 switch (ctx->Pixel.ConvolutionBorderMode[2]) {
1372 case GL_REDUCE:
1373 convolve_sep_reduce(*width, *height,
1374 (const GLfloat (*)[4]) srcImage,
1375 ctx->Separable2D.Width,
1376 ctx->Separable2D.Height,
1377 (const GLfloat (*)[4]) rowFilter,
1378 (const GLfloat (*)[4]) colFilter,
1379 (GLfloat (*)[4]) dstImage);
1380 *width = *width - (MAX2(ctx->Separable2D.Width, 1) - 1);
1381 *height = *height - (MAX2(ctx->Separable2D.Height, 1) - 1);
1382 break;
1383 case GL_CONSTANT_BORDER:
1384 convolve_sep_constant(*width, *height,
1385 (const GLfloat (*)[4]) srcImage,
1386 ctx->Separable2D.Width,
1387 ctx->Separable2D.Height,
1388 (const GLfloat (*)[4]) rowFilter,
1389 (const GLfloat (*)[4]) colFilter,
1390 (GLfloat (*)[4]) dstImage,
1391 ctx->Pixel.ConvolutionBorderColor[2]);
1392 break;
1393 case GL_REPLICATE_BORDER:
1394 convolve_sep_replicate(*width, *height,
1395 (const GLfloat (*)[4]) srcImage,
1396 ctx->Separable2D.Width,
1397 ctx->Separable2D.Height,
1398 (const GLfloat (*)[4]) rowFilter,
1399 (const GLfloat (*)[4]) colFilter,
1400 (GLfloat (*)[4]) dstImage);
1401 break;
1402 default:
1403 ;
1404 }
1405 }
1406
1407
1408
1409 /*
1410 * This function computes an image's size after convolution.
1411 * If the convolution border mode is GL_REDUCE, the post-convolution
1412 * image will be smaller than the original.
1413 */
1414 void
1415 _mesa_adjust_image_for_convolution(const GLcontext *ctx, GLuint dimensions,
1416 GLsizei *width, GLsizei *height)
1417 {
1418 if (ctx->Pixel.Convolution1DEnabled
1419 && dimensions == 1
1420 && ctx->Pixel.ConvolutionBorderMode[0] == GL_REDUCE) {
1421 *width = *width - (MAX2(ctx->Convolution1D.Width, 1) - 1);
1422 }
1423 else if (ctx->Pixel.Convolution2DEnabled
1424 && dimensions > 1
1425 && ctx->Pixel.ConvolutionBorderMode[1] == GL_REDUCE) {
1426 *width = *width - (MAX2(ctx->Convolution2D.Width, 1) - 1);
1427 *height = *height - (MAX2(ctx->Convolution2D.Height, 1) - 1);
1428 }
1429 else if (ctx->Pixel.Separable2DEnabled
1430 && dimensions > 1
1431 && ctx->Pixel.ConvolutionBorderMode[2] == GL_REDUCE) {
1432 *width = *width - (MAX2(ctx->Separable2D.Width, 1) - 1);
1433 *height = *height - (MAX2(ctx->Separable2D.Height, 1) - 1);
1434 }
1435 }