70951112a18a06b7d7a6676557d92f6f6d7b1feb
[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 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 void GLAPIENTRY
523 _mesa_GetConvolutionFilter(GLenum target, GLenum format, GLenum type,
524 GLvoid *image)
525 {
526 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(ctx, 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 image = _mesa_map_validate_pbo_dest(ctx, 2, &ctx->Pack,
562 filter->Width, filter->Height, 1,
563 format, type, image,
564 "glGetConvolutionFilter");
565 if (!image)
566 return;
567
568 for (row = 0; row < filter->Height; row++) {
569 GLvoid *dst = _mesa_image_address2d(&ctx->Pack, image, filter->Width,
570 filter->Height, format, type,
571 row, 0);
572 GLfloat (*src)[4] = (GLfloat (*)[4]) (filter->Filter + row * filter->Width * 4);
573 _mesa_pack_rgba_span_float(ctx, filter->Width, src,
574 format, type, dst, &ctx->Pack, 0x0);
575 }
576
577 _mesa_unmap_pbo_dest(ctx, &ctx->Pack);
578 }
579
580
581 void GLAPIENTRY
582 _mesa_GetConvolutionParameterfv(GLenum target, GLenum pname, GLfloat *params)
583 {
584 GET_CURRENT_CONTEXT(ctx);
585 const struct gl_convolution_attrib *conv;
586 GLuint c;
587 ASSERT_OUTSIDE_BEGIN_END(ctx);
588
589 switch (target) {
590 case GL_CONVOLUTION_1D:
591 c = 0;
592 conv = &ctx->Convolution1D;
593 break;
594 case GL_CONVOLUTION_2D:
595 c = 1;
596 conv = &ctx->Convolution2D;
597 break;
598 case GL_SEPARABLE_2D:
599 c = 2;
600 conv = &ctx->Separable2D;
601 break;
602 default:
603 _mesa_error(ctx, GL_INVALID_ENUM, "glGetConvolutionParameterfv(target)");
604 return;
605 }
606
607 switch (pname) {
608 case GL_CONVOLUTION_BORDER_COLOR:
609 COPY_4V(params, ctx->Pixel.ConvolutionBorderColor[c]);
610 break;
611 case GL_CONVOLUTION_BORDER_MODE:
612 *params = (GLfloat) ctx->Pixel.ConvolutionBorderMode[c];
613 break;
614 case GL_CONVOLUTION_FILTER_SCALE:
615 COPY_4V(params, ctx->Pixel.ConvolutionFilterScale[c]);
616 break;
617 case GL_CONVOLUTION_FILTER_BIAS:
618 COPY_4V(params, ctx->Pixel.ConvolutionFilterBias[c]);
619 break;
620 case GL_CONVOLUTION_FORMAT:
621 *params = (GLfloat) conv->Format;
622 break;
623 case GL_CONVOLUTION_WIDTH:
624 *params = (GLfloat) conv->Width;
625 break;
626 case GL_CONVOLUTION_HEIGHT:
627 *params = (GLfloat) conv->Height;
628 break;
629 case GL_MAX_CONVOLUTION_WIDTH:
630 *params = (GLfloat) ctx->Const.MaxConvolutionWidth;
631 break;
632 case GL_MAX_CONVOLUTION_HEIGHT:
633 *params = (GLfloat) ctx->Const.MaxConvolutionHeight;
634 break;
635 default:
636 _mesa_error(ctx, GL_INVALID_ENUM, "glGetConvolutionParameterfv(pname)");
637 return;
638 }
639 }
640
641
642 void GLAPIENTRY
643 _mesa_GetConvolutionParameteriv(GLenum target, GLenum pname, GLint *params)
644 {
645 GET_CURRENT_CONTEXT(ctx);
646 const struct gl_convolution_attrib *conv;
647 GLuint c;
648 ASSERT_OUTSIDE_BEGIN_END(ctx);
649
650 switch (target) {
651 case GL_CONVOLUTION_1D:
652 c = 0;
653 conv = &ctx->Convolution1D;
654 break;
655 case GL_CONVOLUTION_2D:
656 c = 1;
657 conv = &ctx->Convolution2D;
658 break;
659 case GL_SEPARABLE_2D:
660 c = 2;
661 conv = &ctx->Separable2D;
662 break;
663 default:
664 _mesa_error(ctx, GL_INVALID_ENUM, "glGetConvolutionParameteriv(target)");
665 return;
666 }
667
668 switch (pname) {
669 case GL_CONVOLUTION_BORDER_COLOR:
670 params[0] = FLOAT_TO_INT(ctx->Pixel.ConvolutionBorderColor[c][0]);
671 params[1] = FLOAT_TO_INT(ctx->Pixel.ConvolutionBorderColor[c][1]);
672 params[2] = FLOAT_TO_INT(ctx->Pixel.ConvolutionBorderColor[c][2]);
673 params[3] = FLOAT_TO_INT(ctx->Pixel.ConvolutionBorderColor[c][3]);
674 break;
675 case GL_CONVOLUTION_BORDER_MODE:
676 *params = (GLint) ctx->Pixel.ConvolutionBorderMode[c];
677 break;
678 case GL_CONVOLUTION_FILTER_SCALE:
679 params[0] = (GLint) ctx->Pixel.ConvolutionFilterScale[c][0];
680 params[1] = (GLint) ctx->Pixel.ConvolutionFilterScale[c][1];
681 params[2] = (GLint) ctx->Pixel.ConvolutionFilterScale[c][2];
682 params[3] = (GLint) ctx->Pixel.ConvolutionFilterScale[c][3];
683 break;
684 case GL_CONVOLUTION_FILTER_BIAS:
685 params[0] = (GLint) ctx->Pixel.ConvolutionFilterBias[c][0];
686 params[1] = (GLint) ctx->Pixel.ConvolutionFilterBias[c][1];
687 params[2] = (GLint) ctx->Pixel.ConvolutionFilterBias[c][2];
688 params[3] = (GLint) ctx->Pixel.ConvolutionFilterBias[c][3];
689 break;
690 case GL_CONVOLUTION_FORMAT:
691 *params = (GLint) conv->Format;
692 break;
693 case GL_CONVOLUTION_WIDTH:
694 *params = (GLint) conv->Width;
695 break;
696 case GL_CONVOLUTION_HEIGHT:
697 *params = (GLint) conv->Height;
698 break;
699 case GL_MAX_CONVOLUTION_WIDTH:
700 *params = (GLint) ctx->Const.MaxConvolutionWidth;
701 break;
702 case GL_MAX_CONVOLUTION_HEIGHT:
703 *params = (GLint) ctx->Const.MaxConvolutionHeight;
704 break;
705 default:
706 _mesa_error(ctx, GL_INVALID_ENUM, "glGetConvolutionParameteriv(pname)");
707 return;
708 }
709 }
710
711
712 void GLAPIENTRY
713 _mesa_GetSeparableFilter(GLenum target, GLenum format, GLenum type,
714 GLvoid *row, GLvoid *column, GLvoid *span)
715 {
716 const GLint colStart = MAX_CONVOLUTION_WIDTH * 4;
717 struct gl_convolution_attrib *filter;
718 GET_CURRENT_CONTEXT(ctx);
719 ASSERT_OUTSIDE_BEGIN_END(ctx);
720
721 if (ctx->NewState) {
722 _mesa_update_state(ctx);
723 }
724
725 if (target != GL_SEPARABLE_2D) {
726 _mesa_error(ctx, GL_INVALID_ENUM, "glGetSeparableFilter(target)");
727 return;
728 }
729
730 if (!_mesa_is_legal_format_and_type(ctx, format, type)) {
731 _mesa_error(ctx, GL_INVALID_OPERATION,
732 "glGetConvolutionFilter(format or type)");
733 return;
734 }
735
736 if (format == GL_COLOR_INDEX ||
737 format == GL_STENCIL_INDEX ||
738 format == GL_DEPTH_COMPONENT ||
739 format == GL_INTENSITY ||
740 type == GL_BITMAP) {
741 _mesa_error(ctx, GL_INVALID_ENUM, "glGetConvolutionFilter(format or type)");
742 return;
743 }
744
745 filter = &ctx->Separable2D;
746
747 /* Get row filter */
748 row = _mesa_map_validate_pbo_dest(ctx, 1, &ctx->Pack,
749 filter->Width, 1, 1,
750 format, type, row,
751 "glGetConvolutionFilter");
752 if (row) {
753 GLvoid *dst = _mesa_image_address1d(&ctx->Pack, row, filter->Width,
754 format, type, 0);
755 _mesa_pack_rgba_span_float(ctx, filter->Width,
756 (GLfloat (*)[4]) filter->Filter,
757 format, type, dst, &ctx->Pack, 0x0);
758 _mesa_unmap_pbo_dest(ctx, &ctx->Pack);
759 }
760
761 /* get column filter */
762 column = _mesa_map_validate_pbo_dest(ctx, 1, &ctx->Pack,
763 filter->Height, 1, 1,
764 format, type, column,
765 "glGetConvolutionFilter");
766 if (column) {
767 GLvoid *dst = _mesa_image_address1d(&ctx->Pack, column, filter->Height,
768 format, type, 0);
769 GLfloat (*src)[4] = (GLfloat (*)[4]) (filter->Filter + colStart);
770 _mesa_pack_rgba_span_float(ctx, filter->Height, src,
771 format, type, dst, &ctx->Pack, 0x0);
772 _mesa_unmap_pbo_dest(ctx, &ctx->Pack);
773 }
774
775 (void) span; /* unused at this time */
776 }
777
778
779 void GLAPIENTRY
780 _mesa_SeparableFilter2D(GLenum target, GLenum internalFormat, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *row, const GLvoid *column)
781 {
782 const GLint colStart = MAX_CONVOLUTION_WIDTH * 4;
783 GLint baseFormat;
784 GET_CURRENT_CONTEXT(ctx);
785 ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx);
786
787 if (target != GL_SEPARABLE_2D) {
788 _mesa_error(ctx, GL_INVALID_ENUM, "glSeparableFilter2D(target)");
789 return;
790 }
791
792 baseFormat = base_filter_format(internalFormat);
793 if (baseFormat < 0 || baseFormat == GL_COLOR_INDEX) {
794 _mesa_error(ctx, GL_INVALID_ENUM, "glSeparableFilter2D(internalFormat)");
795 return;
796 }
797
798 if (width < 0 || width > MAX_CONVOLUTION_WIDTH) {
799 _mesa_error(ctx, GL_INVALID_VALUE, "glSeparableFilter2D(width)");
800 return;
801 }
802 if (height < 0 || height > MAX_CONVOLUTION_HEIGHT) {
803 _mesa_error(ctx, GL_INVALID_VALUE, "glSeparableFilter2D(height)");
804 return;
805 }
806
807 if (!_mesa_is_legal_format_and_type(ctx, format, type)) {
808 _mesa_error(ctx, GL_INVALID_OPERATION, "glSeparableFilter2D(format or type)");
809 return;
810 }
811
812 if (format == GL_COLOR_INDEX ||
813 format == GL_STENCIL_INDEX ||
814 format == GL_DEPTH_COMPONENT ||
815 format == GL_INTENSITY ||
816 type == GL_BITMAP) {
817 _mesa_error(ctx, GL_INVALID_ENUM, "glSeparableFilter2D(format or type)");
818 return;
819 }
820
821 ctx->Separable2D.Format = format;
822 ctx->Separable2D.InternalFormat = internalFormat;
823 ctx->Separable2D.Width = width;
824 ctx->Separable2D.Height = height;
825
826 /* unpack row filter */
827 row = _mesa_map_validate_pbo_source(ctx, 1, &ctx->Unpack,
828 width, 1, 1,
829 format, type, row,
830 "glSeparableFilter2D");
831 if (row) {
832 _mesa_unpack_color_span_float(ctx, width, GL_RGBA,
833 ctx->Separable2D.Filter,
834 format, type, row, &ctx->Unpack,
835 0x0); /* transferOps */
836 _mesa_scale_and_bias_rgba(width,
837 (GLfloat (*)[4]) ctx->Separable2D.Filter,
838 ctx->Pixel.ConvolutionFilterScale[2][0],
839 ctx->Pixel.ConvolutionFilterScale[2][1],
840 ctx->Pixel.ConvolutionFilterScale[2][2],
841 ctx->Pixel.ConvolutionFilterScale[2][3],
842 ctx->Pixel.ConvolutionFilterBias[2][0],
843 ctx->Pixel.ConvolutionFilterBias[2][1],
844 ctx->Pixel.ConvolutionFilterBias[2][2],
845 ctx->Pixel.ConvolutionFilterBias[2][3]);
846 _mesa_unmap_pbo_source(ctx, &ctx->Unpack);
847 }
848
849 /* unpack column filter */
850 column = _mesa_map_validate_pbo_source(ctx, 1, &ctx->Unpack,
851 height, 1, 1,
852 format, type, column,
853 "glSeparableFilter2D");
854 if (column) {
855 _mesa_unpack_color_span_float(ctx, height, GL_RGBA,
856 &ctx->Separable2D.Filter[colStart],
857 format, type, column, &ctx->Unpack,
858 0); /* transferOps */
859
860 _mesa_scale_and_bias_rgba(height,
861 (GLfloat (*)[4]) (ctx->Separable2D.Filter + colStart),
862 ctx->Pixel.ConvolutionFilterScale[2][0],
863 ctx->Pixel.ConvolutionFilterScale[2][1],
864 ctx->Pixel.ConvolutionFilterScale[2][2],
865 ctx->Pixel.ConvolutionFilterScale[2][3],
866 ctx->Pixel.ConvolutionFilterBias[2][0],
867 ctx->Pixel.ConvolutionFilterBias[2][1],
868 ctx->Pixel.ConvolutionFilterBias[2][2],
869 ctx->Pixel.ConvolutionFilterBias[2][3]);
870 _mesa_unmap_pbo_source(ctx, &ctx->Unpack);
871 }
872
873 if (_mesa_is_bufferobj(ctx->Unpack.BufferObj)) {
874 ctx->Driver.UnmapBuffer(ctx, GL_PIXEL_UNPACK_BUFFER_EXT,
875 ctx->Unpack.BufferObj);
876 }
877
878 ctx->NewState |= _NEW_PIXEL;
879 }
880
881
882 /**********************************************************************/
883 /*** image convolution functions ***/
884 /**********************************************************************/
885
886 static void
887 convolve_1d_reduce(GLint srcWidth, const GLfloat src[][4],
888 GLint filterWidth, const GLfloat filter[][4],
889 GLfloat dest[][4])
890 {
891 GLint dstWidth;
892 GLint i, n;
893
894 if (filterWidth >= 1)
895 dstWidth = srcWidth - (filterWidth - 1);
896 else
897 dstWidth = srcWidth;
898
899 if (dstWidth <= 0)
900 return; /* null result */
901
902 for (i = 0; i < dstWidth; i++) {
903 GLfloat sumR = 0.0;
904 GLfloat sumG = 0.0;
905 GLfloat sumB = 0.0;
906 GLfloat sumA = 0.0;
907 for (n = 0; n < filterWidth; n++) {
908 sumR += src[i + n][RCOMP] * filter[n][RCOMP];
909 sumG += src[i + n][GCOMP] * filter[n][GCOMP];
910 sumB += src[i + n][BCOMP] * filter[n][BCOMP];
911 sumA += src[i + n][ACOMP] * filter[n][ACOMP];
912 }
913 dest[i][RCOMP] = sumR;
914 dest[i][GCOMP] = sumG;
915 dest[i][BCOMP] = sumB;
916 dest[i][ACOMP] = sumA;
917 }
918 }
919
920
921 static void
922 convolve_1d_constant(GLint srcWidth, const GLfloat src[][4],
923 GLint filterWidth, const GLfloat filter[][4],
924 GLfloat dest[][4],
925 const GLfloat borderColor[4])
926 {
927 const GLint halfFilterWidth = filterWidth / 2;
928 GLint i, n;
929
930 for (i = 0; i < srcWidth; i++) {
931 GLfloat sumR = 0.0;
932 GLfloat sumG = 0.0;
933 GLfloat sumB = 0.0;
934 GLfloat sumA = 0.0;
935 for (n = 0; n < filterWidth; n++) {
936 if (i + n < halfFilterWidth || i + n - halfFilterWidth >= srcWidth) {
937 sumR += borderColor[RCOMP] * filter[n][RCOMP];
938 sumG += borderColor[GCOMP] * filter[n][GCOMP];
939 sumB += borderColor[BCOMP] * filter[n][BCOMP];
940 sumA += borderColor[ACOMP] * filter[n][ACOMP];
941 }
942 else {
943 sumR += src[i + n - halfFilterWidth][RCOMP] * filter[n][RCOMP];
944 sumG += src[i + n - halfFilterWidth][GCOMP] * filter[n][GCOMP];
945 sumB += src[i + n - halfFilterWidth][BCOMP] * filter[n][BCOMP];
946 sumA += src[i + n - halfFilterWidth][ACOMP] * filter[n][ACOMP];
947 }
948 }
949 dest[i][RCOMP] = sumR;
950 dest[i][GCOMP] = sumG;
951 dest[i][BCOMP] = sumB;
952 dest[i][ACOMP] = sumA;
953 }
954 }
955
956
957 static void
958 convolve_1d_replicate(GLint srcWidth, const GLfloat src[][4],
959 GLint filterWidth, const GLfloat filter[][4],
960 GLfloat dest[][4])
961 {
962 const GLint halfFilterWidth = filterWidth / 2;
963 GLint i, n;
964
965 for (i = 0; i < srcWidth; i++) {
966 GLfloat sumR = 0.0;
967 GLfloat sumG = 0.0;
968 GLfloat sumB = 0.0;
969 GLfloat sumA = 0.0;
970 for (n = 0; n < filterWidth; n++) {
971 if (i + n < halfFilterWidth) {
972 sumR += src[0][RCOMP] * filter[n][RCOMP];
973 sumG += src[0][GCOMP] * filter[n][GCOMP];
974 sumB += src[0][BCOMP] * filter[n][BCOMP];
975 sumA += src[0][ACOMP] * filter[n][ACOMP];
976 }
977 else if (i + n - halfFilterWidth >= srcWidth) {
978 sumR += src[srcWidth - 1][RCOMP] * filter[n][RCOMP];
979 sumG += src[srcWidth - 1][GCOMP] * filter[n][GCOMP];
980 sumB += src[srcWidth - 1][BCOMP] * filter[n][BCOMP];
981 sumA += src[srcWidth - 1][ACOMP] * filter[n][ACOMP];
982 }
983 else {
984 sumR += src[i + n - halfFilterWidth][RCOMP] * filter[n][RCOMP];
985 sumG += src[i + n - halfFilterWidth][GCOMP] * filter[n][GCOMP];
986 sumB += src[i + n - halfFilterWidth][BCOMP] * filter[n][BCOMP];
987 sumA += src[i + n - halfFilterWidth][ACOMP] * filter[n][ACOMP];
988 }
989 }
990 dest[i][RCOMP] = sumR;
991 dest[i][GCOMP] = sumG;
992 dest[i][BCOMP] = sumB;
993 dest[i][ACOMP] = sumA;
994 }
995 }
996
997
998 static void
999 convolve_2d_reduce(GLint srcWidth, GLint srcHeight,
1000 const GLfloat src[][4],
1001 GLint filterWidth, GLint filterHeight,
1002 const GLfloat filter[][4],
1003 GLfloat dest[][4])
1004 {
1005 GLint dstWidth, dstHeight;
1006 GLint i, j, n, m;
1007
1008 if (filterWidth >= 1)
1009 dstWidth = srcWidth - (filterWidth - 1);
1010 else
1011 dstWidth = srcWidth;
1012
1013 if (filterHeight >= 1)
1014 dstHeight = srcHeight - (filterHeight - 1);
1015 else
1016 dstHeight = srcHeight;
1017
1018 if (dstWidth <= 0 || dstHeight <= 0)
1019 return;
1020
1021 for (j = 0; j < dstHeight; j++) {
1022 for (i = 0; i < dstWidth; i++) {
1023 GLfloat sumR = 0.0;
1024 GLfloat sumG = 0.0;
1025 GLfloat sumB = 0.0;
1026 GLfloat sumA = 0.0;
1027 for (m = 0; m < filterHeight; m++) {
1028 for (n = 0; n < filterWidth; n++) {
1029 const GLint k = (j + m) * srcWidth + i + n;
1030 const GLint f = m * filterWidth + n;
1031 sumR += src[k][RCOMP] * filter[f][RCOMP];
1032 sumG += src[k][GCOMP] * filter[f][GCOMP];
1033 sumB += src[k][BCOMP] * filter[f][BCOMP];
1034 sumA += src[k][ACOMP] * filter[f][ACOMP];
1035 }
1036 }
1037 dest[j * dstWidth + i][RCOMP] = sumR;
1038 dest[j * dstWidth + i][GCOMP] = sumG;
1039 dest[j * dstWidth + i][BCOMP] = sumB;
1040 dest[j * dstWidth + i][ACOMP] = sumA;
1041 }
1042 }
1043 }
1044
1045
1046 static void
1047 convolve_2d_constant(GLint srcWidth, GLint srcHeight,
1048 const GLfloat src[][4],
1049 GLint filterWidth, GLint filterHeight,
1050 const GLfloat filter[][4],
1051 GLfloat dest[][4],
1052 const GLfloat borderColor[4])
1053 {
1054 const GLint halfFilterWidth = filterWidth / 2;
1055 const GLint halfFilterHeight = filterHeight / 2;
1056 GLint i, j, n, m;
1057
1058 for (j = 0; j < srcHeight; j++) {
1059 for (i = 0; i < srcWidth; i++) {
1060 GLfloat sumR = 0.0;
1061 GLfloat sumG = 0.0;
1062 GLfloat sumB = 0.0;
1063 GLfloat sumA = 0.0;
1064 for (m = 0; m < filterHeight; m++) {
1065 for (n = 0; n < filterWidth; n++) {
1066 const GLint f = m * filterWidth + n;
1067 const GLint is = i + n - halfFilterWidth;
1068 const GLint js = j + m - halfFilterHeight;
1069 if (is < 0 || is >= srcWidth ||
1070 js < 0 || js >= srcHeight) {
1071 sumR += borderColor[RCOMP] * filter[f][RCOMP];
1072 sumG += borderColor[GCOMP] * filter[f][GCOMP];
1073 sumB += borderColor[BCOMP] * filter[f][BCOMP];
1074 sumA += borderColor[ACOMP] * filter[f][ACOMP];
1075 }
1076 else {
1077 const GLint k = js * srcWidth + is;
1078 sumR += src[k][RCOMP] * filter[f][RCOMP];
1079 sumG += src[k][GCOMP] * filter[f][GCOMP];
1080 sumB += src[k][BCOMP] * filter[f][BCOMP];
1081 sumA += src[k][ACOMP] * filter[f][ACOMP];
1082 }
1083 }
1084 }
1085 dest[j * srcWidth + i][RCOMP] = sumR;
1086 dest[j * srcWidth + i][GCOMP] = sumG;
1087 dest[j * srcWidth + i][BCOMP] = sumB;
1088 dest[j * srcWidth + i][ACOMP] = sumA;
1089 }
1090 }
1091 }
1092
1093
1094 static void
1095 convolve_2d_replicate(GLint srcWidth, GLint srcHeight,
1096 const GLfloat src[][4],
1097 GLint filterWidth, GLint filterHeight,
1098 const GLfloat filter[][4],
1099 GLfloat dest[][4])
1100 {
1101 const GLint halfFilterWidth = filterWidth / 2;
1102 const GLint halfFilterHeight = filterHeight / 2;
1103 GLint i, j, n, m;
1104
1105 for (j = 0; j < srcHeight; j++) {
1106 for (i = 0; i < srcWidth; i++) {
1107 GLfloat sumR = 0.0;
1108 GLfloat sumG = 0.0;
1109 GLfloat sumB = 0.0;
1110 GLfloat sumA = 0.0;
1111 for (m = 0; m < filterHeight; m++) {
1112 for (n = 0; n < filterWidth; n++) {
1113 const GLint f = m * filterWidth + n;
1114 GLint is = i + n - halfFilterWidth;
1115 GLint js = j + m - halfFilterHeight;
1116 GLint k;
1117 if (is < 0)
1118 is = 0;
1119 else if (is >= srcWidth)
1120 is = srcWidth - 1;
1121 if (js < 0)
1122 js = 0;
1123 else if (js >= srcHeight)
1124 js = srcHeight - 1;
1125 k = js * srcWidth + is;
1126 sumR += src[k][RCOMP] * filter[f][RCOMP];
1127 sumG += src[k][GCOMP] * filter[f][GCOMP];
1128 sumB += src[k][BCOMP] * filter[f][BCOMP];
1129 sumA += src[k][ACOMP] * filter[f][ACOMP];
1130 }
1131 }
1132 dest[j * srcWidth + i][RCOMP] = sumR;
1133 dest[j * srcWidth + i][GCOMP] = sumG;
1134 dest[j * srcWidth + i][BCOMP] = sumB;
1135 dest[j * srcWidth + i][ACOMP] = sumA;
1136 }
1137 }
1138 }
1139
1140
1141 static void
1142 convolve_sep_reduce(GLint srcWidth, GLint srcHeight,
1143 const GLfloat src[][4],
1144 GLint filterWidth, GLint filterHeight,
1145 const GLfloat rowFilt[][4],
1146 const GLfloat colFilt[][4],
1147 GLfloat dest[][4])
1148 {
1149 GLint dstWidth, dstHeight;
1150 GLint i, j, n, m;
1151
1152 if (filterWidth >= 1)
1153 dstWidth = srcWidth - (filterWidth - 1);
1154 else
1155 dstWidth = srcWidth;
1156
1157 if (filterHeight >= 1)
1158 dstHeight = srcHeight - (filterHeight - 1);
1159 else
1160 dstHeight = srcHeight;
1161
1162 if (dstWidth <= 0 || dstHeight <= 0)
1163 return;
1164
1165 for (j = 0; j < dstHeight; j++) {
1166 for (i = 0; i < dstWidth; i++) {
1167 GLfloat sumR = 0.0;
1168 GLfloat sumG = 0.0;
1169 GLfloat sumB = 0.0;
1170 GLfloat sumA = 0.0;
1171 for (m = 0; m < filterHeight; m++) {
1172 for (n = 0; n < filterWidth; n++) {
1173 GLint k = (j + m) * srcWidth + i + n;
1174 sumR += src[k][RCOMP] * rowFilt[n][RCOMP] * colFilt[m][RCOMP];
1175 sumG += src[k][GCOMP] * rowFilt[n][GCOMP] * colFilt[m][GCOMP];
1176 sumB += src[k][BCOMP] * rowFilt[n][BCOMP] * colFilt[m][BCOMP];
1177 sumA += src[k][ACOMP] * rowFilt[n][ACOMP] * colFilt[m][ACOMP];
1178 }
1179 }
1180 dest[j * dstWidth + i][RCOMP] = sumR;
1181 dest[j * dstWidth + i][GCOMP] = sumG;
1182 dest[j * dstWidth + i][BCOMP] = sumB;
1183 dest[j * dstWidth + i][ACOMP] = sumA;
1184 }
1185 }
1186 }
1187
1188
1189 static void
1190 convolve_sep_constant(GLint srcWidth, GLint srcHeight,
1191 const GLfloat src[][4],
1192 GLint filterWidth, GLint filterHeight,
1193 const GLfloat rowFilt[][4],
1194 const GLfloat colFilt[][4],
1195 GLfloat dest[][4],
1196 const GLfloat borderColor[4])
1197 {
1198 const GLint halfFilterWidth = filterWidth / 2;
1199 const GLint halfFilterHeight = filterHeight / 2;
1200 GLint i, j, n, m;
1201
1202 for (j = 0; j < srcHeight; j++) {
1203 for (i = 0; i < srcWidth; i++) {
1204 GLfloat sumR = 0.0;
1205 GLfloat sumG = 0.0;
1206 GLfloat sumB = 0.0;
1207 GLfloat sumA = 0.0;
1208 for (m = 0; m < filterHeight; m++) {
1209 for (n = 0; n < filterWidth; n++) {
1210 const GLint is = i + n - halfFilterWidth;
1211 const GLint js = j + m - halfFilterHeight;
1212 if (is < 0 || is >= srcWidth ||
1213 js < 0 || js >= srcHeight) {
1214 sumR += borderColor[RCOMP] * rowFilt[n][RCOMP] * colFilt[m][RCOMP];
1215 sumG += borderColor[GCOMP] * rowFilt[n][GCOMP] * colFilt[m][GCOMP];
1216 sumB += borderColor[BCOMP] * rowFilt[n][BCOMP] * colFilt[m][BCOMP];
1217 sumA += borderColor[ACOMP] * rowFilt[n][ACOMP] * colFilt[m][ACOMP];
1218 }
1219 else {
1220 GLint k = js * srcWidth + is;
1221 sumR += src[k][RCOMP] * rowFilt[n][RCOMP] * colFilt[m][RCOMP];
1222 sumG += src[k][GCOMP] * rowFilt[n][GCOMP] * colFilt[m][GCOMP];
1223 sumB += src[k][BCOMP] * rowFilt[n][BCOMP] * colFilt[m][BCOMP];
1224 sumA += src[k][ACOMP] * rowFilt[n][ACOMP] * colFilt[m][ACOMP];
1225 }
1226
1227 }
1228 }
1229 dest[j * srcWidth + i][RCOMP] = sumR;
1230 dest[j * srcWidth + i][GCOMP] = sumG;
1231 dest[j * srcWidth + i][BCOMP] = sumB;
1232 dest[j * srcWidth + i][ACOMP] = sumA;
1233 }
1234 }
1235 }
1236
1237
1238 static void
1239 convolve_sep_replicate(GLint srcWidth, GLint srcHeight,
1240 const GLfloat src[][4],
1241 GLint filterWidth, GLint filterHeight,
1242 const GLfloat rowFilt[][4],
1243 const GLfloat colFilt[][4],
1244 GLfloat dest[][4])
1245 {
1246 const GLint halfFilterWidth = filterWidth / 2;
1247 const GLint halfFilterHeight = filterHeight / 2;
1248 GLint i, j, n, m;
1249
1250 for (j = 0; j < srcHeight; j++) {
1251 for (i = 0; i < srcWidth; i++) {
1252 GLfloat sumR = 0.0;
1253 GLfloat sumG = 0.0;
1254 GLfloat sumB = 0.0;
1255 GLfloat sumA = 0.0;
1256 for (m = 0; m < filterHeight; m++) {
1257 for (n = 0; n < filterWidth; n++) {
1258 GLint is = i + n - halfFilterWidth;
1259 GLint js = j + m - halfFilterHeight;
1260 GLint k;
1261 if (is < 0)
1262 is = 0;
1263 else if (is >= srcWidth)
1264 is = srcWidth - 1;
1265 if (js < 0)
1266 js = 0;
1267 else if (js >= srcHeight)
1268 js = srcHeight - 1;
1269 k = js * srcWidth + is;
1270 sumR += src[k][RCOMP] * rowFilt[n][RCOMP] * colFilt[m][RCOMP];
1271 sumG += src[k][GCOMP] * rowFilt[n][GCOMP] * colFilt[m][GCOMP];
1272 sumB += src[k][BCOMP] * rowFilt[n][BCOMP] * colFilt[m][BCOMP];
1273 sumA += src[k][ACOMP] * rowFilt[n][ACOMP] * colFilt[m][ACOMP];
1274 }
1275 }
1276 dest[j * srcWidth + i][RCOMP] = sumR;
1277 dest[j * srcWidth + i][GCOMP] = sumG;
1278 dest[j * srcWidth + i][BCOMP] = sumB;
1279 dest[j * srcWidth + i][ACOMP] = sumA;
1280 }
1281 }
1282 }
1283
1284
1285
1286 void
1287 _mesa_convolve_1d_image(const GLcontext *ctx, GLsizei *width,
1288 const GLfloat *srcImage, GLfloat *dstImage)
1289 {
1290 switch (ctx->Pixel.ConvolutionBorderMode[0]) {
1291 case GL_REDUCE:
1292 convolve_1d_reduce(*width, (const GLfloat (*)[4]) srcImage,
1293 ctx->Convolution1D.Width,
1294 (const GLfloat (*)[4]) ctx->Convolution1D.Filter,
1295 (GLfloat (*)[4]) dstImage);
1296 *width = *width - (MAX2(ctx->Convolution1D.Width, 1) - 1);
1297 break;
1298 case GL_CONSTANT_BORDER:
1299 convolve_1d_constant(*width, (const GLfloat (*)[4]) srcImage,
1300 ctx->Convolution1D.Width,
1301 (const GLfloat (*)[4]) ctx->Convolution1D.Filter,
1302 (GLfloat (*)[4]) dstImage,
1303 ctx->Pixel.ConvolutionBorderColor[0]);
1304 break;
1305 case GL_REPLICATE_BORDER:
1306 convolve_1d_replicate(*width, (const GLfloat (*)[4]) srcImage,
1307 ctx->Convolution1D.Width,
1308 (const GLfloat (*)[4]) ctx->Convolution1D.Filter,
1309 (GLfloat (*)[4]) dstImage);
1310 break;
1311 default:
1312 ;
1313 }
1314 }
1315
1316
1317 void
1318 _mesa_convolve_2d_image(const GLcontext *ctx, GLsizei *width, GLsizei *height,
1319 const GLfloat *srcImage, GLfloat *dstImage)
1320 {
1321 switch (ctx->Pixel.ConvolutionBorderMode[1]) {
1322 case GL_REDUCE:
1323 convolve_2d_reduce(*width, *height,
1324 (const GLfloat (*)[4]) srcImage,
1325 ctx->Convolution2D.Width,
1326 ctx->Convolution2D.Height,
1327 (const GLfloat (*)[4]) ctx->Convolution2D.Filter,
1328 (GLfloat (*)[4]) dstImage);
1329 *width = *width - (MAX2(ctx->Convolution2D.Width, 1) - 1);
1330 *height = *height - (MAX2(ctx->Convolution2D.Height, 1) - 1);
1331 break;
1332 case GL_CONSTANT_BORDER:
1333 convolve_2d_constant(*width, *height,
1334 (const GLfloat (*)[4]) srcImage,
1335 ctx->Convolution2D.Width,
1336 ctx->Convolution2D.Height,
1337 (const GLfloat (*)[4]) ctx->Convolution2D.Filter,
1338 (GLfloat (*)[4]) dstImage,
1339 ctx->Pixel.ConvolutionBorderColor[1]);
1340 break;
1341 case GL_REPLICATE_BORDER:
1342 convolve_2d_replicate(*width, *height,
1343 (const GLfloat (*)[4]) srcImage,
1344 ctx->Convolution2D.Width,
1345 ctx->Convolution2D.Height,
1346 (const GLfloat (*)[4])ctx->Convolution2D.Filter,
1347 (GLfloat (*)[4]) dstImage);
1348 break;
1349 default:
1350 ;
1351 }
1352 }
1353
1354
1355 void
1356 _mesa_convolve_sep_image(const GLcontext *ctx,
1357 GLsizei *width, GLsizei *height,
1358 const GLfloat *srcImage, GLfloat *dstImage)
1359 {
1360 const GLfloat *rowFilter = ctx->Separable2D.Filter;
1361 const GLfloat *colFilter = rowFilter + 4 * MAX_CONVOLUTION_WIDTH;
1362
1363 switch (ctx->Pixel.ConvolutionBorderMode[2]) {
1364 case GL_REDUCE:
1365 convolve_sep_reduce(*width, *height,
1366 (const GLfloat (*)[4]) srcImage,
1367 ctx->Separable2D.Width,
1368 ctx->Separable2D.Height,
1369 (const GLfloat (*)[4]) rowFilter,
1370 (const GLfloat (*)[4]) colFilter,
1371 (GLfloat (*)[4]) dstImage);
1372 *width = *width - (MAX2(ctx->Separable2D.Width, 1) - 1);
1373 *height = *height - (MAX2(ctx->Separable2D.Height, 1) - 1);
1374 break;
1375 case GL_CONSTANT_BORDER:
1376 convolve_sep_constant(*width, *height,
1377 (const GLfloat (*)[4]) srcImage,
1378 ctx->Separable2D.Width,
1379 ctx->Separable2D.Height,
1380 (const GLfloat (*)[4]) rowFilter,
1381 (const GLfloat (*)[4]) colFilter,
1382 (GLfloat (*)[4]) dstImage,
1383 ctx->Pixel.ConvolutionBorderColor[2]);
1384 break;
1385 case GL_REPLICATE_BORDER:
1386 convolve_sep_replicate(*width, *height,
1387 (const GLfloat (*)[4]) srcImage,
1388 ctx->Separable2D.Width,
1389 ctx->Separable2D.Height,
1390 (const GLfloat (*)[4]) rowFilter,
1391 (const GLfloat (*)[4]) colFilter,
1392 (GLfloat (*)[4]) dstImage);
1393 break;
1394 default:
1395 ;
1396 }
1397 }
1398
1399
1400
1401 /*
1402 * This function computes an image's size after convolution.
1403 * If the convolution border mode is GL_REDUCE, the post-convolution
1404 * image will be smaller than the original.
1405 */
1406 void
1407 _mesa_adjust_image_for_convolution(const GLcontext *ctx, GLuint dimensions,
1408 GLsizei *width, GLsizei *height)
1409 {
1410 if (ctx->Pixel.Convolution1DEnabled
1411 && dimensions == 1
1412 && ctx->Pixel.ConvolutionBorderMode[0] == GL_REDUCE) {
1413 *width = *width - (MAX2(ctx->Convolution1D.Width, 1) - 1);
1414 }
1415 else if (ctx->Pixel.Convolution2DEnabled
1416 && dimensions > 1
1417 && ctx->Pixel.ConvolutionBorderMode[1] == GL_REDUCE) {
1418 *width = *width - (MAX2(ctx->Convolution2D.Width, 1) - 1);
1419 *height = *height - (MAX2(ctx->Convolution2D.Height, 1) - 1);
1420 }
1421 else if (ctx->Pixel.Separable2DEnabled
1422 && dimensions > 1
1423 && ctx->Pixel.ConvolutionBorderMode[2] == GL_REDUCE) {
1424 *width = *width - (MAX2(ctx->Separable2D.Width, 1) - 1);
1425 *height = *height - (MAX2(ctx->Separable2D.Height, 1) - 1);
1426 }
1427 }