Merge branch 'mesa_7_5_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 if (ctx->Unpack.BufferObj->Name) {
148 /* unpack filter from PBO */
149 GLubyte *buf;
150 if (!_mesa_validate_pbo_access(1, &ctx->Unpack, width, 1, 1,
151 format, type, image)) {
152 _mesa_error(ctx, GL_INVALID_OPERATION,
153 "glConvolutionFilter1D(invalid PBO access)");
154 return;
155 }
156 buf = (GLubyte *) ctx->Driver.MapBuffer(ctx, GL_PIXEL_UNPACK_BUFFER_EXT,
157 GL_READ_ONLY_ARB,
158 ctx->Unpack.BufferObj);
159 if (!buf) {
160 /* buffer is already mapped - that's an error */
161 _mesa_error(ctx, GL_INVALID_OPERATION,
162 "glConvolutionFilter1D(PBO is mapped)");
163 return;
164 }
165 image = ADD_POINTERS(buf, image);
166 }
167 else if (!image) {
168 return;
169 }
170
171 _mesa_unpack_color_span_float(ctx, width, GL_RGBA,
172 ctx->Convolution1D.Filter,
173 format, type, image, &ctx->Unpack,
174 0); /* transferOps */
175
176 if (ctx->Unpack.BufferObj->Name) {
177 ctx->Driver.UnmapBuffer(ctx, GL_PIXEL_UNPACK_BUFFER_EXT,
178 ctx->Unpack.BufferObj);
179 }
180
181 _mesa_scale_and_bias_rgba(width,
182 (GLfloat (*)[4]) ctx->Convolution1D.Filter,
183 ctx->Pixel.ConvolutionFilterScale[0][0],
184 ctx->Pixel.ConvolutionFilterScale[0][1],
185 ctx->Pixel.ConvolutionFilterScale[0][2],
186 ctx->Pixel.ConvolutionFilterScale[0][3],
187 ctx->Pixel.ConvolutionFilterBias[0][0],
188 ctx->Pixel.ConvolutionFilterBias[0][1],
189 ctx->Pixel.ConvolutionFilterBias[0][2],
190 ctx->Pixel.ConvolutionFilterBias[0][3]);
191
192 ctx->NewState |= _NEW_PIXEL;
193 }
194
195
196 void GLAPIENTRY
197 _mesa_ConvolutionFilter2D(GLenum target, GLenum internalFormat, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *image)
198 {
199 GLint baseFormat;
200 GLint i;
201 GET_CURRENT_CONTEXT(ctx);
202 ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx);
203
204 if (target != GL_CONVOLUTION_2D) {
205 _mesa_error(ctx, GL_INVALID_ENUM, "glConvolutionFilter2D(target)");
206 return;
207 }
208
209 baseFormat = base_filter_format(internalFormat);
210 if (baseFormat < 0 || baseFormat == GL_COLOR_INDEX) {
211 _mesa_error(ctx, GL_INVALID_ENUM, "glConvolutionFilter2D(internalFormat)");
212 return;
213 }
214
215 if (width < 0 || width > MAX_CONVOLUTION_WIDTH) {
216 _mesa_error(ctx, GL_INVALID_VALUE, "glConvolutionFilter2D(width)");
217 return;
218 }
219 if (height < 0 || height > MAX_CONVOLUTION_HEIGHT) {
220 _mesa_error(ctx, GL_INVALID_VALUE, "glConvolutionFilter2D(height)");
221 return;
222 }
223
224 if (!_mesa_is_legal_format_and_type(ctx, format, type)) {
225 _mesa_error(ctx, GL_INVALID_OPERATION, "glConvolutionFilter2D(format or type)");
226 return;
227 }
228 if (format == GL_COLOR_INDEX ||
229 format == GL_STENCIL_INDEX ||
230 format == GL_DEPTH_COMPONENT ||
231 format == GL_INTENSITY ||
232 type == GL_BITMAP) {
233 _mesa_error(ctx, GL_INVALID_ENUM, "glConvolutionFilter2D(format or type)");
234 return;
235 }
236
237 /* this should have been caught earlier */
238 assert(_mesa_components_in_format(format));
239
240 ctx->Convolution2D.Format = format;
241 ctx->Convolution2D.InternalFormat = internalFormat;
242 ctx->Convolution2D.Width = width;
243 ctx->Convolution2D.Height = height;
244
245 if (ctx->Unpack.BufferObj->Name) {
246 /* unpack filter from PBO */
247 GLubyte *buf;
248 if (!_mesa_validate_pbo_access(2, &ctx->Unpack, width, height, 1,
249 format, type, image)) {
250 _mesa_error(ctx, GL_INVALID_OPERATION,
251 "glConvolutionFilter2D(invalid PBO access)");
252 return;
253 }
254 buf = (GLubyte *) ctx->Driver.MapBuffer(ctx, GL_PIXEL_UNPACK_BUFFER_EXT,
255 GL_READ_ONLY_ARB,
256 ctx->Unpack.BufferObj);
257 if (!buf) {
258 /* buffer is already mapped - that's an error */
259 _mesa_error(ctx, GL_INVALID_OPERATION,
260 "glConvolutionFilter2D(PBO is mapped)");
261 return;
262 }
263 image = ADD_POINTERS(buf, image);
264 }
265 else if (!image) {
266 return;
267 }
268
269 /* Unpack filter image. We always store filters in RGBA format. */
270 for (i = 0; i < height; i++) {
271 const GLvoid *src = _mesa_image_address2d(&ctx->Unpack, image, width,
272 height, format, type, i, 0);
273 GLfloat *dst = ctx->Convolution2D.Filter + i * width * 4;
274 _mesa_unpack_color_span_float(ctx, width, GL_RGBA, dst,
275 format, type, src, &ctx->Unpack,
276 0); /* transferOps */
277 }
278
279 if (ctx->Unpack.BufferObj->Name) {
280 ctx->Driver.UnmapBuffer(ctx, GL_PIXEL_UNPACK_BUFFER_EXT,
281 ctx->Unpack.BufferObj);
282 }
283
284 _mesa_scale_and_bias_rgba(width * height,
285 (GLfloat (*)[4]) ctx->Convolution2D.Filter,
286 ctx->Pixel.ConvolutionFilterScale[1][0],
287 ctx->Pixel.ConvolutionFilterScale[1][1],
288 ctx->Pixel.ConvolutionFilterScale[1][2],
289 ctx->Pixel.ConvolutionFilterScale[1][3],
290 ctx->Pixel.ConvolutionFilterBias[1][0],
291 ctx->Pixel.ConvolutionFilterBias[1][1],
292 ctx->Pixel.ConvolutionFilterBias[1][2],
293 ctx->Pixel.ConvolutionFilterBias[1][3]);
294
295 ctx->NewState |= _NEW_PIXEL;
296 }
297
298
299 void GLAPIENTRY
300 _mesa_ConvolutionParameterf(GLenum target, GLenum pname, GLfloat param)
301 {
302 GET_CURRENT_CONTEXT(ctx);
303 GLuint c;
304 ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx);
305
306 switch (target) {
307 case GL_CONVOLUTION_1D:
308 c = 0;
309 break;
310 case GL_CONVOLUTION_2D:
311 c = 1;
312 break;
313 case GL_SEPARABLE_2D:
314 c = 2;
315 break;
316 default:
317 _mesa_error(ctx, GL_INVALID_ENUM, "glConvolutionParameterf(target)");
318 return;
319 }
320
321 switch (pname) {
322 case GL_CONVOLUTION_BORDER_MODE:
323 if (param == (GLfloat) GL_REDUCE ||
324 param == (GLfloat) GL_CONSTANT_BORDER ||
325 param == (GLfloat) GL_REPLICATE_BORDER) {
326 ctx->Pixel.ConvolutionBorderMode[c] = (GLenum) param;
327 }
328 else {
329 _mesa_error(ctx, GL_INVALID_ENUM, "glConvolutionParameterf(params)");
330 return;
331 }
332 break;
333 default:
334 _mesa_error(ctx, GL_INVALID_ENUM, "glConvolutionParameterf(pname)");
335 return;
336 }
337
338 ctx->NewState |= _NEW_PIXEL;
339 }
340
341
342 void GLAPIENTRY
343 _mesa_ConvolutionParameterfv(GLenum target, GLenum pname, const GLfloat *params)
344 {
345 GET_CURRENT_CONTEXT(ctx);
346 GLuint c;
347 ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx);
348
349 switch (target) {
350 case GL_CONVOLUTION_1D:
351 c = 0;
352 break;
353 case GL_CONVOLUTION_2D:
354 c = 1;
355 break;
356 case GL_SEPARABLE_2D:
357 c = 2;
358 break;
359 default:
360 _mesa_error(ctx, GL_INVALID_ENUM, "glConvolutionParameterfv(target)");
361 return;
362 }
363
364 switch (pname) {
365 case GL_CONVOLUTION_BORDER_COLOR:
366 COPY_4V(ctx->Pixel.ConvolutionBorderColor[c], params);
367 break;
368 case GL_CONVOLUTION_BORDER_MODE:
369 if (params[0] == (GLfloat) GL_REDUCE ||
370 params[0] == (GLfloat) GL_CONSTANT_BORDER ||
371 params[0] == (GLfloat) GL_REPLICATE_BORDER) {
372 ctx->Pixel.ConvolutionBorderMode[c] = (GLenum) params[0];
373 }
374 else {
375 _mesa_error(ctx, GL_INVALID_ENUM, "glConvolutionParameterfv(params)");
376 return;
377 }
378 break;
379 case GL_CONVOLUTION_FILTER_SCALE:
380 COPY_4V(ctx->Pixel.ConvolutionFilterScale[c], params);
381 break;
382 case GL_CONVOLUTION_FILTER_BIAS:
383 COPY_4V(ctx->Pixel.ConvolutionFilterBias[c], params);
384 break;
385 default:
386 _mesa_error(ctx, GL_INVALID_ENUM, "glConvolutionParameterfv(pname)");
387 return;
388 }
389
390 ctx->NewState |= _NEW_PIXEL;
391 }
392
393
394 void GLAPIENTRY
395 _mesa_ConvolutionParameteri(GLenum target, GLenum pname, GLint param)
396 {
397 GET_CURRENT_CONTEXT(ctx);
398 GLuint c;
399 ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx);
400
401 switch (target) {
402 case GL_CONVOLUTION_1D:
403 c = 0;
404 break;
405 case GL_CONVOLUTION_2D:
406 c = 1;
407 break;
408 case GL_SEPARABLE_2D:
409 c = 2;
410 break;
411 default:
412 _mesa_error(ctx, GL_INVALID_ENUM, "glConvolutionParameteri(target)");
413 return;
414 }
415
416 switch (pname) {
417 case GL_CONVOLUTION_BORDER_MODE:
418 if (param == (GLint) GL_REDUCE ||
419 param == (GLint) GL_CONSTANT_BORDER ||
420 param == (GLint) GL_REPLICATE_BORDER) {
421 ctx->Pixel.ConvolutionBorderMode[c] = (GLenum) param;
422 }
423 else {
424 _mesa_error(ctx, GL_INVALID_ENUM, "glConvolutionParameteri(params)");
425 return;
426 }
427 break;
428 default:
429 _mesa_error(ctx, GL_INVALID_ENUM, "glConvolutionParameteri(pname)");
430 return;
431 }
432
433 ctx->NewState |= _NEW_PIXEL;
434 }
435
436
437 void GLAPIENTRY
438 _mesa_ConvolutionParameteriv(GLenum target, GLenum pname, const GLint *params)
439 {
440 GET_CURRENT_CONTEXT(ctx);
441 GLuint c;
442 ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx);
443
444 switch (target) {
445 case GL_CONVOLUTION_1D:
446 c = 0;
447 break;
448 case GL_CONVOLUTION_2D:
449 c = 1;
450 break;
451 case GL_SEPARABLE_2D:
452 c = 2;
453 break;
454 default:
455 _mesa_error(ctx, GL_INVALID_ENUM, "glConvolutionParameteriv(target)");
456 return;
457 }
458
459 switch (pname) {
460 case GL_CONVOLUTION_BORDER_COLOR:
461 ctx->Pixel.ConvolutionBorderColor[c][0] = INT_TO_FLOAT(params[0]);
462 ctx->Pixel.ConvolutionBorderColor[c][1] = INT_TO_FLOAT(params[1]);
463 ctx->Pixel.ConvolutionBorderColor[c][2] = INT_TO_FLOAT(params[2]);
464 ctx->Pixel.ConvolutionBorderColor[c][3] = INT_TO_FLOAT(params[3]);
465 break;
466 case GL_CONVOLUTION_BORDER_MODE:
467 if (params[0] == (GLint) GL_REDUCE ||
468 params[0] == (GLint) GL_CONSTANT_BORDER ||
469 params[0] == (GLint) GL_REPLICATE_BORDER) {
470 ctx->Pixel.ConvolutionBorderMode[c] = (GLenum) params[0];
471 }
472 else {
473 _mesa_error(ctx, GL_INVALID_ENUM, "glConvolutionParameteriv(params)");
474 return;
475 }
476 break;
477 case GL_CONVOLUTION_FILTER_SCALE:
478 /* COPY_4V(ctx->Pixel.ConvolutionFilterScale[c], params); */
479 /* need cast to prevent compiler warnings */
480 ctx->Pixel.ConvolutionFilterScale[c][0] = (GLfloat) params[0];
481 ctx->Pixel.ConvolutionFilterScale[c][1] = (GLfloat) params[1];
482 ctx->Pixel.ConvolutionFilterScale[c][2] = (GLfloat) params[2];
483 ctx->Pixel.ConvolutionFilterScale[c][3] = (GLfloat) params[3];
484 break;
485 case GL_CONVOLUTION_FILTER_BIAS:
486 /* COPY_4V(ctx->Pixel.ConvolutionFilterBias[c], params); */
487 /* need cast to prevent compiler warnings */
488 ctx->Pixel.ConvolutionFilterBias[c][0] = (GLfloat) params[0];
489 ctx->Pixel.ConvolutionFilterBias[c][1] = (GLfloat) params[1];
490 ctx->Pixel.ConvolutionFilterBias[c][2] = (GLfloat) params[2];
491 ctx->Pixel.ConvolutionFilterBias[c][3] = (GLfloat) params[3];
492 break;
493 default:
494 _mesa_error(ctx, GL_INVALID_ENUM, "glConvolutionParameteriv(pname)");
495 return;
496 }
497
498 ctx->NewState |= _NEW_PIXEL;
499 }
500
501
502 void GLAPIENTRY
503 _mesa_CopyConvolutionFilter1D(GLenum target, GLenum internalFormat, GLint x, GLint y, GLsizei width)
504 {
505 GLint baseFormat;
506 GET_CURRENT_CONTEXT(ctx);
507 ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx);
508
509 if (target != GL_CONVOLUTION_1D) {
510 _mesa_error(ctx, GL_INVALID_ENUM, "glCopyConvolutionFilter1D(target)");
511 return;
512 }
513
514 baseFormat = base_filter_format(internalFormat);
515 if (baseFormat < 0 || baseFormat == GL_COLOR_INDEX) {
516 _mesa_error(ctx, GL_INVALID_ENUM, "glCopyConvolutionFilter1D(internalFormat)");
517 return;
518 }
519
520 if (width < 0 || width > MAX_CONVOLUTION_WIDTH) {
521 _mesa_error(ctx, GL_INVALID_VALUE, "glCopyConvolutionFilter1D(width)");
522 return;
523 }
524
525 ctx->Driver.CopyConvolutionFilter1D( ctx, target,
526 internalFormat, x, y, width);
527 }
528
529
530 void GLAPIENTRY
531 _mesa_CopyConvolutionFilter2D(GLenum target, GLenum internalFormat, GLint x, GLint y, GLsizei width, GLsizei height)
532 {
533 GLint baseFormat;
534 GET_CURRENT_CONTEXT(ctx);
535 ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx);
536
537 if (target != GL_CONVOLUTION_2D) {
538 _mesa_error(ctx, GL_INVALID_ENUM, "glCopyConvolutionFilter2D(target)");
539 return;
540 }
541
542 baseFormat = base_filter_format(internalFormat);
543 if (baseFormat < 0 || baseFormat == GL_COLOR_INDEX) {
544 _mesa_error(ctx, GL_INVALID_ENUM, "glCopyConvolutionFilter2D(internalFormat)");
545 return;
546 }
547
548 if (width < 0 || width > MAX_CONVOLUTION_WIDTH) {
549 _mesa_error(ctx, GL_INVALID_VALUE, "glCopyConvolutionFilter2D(width)");
550 return;
551 }
552 if (height < 0 || height > MAX_CONVOLUTION_HEIGHT) {
553 _mesa_error(ctx, GL_INVALID_VALUE, "glCopyConvolutionFilter2D(height)");
554 return;
555 }
556
557 ctx->Driver.CopyConvolutionFilter2D( ctx, target, internalFormat, x, y,
558 width, height );
559 }
560
561
562 void GLAPIENTRY
563 _mesa_GetConvolutionFilter(GLenum target, GLenum format, GLenum type,
564 GLvoid *image)
565 {
566 struct gl_convolution_attrib *filter;
567 GLuint row;
568 GET_CURRENT_CONTEXT(ctx);
569 ASSERT_OUTSIDE_BEGIN_END(ctx);
570
571 if (ctx->NewState) {
572 _mesa_update_state(ctx);
573 }
574
575 if (!_mesa_is_legal_format_and_type(ctx, format, type)) {
576 _mesa_error(ctx, GL_INVALID_OPERATION, "glGetConvolutionFilter(format or type)");
577 return;
578 }
579
580 if (format == GL_COLOR_INDEX ||
581 format == GL_STENCIL_INDEX ||
582 format == GL_DEPTH_COMPONENT ||
583 format == GL_INTENSITY ||
584 type == GL_BITMAP) {
585 _mesa_error(ctx, GL_INVALID_ENUM, "glGetConvolutionFilter(format or type)");
586 return;
587 }
588
589 switch (target) {
590 case GL_CONVOLUTION_1D:
591 filter = &(ctx->Convolution1D);
592 break;
593 case GL_CONVOLUTION_2D:
594 filter = &(ctx->Convolution2D);
595 break;
596 default:
597 _mesa_error(ctx, GL_INVALID_ENUM, "glGetConvolutionFilter(target)");
598 return;
599 }
600
601 if (ctx->Pack.BufferObj->Name) {
602 /* Pack the filter into a PBO */
603 GLubyte *buf;
604 if (!_mesa_validate_pbo_access(2, &ctx->Pack,
605 filter->Width, filter->Height,
606 1, format, type, image)) {
607 _mesa_error(ctx, GL_INVALID_OPERATION,
608 "glGetConvolutionFilter(invalid PBO access)");
609 return;
610 }
611 buf = (GLubyte *) ctx->Driver.MapBuffer(ctx, GL_PIXEL_PACK_BUFFER_EXT,
612 GL_WRITE_ONLY_ARB,
613 ctx->Pack.BufferObj);
614 if (!buf) {
615 /* buffer is already mapped - that's an error */
616 _mesa_error(ctx, GL_INVALID_OPERATION,
617 "glGetConvolutionFilter(PBO is mapped)");
618 return;
619 }
620 image = ADD_POINTERS(image, buf);
621 }
622
623 for (row = 0; row < filter->Height; row++) {
624 GLvoid *dst = _mesa_image_address2d(&ctx->Pack, image, filter->Width,
625 filter->Height, format, type,
626 row, 0);
627 GLfloat (*src)[4] = (GLfloat (*)[4]) (filter->Filter + row * filter->Width * 4);
628 _mesa_pack_rgba_span_float(ctx, filter->Width, src,
629 format, type, dst, &ctx->Pack, 0x0);
630 }
631
632 if (ctx->Pack.BufferObj->Name) {
633 ctx->Driver.UnmapBuffer(ctx, GL_PIXEL_PACK_BUFFER_EXT,
634 ctx->Pack.BufferObj);
635 }
636 }
637
638
639 void GLAPIENTRY
640 _mesa_GetConvolutionParameterfv(GLenum target, GLenum pname, GLfloat *params)
641 {
642 GET_CURRENT_CONTEXT(ctx);
643 const struct gl_convolution_attrib *conv;
644 GLuint c;
645 ASSERT_OUTSIDE_BEGIN_END(ctx);
646
647 switch (target) {
648 case GL_CONVOLUTION_1D:
649 c = 0;
650 conv = &ctx->Convolution1D;
651 break;
652 case GL_CONVOLUTION_2D:
653 c = 1;
654 conv = &ctx->Convolution2D;
655 break;
656 case GL_SEPARABLE_2D:
657 c = 2;
658 conv = &ctx->Separable2D;
659 break;
660 default:
661 _mesa_error(ctx, GL_INVALID_ENUM, "glGetConvolutionParameterfv(target)");
662 return;
663 }
664
665 switch (pname) {
666 case GL_CONVOLUTION_BORDER_COLOR:
667 COPY_4V(params, ctx->Pixel.ConvolutionBorderColor[c]);
668 break;
669 case GL_CONVOLUTION_BORDER_MODE:
670 *params = (GLfloat) ctx->Pixel.ConvolutionBorderMode[c];
671 break;
672 case GL_CONVOLUTION_FILTER_SCALE:
673 COPY_4V(params, ctx->Pixel.ConvolutionFilterScale[c]);
674 break;
675 case GL_CONVOLUTION_FILTER_BIAS:
676 COPY_4V(params, ctx->Pixel.ConvolutionFilterBias[c]);
677 break;
678 case GL_CONVOLUTION_FORMAT:
679 *params = (GLfloat) conv->Format;
680 break;
681 case GL_CONVOLUTION_WIDTH:
682 *params = (GLfloat) conv->Width;
683 break;
684 case GL_CONVOLUTION_HEIGHT:
685 *params = (GLfloat) conv->Height;
686 break;
687 case GL_MAX_CONVOLUTION_WIDTH:
688 *params = (GLfloat) ctx->Const.MaxConvolutionWidth;
689 break;
690 case GL_MAX_CONVOLUTION_HEIGHT:
691 *params = (GLfloat) ctx->Const.MaxConvolutionHeight;
692 break;
693 default:
694 _mesa_error(ctx, GL_INVALID_ENUM, "glGetConvolutionParameterfv(pname)");
695 return;
696 }
697 }
698
699
700 void GLAPIENTRY
701 _mesa_GetConvolutionParameteriv(GLenum target, GLenum pname, GLint *params)
702 {
703 GET_CURRENT_CONTEXT(ctx);
704 const struct gl_convolution_attrib *conv;
705 GLuint c;
706 ASSERT_OUTSIDE_BEGIN_END(ctx);
707
708 switch (target) {
709 case GL_CONVOLUTION_1D:
710 c = 0;
711 conv = &ctx->Convolution1D;
712 break;
713 case GL_CONVOLUTION_2D:
714 c = 1;
715 conv = &ctx->Convolution2D;
716 break;
717 case GL_SEPARABLE_2D:
718 c = 2;
719 conv = &ctx->Separable2D;
720 break;
721 default:
722 _mesa_error(ctx, GL_INVALID_ENUM, "glGetConvolutionParameteriv(target)");
723 return;
724 }
725
726 switch (pname) {
727 case GL_CONVOLUTION_BORDER_COLOR:
728 params[0] = FLOAT_TO_INT(ctx->Pixel.ConvolutionBorderColor[c][0]);
729 params[1] = FLOAT_TO_INT(ctx->Pixel.ConvolutionBorderColor[c][1]);
730 params[2] = FLOAT_TO_INT(ctx->Pixel.ConvolutionBorderColor[c][2]);
731 params[3] = FLOAT_TO_INT(ctx->Pixel.ConvolutionBorderColor[c][3]);
732 break;
733 case GL_CONVOLUTION_BORDER_MODE:
734 *params = (GLint) ctx->Pixel.ConvolutionBorderMode[c];
735 break;
736 case GL_CONVOLUTION_FILTER_SCALE:
737 params[0] = (GLint) ctx->Pixel.ConvolutionFilterScale[c][0];
738 params[1] = (GLint) ctx->Pixel.ConvolutionFilterScale[c][1];
739 params[2] = (GLint) ctx->Pixel.ConvolutionFilterScale[c][2];
740 params[3] = (GLint) ctx->Pixel.ConvolutionFilterScale[c][3];
741 break;
742 case GL_CONVOLUTION_FILTER_BIAS:
743 params[0] = (GLint) ctx->Pixel.ConvolutionFilterBias[c][0];
744 params[1] = (GLint) ctx->Pixel.ConvolutionFilterBias[c][1];
745 params[2] = (GLint) ctx->Pixel.ConvolutionFilterBias[c][2];
746 params[3] = (GLint) ctx->Pixel.ConvolutionFilterBias[c][3];
747 break;
748 case GL_CONVOLUTION_FORMAT:
749 *params = (GLint) conv->Format;
750 break;
751 case GL_CONVOLUTION_WIDTH:
752 *params = (GLint) conv->Width;
753 break;
754 case GL_CONVOLUTION_HEIGHT:
755 *params = (GLint) conv->Height;
756 break;
757 case GL_MAX_CONVOLUTION_WIDTH:
758 *params = (GLint) ctx->Const.MaxConvolutionWidth;
759 break;
760 case GL_MAX_CONVOLUTION_HEIGHT:
761 *params = (GLint) ctx->Const.MaxConvolutionHeight;
762 break;
763 default:
764 _mesa_error(ctx, GL_INVALID_ENUM, "glGetConvolutionParameteriv(pname)");
765 return;
766 }
767 }
768
769
770 void GLAPIENTRY
771 _mesa_GetSeparableFilter(GLenum target, GLenum format, GLenum type,
772 GLvoid *row, GLvoid *column, GLvoid *span)
773 {
774 const GLint colStart = MAX_CONVOLUTION_WIDTH * 4;
775 struct gl_convolution_attrib *filter;
776 GET_CURRENT_CONTEXT(ctx);
777 ASSERT_OUTSIDE_BEGIN_END(ctx);
778
779 if (ctx->NewState) {
780 _mesa_update_state(ctx);
781 }
782
783 if (target != GL_SEPARABLE_2D) {
784 _mesa_error(ctx, GL_INVALID_ENUM, "glGetSeparableFilter(target)");
785 return;
786 }
787
788 if (!_mesa_is_legal_format_and_type(ctx, format, type)) {
789 _mesa_error(ctx, GL_INVALID_OPERATION,
790 "glGetConvolutionFilter(format or type)");
791 return;
792 }
793
794 if (format == GL_COLOR_INDEX ||
795 format == GL_STENCIL_INDEX ||
796 format == GL_DEPTH_COMPONENT ||
797 format == GL_INTENSITY ||
798 type == GL_BITMAP) {
799 _mesa_error(ctx, GL_INVALID_ENUM, "glGetConvolutionFilter(format or type)");
800 return;
801 }
802
803 filter = &ctx->Separable2D;
804
805 if (ctx->Pack.BufferObj->Name) {
806 /* Pack filter into PBO */
807 GLubyte *buf;
808 if (!_mesa_validate_pbo_access(1, &ctx->Pack, filter->Width, 1, 1,
809 format, type, row)) {
810 _mesa_error(ctx, GL_INVALID_OPERATION,
811 "glGetSeparableFilter(invalid PBO access, width)");
812 return;
813 }
814 if (!_mesa_validate_pbo_access(1, &ctx->Pack, filter->Height, 1, 1,
815 format, type, column)) {
816 _mesa_error(ctx, GL_INVALID_OPERATION,
817 "glGetSeparableFilter(invalid PBO access, height)");
818 return;
819 }
820 buf = (GLubyte *) ctx->Driver.MapBuffer(ctx, GL_PIXEL_PACK_BUFFER_EXT,
821 GL_WRITE_ONLY_ARB,
822 ctx->Pack.BufferObj);
823 if (!buf) {
824 /* buffer is already mapped - that's an error */
825 _mesa_error(ctx, GL_INVALID_OPERATION,
826 "glGetSeparableFilter(PBO is mapped)");
827 return;
828 }
829 row = ADD_POINTERS(buf, row);
830 column = ADD_POINTERS(buf, column);
831 }
832
833 /* Row filter */
834 if (row) {
835 GLvoid *dst = _mesa_image_address1d(&ctx->Pack, row, filter->Width,
836 format, type, 0);
837 _mesa_pack_rgba_span_float(ctx, filter->Width,
838 (GLfloat (*)[4]) filter->Filter,
839 format, type, dst, &ctx->Pack, 0x0);
840 }
841
842 /* Column filter */
843 if (column) {
844 GLvoid *dst = _mesa_image_address1d(&ctx->Pack, column, filter->Height,
845 format, type, 0);
846 GLfloat (*src)[4] = (GLfloat (*)[4]) (filter->Filter + colStart);
847 _mesa_pack_rgba_span_float(ctx, filter->Height, src,
848 format, type, dst, &ctx->Pack, 0x0);
849 }
850
851 (void) span; /* unused at this time */
852
853 if (ctx->Pack.BufferObj->Name) {
854 /* Pack filter into PBO */
855 ctx->Driver.UnmapBuffer(ctx, GL_PIXEL_UNPACK_BUFFER_EXT,
856 ctx->Unpack.BufferObj);
857 }
858 }
859
860
861 void GLAPIENTRY
862 _mesa_SeparableFilter2D(GLenum target, GLenum internalFormat, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *row, const GLvoid *column)
863 {
864 const GLint colStart = MAX_CONVOLUTION_WIDTH * 4;
865 GLint baseFormat;
866 GET_CURRENT_CONTEXT(ctx);
867 ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx);
868
869 if (target != GL_SEPARABLE_2D) {
870 _mesa_error(ctx, GL_INVALID_ENUM, "glSeparableFilter2D(target)");
871 return;
872 }
873
874 baseFormat = base_filter_format(internalFormat);
875 if (baseFormat < 0 || baseFormat == GL_COLOR_INDEX) {
876 _mesa_error(ctx, GL_INVALID_ENUM, "glSeparableFilter2D(internalFormat)");
877 return;
878 }
879
880 if (width < 0 || width > MAX_CONVOLUTION_WIDTH) {
881 _mesa_error(ctx, GL_INVALID_VALUE, "glSeparableFilter2D(width)");
882 return;
883 }
884 if (height < 0 || height > MAX_CONVOLUTION_HEIGHT) {
885 _mesa_error(ctx, GL_INVALID_VALUE, "glSeparableFilter2D(height)");
886 return;
887 }
888
889 if (!_mesa_is_legal_format_and_type(ctx, format, type)) {
890 _mesa_error(ctx, GL_INVALID_OPERATION, "glSeparableFilter2D(format or type)");
891 return;
892 }
893
894 if (format == GL_COLOR_INDEX ||
895 format == GL_STENCIL_INDEX ||
896 format == GL_DEPTH_COMPONENT ||
897 format == GL_INTENSITY ||
898 type == GL_BITMAP) {
899 _mesa_error(ctx, GL_INVALID_ENUM, "glSeparableFilter2D(format or type)");
900 return;
901 }
902
903 ctx->Separable2D.Format = format;
904 ctx->Separable2D.InternalFormat = internalFormat;
905 ctx->Separable2D.Width = width;
906 ctx->Separable2D.Height = height;
907
908 if (ctx->Unpack.BufferObj->Name) {
909 /* unpack filter from PBO */
910 GLubyte *buf;
911 if (!_mesa_validate_pbo_access(1, &ctx->Unpack, width, 1, 1,
912 format, type, row)) {
913 _mesa_error(ctx, GL_INVALID_OPERATION,
914 "glSeparableFilter2D(invalid PBO access, width)");
915 return;
916 }
917 if (!_mesa_validate_pbo_access(1, &ctx->Unpack, height, 1, 1,
918 format, type, column)) {
919 _mesa_error(ctx, GL_INVALID_OPERATION,
920 "glSeparableFilter2D(invalid PBO access, height)");
921 return;
922 }
923 buf = (GLubyte *) ctx->Driver.MapBuffer(ctx, GL_PIXEL_UNPACK_BUFFER_EXT,
924 GL_READ_ONLY_ARB,
925 ctx->Unpack.BufferObj);
926 if (!buf) {
927 /* buffer is already mapped - that's an error */
928 _mesa_error(ctx, GL_INVALID_OPERATION,
929 "glSeparableFilter2D(PBO is mapped)");
930 return;
931 }
932 row = ADD_POINTERS(buf, row);
933 column = ADD_POINTERS(buf, column);
934 }
935
936 /* unpack row filter */
937 if (row) {
938 _mesa_unpack_color_span_float(ctx, width, GL_RGBA,
939 ctx->Separable2D.Filter,
940 format, type, row, &ctx->Unpack,
941 0); /* transferOps */
942
943 _mesa_scale_and_bias_rgba(width,
944 (GLfloat (*)[4]) ctx->Separable2D.Filter,
945 ctx->Pixel.ConvolutionFilterScale[2][0],
946 ctx->Pixel.ConvolutionFilterScale[2][1],
947 ctx->Pixel.ConvolutionFilterScale[2][2],
948 ctx->Pixel.ConvolutionFilterScale[2][3],
949 ctx->Pixel.ConvolutionFilterBias[2][0],
950 ctx->Pixel.ConvolutionFilterBias[2][1],
951 ctx->Pixel.ConvolutionFilterBias[2][2],
952 ctx->Pixel.ConvolutionFilterBias[2][3]);
953 }
954
955 /* unpack column filter */
956 if (column) {
957 _mesa_unpack_color_span_float(ctx, height, GL_RGBA,
958 &ctx->Separable2D.Filter[colStart],
959 format, type, column, &ctx->Unpack,
960 0); /* transferOps */
961
962 _mesa_scale_and_bias_rgba(height,
963 (GLfloat (*)[4]) (ctx->Separable2D.Filter + colStart),
964 ctx->Pixel.ConvolutionFilterScale[2][0],
965 ctx->Pixel.ConvolutionFilterScale[2][1],
966 ctx->Pixel.ConvolutionFilterScale[2][2],
967 ctx->Pixel.ConvolutionFilterScale[2][3],
968 ctx->Pixel.ConvolutionFilterBias[2][0],
969 ctx->Pixel.ConvolutionFilterBias[2][1],
970 ctx->Pixel.ConvolutionFilterBias[2][2],
971 ctx->Pixel.ConvolutionFilterBias[2][3]);
972 }
973
974 if (ctx->Unpack.BufferObj->Name) {
975 ctx->Driver.UnmapBuffer(ctx, GL_PIXEL_UNPACK_BUFFER_EXT,
976 ctx->Unpack.BufferObj);
977 }
978
979 ctx->NewState |= _NEW_PIXEL;
980 }
981
982
983 /**********************************************************************/
984 /*** image convolution functions ***/
985 /**********************************************************************/
986
987 static void
988 convolve_1d_reduce(GLint srcWidth, const GLfloat src[][4],
989 GLint filterWidth, const GLfloat filter[][4],
990 GLfloat dest[][4])
991 {
992 GLint dstWidth;
993 GLint i, n;
994
995 if (filterWidth >= 1)
996 dstWidth = srcWidth - (filterWidth - 1);
997 else
998 dstWidth = srcWidth;
999
1000 if (dstWidth <= 0)
1001 return; /* null result */
1002
1003 for (i = 0; i < dstWidth; i++) {
1004 GLfloat sumR = 0.0;
1005 GLfloat sumG = 0.0;
1006 GLfloat sumB = 0.0;
1007 GLfloat sumA = 0.0;
1008 for (n = 0; n < filterWidth; n++) {
1009 sumR += src[i + n][RCOMP] * filter[n][RCOMP];
1010 sumG += src[i + n][GCOMP] * filter[n][GCOMP];
1011 sumB += src[i + n][BCOMP] * filter[n][BCOMP];
1012 sumA += src[i + n][ACOMP] * filter[n][ACOMP];
1013 }
1014 dest[i][RCOMP] = sumR;
1015 dest[i][GCOMP] = sumG;
1016 dest[i][BCOMP] = sumB;
1017 dest[i][ACOMP] = sumA;
1018 }
1019 }
1020
1021
1022 static void
1023 convolve_1d_constant(GLint srcWidth, const GLfloat src[][4],
1024 GLint filterWidth, const GLfloat filter[][4],
1025 GLfloat dest[][4],
1026 const GLfloat borderColor[4])
1027 {
1028 const GLint halfFilterWidth = filterWidth / 2;
1029 GLint i, n;
1030
1031 for (i = 0; i < srcWidth; i++) {
1032 GLfloat sumR = 0.0;
1033 GLfloat sumG = 0.0;
1034 GLfloat sumB = 0.0;
1035 GLfloat sumA = 0.0;
1036 for (n = 0; n < filterWidth; n++) {
1037 if (i + n < halfFilterWidth || i + n - halfFilterWidth >= srcWidth) {
1038 sumR += borderColor[RCOMP] * filter[n][RCOMP];
1039 sumG += borderColor[GCOMP] * filter[n][GCOMP];
1040 sumB += borderColor[BCOMP] * filter[n][BCOMP];
1041 sumA += borderColor[ACOMP] * filter[n][ACOMP];
1042 }
1043 else {
1044 sumR += src[i + n - halfFilterWidth][RCOMP] * filter[n][RCOMP];
1045 sumG += src[i + n - halfFilterWidth][GCOMP] * filter[n][GCOMP];
1046 sumB += src[i + n - halfFilterWidth][BCOMP] * filter[n][BCOMP];
1047 sumA += src[i + n - halfFilterWidth][ACOMP] * filter[n][ACOMP];
1048 }
1049 }
1050 dest[i][RCOMP] = sumR;
1051 dest[i][GCOMP] = sumG;
1052 dest[i][BCOMP] = sumB;
1053 dest[i][ACOMP] = sumA;
1054 }
1055 }
1056
1057
1058 static void
1059 convolve_1d_replicate(GLint srcWidth, const GLfloat src[][4],
1060 GLint filterWidth, const GLfloat filter[][4],
1061 GLfloat dest[][4])
1062 {
1063 const GLint halfFilterWidth = filterWidth / 2;
1064 GLint i, n;
1065
1066 for (i = 0; i < srcWidth; i++) {
1067 GLfloat sumR = 0.0;
1068 GLfloat sumG = 0.0;
1069 GLfloat sumB = 0.0;
1070 GLfloat sumA = 0.0;
1071 for (n = 0; n < filterWidth; n++) {
1072 if (i + n < halfFilterWidth) {
1073 sumR += src[0][RCOMP] * filter[n][RCOMP];
1074 sumG += src[0][GCOMP] * filter[n][GCOMP];
1075 sumB += src[0][BCOMP] * filter[n][BCOMP];
1076 sumA += src[0][ACOMP] * filter[n][ACOMP];
1077 }
1078 else if (i + n - halfFilterWidth >= srcWidth) {
1079 sumR += src[srcWidth - 1][RCOMP] * filter[n][RCOMP];
1080 sumG += src[srcWidth - 1][GCOMP] * filter[n][GCOMP];
1081 sumB += src[srcWidth - 1][BCOMP] * filter[n][BCOMP];
1082 sumA += src[srcWidth - 1][ACOMP] * filter[n][ACOMP];
1083 }
1084 else {
1085 sumR += src[i + n - halfFilterWidth][RCOMP] * filter[n][RCOMP];
1086 sumG += src[i + n - halfFilterWidth][GCOMP] * filter[n][GCOMP];
1087 sumB += src[i + n - halfFilterWidth][BCOMP] * filter[n][BCOMP];
1088 sumA += src[i + n - halfFilterWidth][ACOMP] * filter[n][ACOMP];
1089 }
1090 }
1091 dest[i][RCOMP] = sumR;
1092 dest[i][GCOMP] = sumG;
1093 dest[i][BCOMP] = sumB;
1094 dest[i][ACOMP] = sumA;
1095 }
1096 }
1097
1098
1099 static void
1100 convolve_2d_reduce(GLint srcWidth, GLint srcHeight,
1101 const GLfloat src[][4],
1102 GLint filterWidth, GLint filterHeight,
1103 const GLfloat filter[][4],
1104 GLfloat dest[][4])
1105 {
1106 GLint dstWidth, dstHeight;
1107 GLint i, j, n, m;
1108
1109 if (filterWidth >= 1)
1110 dstWidth = srcWidth - (filterWidth - 1);
1111 else
1112 dstWidth = srcWidth;
1113
1114 if (filterHeight >= 1)
1115 dstHeight = srcHeight - (filterHeight - 1);
1116 else
1117 dstHeight = srcHeight;
1118
1119 if (dstWidth <= 0 || dstHeight <= 0)
1120 return;
1121
1122 for (j = 0; j < dstHeight; j++) {
1123 for (i = 0; i < dstWidth; i++) {
1124 GLfloat sumR = 0.0;
1125 GLfloat sumG = 0.0;
1126 GLfloat sumB = 0.0;
1127 GLfloat sumA = 0.0;
1128 for (m = 0; m < filterHeight; m++) {
1129 for (n = 0; n < filterWidth; n++) {
1130 const GLint k = (j + m) * srcWidth + i + n;
1131 const GLint f = m * filterWidth + n;
1132 sumR += src[k][RCOMP] * filter[f][RCOMP];
1133 sumG += src[k][GCOMP] * filter[f][GCOMP];
1134 sumB += src[k][BCOMP] * filter[f][BCOMP];
1135 sumA += src[k][ACOMP] * filter[f][ACOMP];
1136 }
1137 }
1138 dest[j * dstWidth + i][RCOMP] = sumR;
1139 dest[j * dstWidth + i][GCOMP] = sumG;
1140 dest[j * dstWidth + i][BCOMP] = sumB;
1141 dest[j * dstWidth + i][ACOMP] = sumA;
1142 }
1143 }
1144 }
1145
1146
1147 static void
1148 convolve_2d_constant(GLint srcWidth, GLint srcHeight,
1149 const GLfloat src[][4],
1150 GLint filterWidth, GLint filterHeight,
1151 const GLfloat filter[][4],
1152 GLfloat dest[][4],
1153 const GLfloat borderColor[4])
1154 {
1155 const GLint halfFilterWidth = filterWidth / 2;
1156 const GLint halfFilterHeight = filterHeight / 2;
1157 GLint i, j, n, m;
1158
1159 for (j = 0; j < srcHeight; j++) {
1160 for (i = 0; i < srcWidth; i++) {
1161 GLfloat sumR = 0.0;
1162 GLfloat sumG = 0.0;
1163 GLfloat sumB = 0.0;
1164 GLfloat sumA = 0.0;
1165 for (m = 0; m < filterHeight; m++) {
1166 for (n = 0; n < filterWidth; n++) {
1167 const GLint f = m * filterWidth + n;
1168 const GLint is = i + n - halfFilterWidth;
1169 const GLint js = j + m - halfFilterHeight;
1170 if (is < 0 || is >= srcWidth ||
1171 js < 0 || js >= srcHeight) {
1172 sumR += borderColor[RCOMP] * filter[f][RCOMP];
1173 sumG += borderColor[GCOMP] * filter[f][GCOMP];
1174 sumB += borderColor[BCOMP] * filter[f][BCOMP];
1175 sumA += borderColor[ACOMP] * filter[f][ACOMP];
1176 }
1177 else {
1178 const GLint k = js * srcWidth + is;
1179 sumR += src[k][RCOMP] * filter[f][RCOMP];
1180 sumG += src[k][GCOMP] * filter[f][GCOMP];
1181 sumB += src[k][BCOMP] * filter[f][BCOMP];
1182 sumA += src[k][ACOMP] * filter[f][ACOMP];
1183 }
1184 }
1185 }
1186 dest[j * srcWidth + i][RCOMP] = sumR;
1187 dest[j * srcWidth + i][GCOMP] = sumG;
1188 dest[j * srcWidth + i][BCOMP] = sumB;
1189 dest[j * srcWidth + i][ACOMP] = sumA;
1190 }
1191 }
1192 }
1193
1194
1195 static void
1196 convolve_2d_replicate(GLint srcWidth, GLint srcHeight,
1197 const GLfloat src[][4],
1198 GLint filterWidth, GLint filterHeight,
1199 const GLfloat filter[][4],
1200 GLfloat dest[][4])
1201 {
1202 const GLint halfFilterWidth = filterWidth / 2;
1203 const GLint halfFilterHeight = filterHeight / 2;
1204 GLint i, j, n, m;
1205
1206 for (j = 0; j < srcHeight; j++) {
1207 for (i = 0; i < srcWidth; i++) {
1208 GLfloat sumR = 0.0;
1209 GLfloat sumG = 0.0;
1210 GLfloat sumB = 0.0;
1211 GLfloat sumA = 0.0;
1212 for (m = 0; m < filterHeight; m++) {
1213 for (n = 0; n < filterWidth; n++) {
1214 const GLint f = m * filterWidth + n;
1215 GLint is = i + n - halfFilterWidth;
1216 GLint js = j + m - halfFilterHeight;
1217 GLint k;
1218 if (is < 0)
1219 is = 0;
1220 else if (is >= srcWidth)
1221 is = srcWidth - 1;
1222 if (js < 0)
1223 js = 0;
1224 else if (js >= srcHeight)
1225 js = srcHeight - 1;
1226 k = js * srcWidth + is;
1227 sumR += src[k][RCOMP] * filter[f][RCOMP];
1228 sumG += src[k][GCOMP] * filter[f][GCOMP];
1229 sumB += src[k][BCOMP] * filter[f][BCOMP];
1230 sumA += src[k][ACOMP] * filter[f][ACOMP];
1231 }
1232 }
1233 dest[j * srcWidth + i][RCOMP] = sumR;
1234 dest[j * srcWidth + i][GCOMP] = sumG;
1235 dest[j * srcWidth + i][BCOMP] = sumB;
1236 dest[j * srcWidth + i][ACOMP] = sumA;
1237 }
1238 }
1239 }
1240
1241
1242 static void
1243 convolve_sep_reduce(GLint srcWidth, GLint srcHeight,
1244 const GLfloat src[][4],
1245 GLint filterWidth, GLint filterHeight,
1246 const GLfloat rowFilt[][4],
1247 const GLfloat colFilt[][4],
1248 GLfloat dest[][4])
1249 {
1250 GLint dstWidth, dstHeight;
1251 GLint i, j, n, m;
1252
1253 if (filterWidth >= 1)
1254 dstWidth = srcWidth - (filterWidth - 1);
1255 else
1256 dstWidth = srcWidth;
1257
1258 if (filterHeight >= 1)
1259 dstHeight = srcHeight - (filterHeight - 1);
1260 else
1261 dstHeight = srcHeight;
1262
1263 if (dstWidth <= 0 || dstHeight <= 0)
1264 return;
1265
1266 for (j = 0; j < dstHeight; j++) {
1267 for (i = 0; i < dstWidth; i++) {
1268 GLfloat sumR = 0.0;
1269 GLfloat sumG = 0.0;
1270 GLfloat sumB = 0.0;
1271 GLfloat sumA = 0.0;
1272 for (m = 0; m < filterHeight; m++) {
1273 for (n = 0; n < filterWidth; n++) {
1274 GLint k = (j + m) * srcWidth + i + n;
1275 sumR += src[k][RCOMP] * rowFilt[n][RCOMP] * colFilt[m][RCOMP];
1276 sumG += src[k][GCOMP] * rowFilt[n][GCOMP] * colFilt[m][GCOMP];
1277 sumB += src[k][BCOMP] * rowFilt[n][BCOMP] * colFilt[m][BCOMP];
1278 sumA += src[k][ACOMP] * rowFilt[n][ACOMP] * colFilt[m][ACOMP];
1279 }
1280 }
1281 dest[j * dstWidth + i][RCOMP] = sumR;
1282 dest[j * dstWidth + i][GCOMP] = sumG;
1283 dest[j * dstWidth + i][BCOMP] = sumB;
1284 dest[j * dstWidth + i][ACOMP] = sumA;
1285 }
1286 }
1287 }
1288
1289
1290 static void
1291 convolve_sep_constant(GLint srcWidth, GLint srcHeight,
1292 const GLfloat src[][4],
1293 GLint filterWidth, GLint filterHeight,
1294 const GLfloat rowFilt[][4],
1295 const GLfloat colFilt[][4],
1296 GLfloat dest[][4],
1297 const GLfloat borderColor[4])
1298 {
1299 const GLint halfFilterWidth = filterWidth / 2;
1300 const GLint halfFilterHeight = filterHeight / 2;
1301 GLint i, j, n, m;
1302
1303 for (j = 0; j < srcHeight; j++) {
1304 for (i = 0; i < srcWidth; i++) {
1305 GLfloat sumR = 0.0;
1306 GLfloat sumG = 0.0;
1307 GLfloat sumB = 0.0;
1308 GLfloat sumA = 0.0;
1309 for (m = 0; m < filterHeight; m++) {
1310 for (n = 0; n < filterWidth; n++) {
1311 const GLint is = i + n - halfFilterWidth;
1312 const GLint js = j + m - halfFilterHeight;
1313 if (is < 0 || is >= srcWidth ||
1314 js < 0 || js >= srcHeight) {
1315 sumR += borderColor[RCOMP] * rowFilt[n][RCOMP] * colFilt[m][RCOMP];
1316 sumG += borderColor[GCOMP] * rowFilt[n][GCOMP] * colFilt[m][GCOMP];
1317 sumB += borderColor[BCOMP] * rowFilt[n][BCOMP] * colFilt[m][BCOMP];
1318 sumA += borderColor[ACOMP] * rowFilt[n][ACOMP] * colFilt[m][ACOMP];
1319 }
1320 else {
1321 GLint k = js * srcWidth + is;
1322 sumR += src[k][RCOMP] * rowFilt[n][RCOMP] * colFilt[m][RCOMP];
1323 sumG += src[k][GCOMP] * rowFilt[n][GCOMP] * colFilt[m][GCOMP];
1324 sumB += src[k][BCOMP] * rowFilt[n][BCOMP] * colFilt[m][BCOMP];
1325 sumA += src[k][ACOMP] * rowFilt[n][ACOMP] * colFilt[m][ACOMP];
1326 }
1327
1328 }
1329 }
1330 dest[j * srcWidth + i][RCOMP] = sumR;
1331 dest[j * srcWidth + i][GCOMP] = sumG;
1332 dest[j * srcWidth + i][BCOMP] = sumB;
1333 dest[j * srcWidth + i][ACOMP] = sumA;
1334 }
1335 }
1336 }
1337
1338
1339 static void
1340 convolve_sep_replicate(GLint srcWidth, GLint srcHeight,
1341 const GLfloat src[][4],
1342 GLint filterWidth, GLint filterHeight,
1343 const GLfloat rowFilt[][4],
1344 const GLfloat colFilt[][4],
1345 GLfloat dest[][4])
1346 {
1347 const GLint halfFilterWidth = filterWidth / 2;
1348 const GLint halfFilterHeight = filterHeight / 2;
1349 GLint i, j, n, m;
1350
1351 for (j = 0; j < srcHeight; j++) {
1352 for (i = 0; i < srcWidth; i++) {
1353 GLfloat sumR = 0.0;
1354 GLfloat sumG = 0.0;
1355 GLfloat sumB = 0.0;
1356 GLfloat sumA = 0.0;
1357 for (m = 0; m < filterHeight; m++) {
1358 for (n = 0; n < filterWidth; n++) {
1359 GLint is = i + n - halfFilterWidth;
1360 GLint js = j + m - halfFilterHeight;
1361 GLint k;
1362 if (is < 0)
1363 is = 0;
1364 else if (is >= srcWidth)
1365 is = srcWidth - 1;
1366 if (js < 0)
1367 js = 0;
1368 else if (js >= srcHeight)
1369 js = srcHeight - 1;
1370 k = js * srcWidth + is;
1371 sumR += src[k][RCOMP] * rowFilt[n][RCOMP] * colFilt[m][RCOMP];
1372 sumG += src[k][GCOMP] * rowFilt[n][GCOMP] * colFilt[m][GCOMP];
1373 sumB += src[k][BCOMP] * rowFilt[n][BCOMP] * colFilt[m][BCOMP];
1374 sumA += src[k][ACOMP] * rowFilt[n][ACOMP] * colFilt[m][ACOMP];
1375 }
1376 }
1377 dest[j * srcWidth + i][RCOMP] = sumR;
1378 dest[j * srcWidth + i][GCOMP] = sumG;
1379 dest[j * srcWidth + i][BCOMP] = sumB;
1380 dest[j * srcWidth + i][ACOMP] = sumA;
1381 }
1382 }
1383 }
1384
1385
1386
1387 void
1388 _mesa_convolve_1d_image(const GLcontext *ctx, GLsizei *width,
1389 const GLfloat *srcImage, GLfloat *dstImage)
1390 {
1391 switch (ctx->Pixel.ConvolutionBorderMode[0]) {
1392 case GL_REDUCE:
1393 convolve_1d_reduce(*width, (const GLfloat (*)[4]) srcImage,
1394 ctx->Convolution1D.Width,
1395 (const GLfloat (*)[4]) ctx->Convolution1D.Filter,
1396 (GLfloat (*)[4]) dstImage);
1397 *width = *width - (MAX2(ctx->Convolution1D.Width, 1) - 1);
1398 break;
1399 case GL_CONSTANT_BORDER:
1400 convolve_1d_constant(*width, (const GLfloat (*)[4]) srcImage,
1401 ctx->Convolution1D.Width,
1402 (const GLfloat (*)[4]) ctx->Convolution1D.Filter,
1403 (GLfloat (*)[4]) dstImage,
1404 ctx->Pixel.ConvolutionBorderColor[0]);
1405 break;
1406 case GL_REPLICATE_BORDER:
1407 convolve_1d_replicate(*width, (const GLfloat (*)[4]) srcImage,
1408 ctx->Convolution1D.Width,
1409 (const GLfloat (*)[4]) ctx->Convolution1D.Filter,
1410 (GLfloat (*)[4]) dstImage);
1411 break;
1412 default:
1413 ;
1414 }
1415 }
1416
1417
1418 void
1419 _mesa_convolve_2d_image(const GLcontext *ctx, GLsizei *width, GLsizei *height,
1420 const GLfloat *srcImage, GLfloat *dstImage)
1421 {
1422 switch (ctx->Pixel.ConvolutionBorderMode[1]) {
1423 case GL_REDUCE:
1424 convolve_2d_reduce(*width, *height,
1425 (const GLfloat (*)[4]) srcImage,
1426 ctx->Convolution2D.Width,
1427 ctx->Convolution2D.Height,
1428 (const GLfloat (*)[4]) ctx->Convolution2D.Filter,
1429 (GLfloat (*)[4]) dstImage);
1430 *width = *width - (MAX2(ctx->Convolution2D.Width, 1) - 1);
1431 *height = *height - (MAX2(ctx->Convolution2D.Height, 1) - 1);
1432 break;
1433 case GL_CONSTANT_BORDER:
1434 convolve_2d_constant(*width, *height,
1435 (const GLfloat (*)[4]) srcImage,
1436 ctx->Convolution2D.Width,
1437 ctx->Convolution2D.Height,
1438 (const GLfloat (*)[4]) ctx->Convolution2D.Filter,
1439 (GLfloat (*)[4]) dstImage,
1440 ctx->Pixel.ConvolutionBorderColor[1]);
1441 break;
1442 case GL_REPLICATE_BORDER:
1443 convolve_2d_replicate(*width, *height,
1444 (const GLfloat (*)[4]) srcImage,
1445 ctx->Convolution2D.Width,
1446 ctx->Convolution2D.Height,
1447 (const GLfloat (*)[4])ctx->Convolution2D.Filter,
1448 (GLfloat (*)[4]) dstImage);
1449 break;
1450 default:
1451 ;
1452 }
1453 }
1454
1455
1456 void
1457 _mesa_convolve_sep_image(const GLcontext *ctx,
1458 GLsizei *width, GLsizei *height,
1459 const GLfloat *srcImage, GLfloat *dstImage)
1460 {
1461 const GLfloat *rowFilter = ctx->Separable2D.Filter;
1462 const GLfloat *colFilter = rowFilter + 4 * MAX_CONVOLUTION_WIDTH;
1463
1464 switch (ctx->Pixel.ConvolutionBorderMode[2]) {
1465 case GL_REDUCE:
1466 convolve_sep_reduce(*width, *height,
1467 (const GLfloat (*)[4]) srcImage,
1468 ctx->Separable2D.Width,
1469 ctx->Separable2D.Height,
1470 (const GLfloat (*)[4]) rowFilter,
1471 (const GLfloat (*)[4]) colFilter,
1472 (GLfloat (*)[4]) dstImage);
1473 *width = *width - (MAX2(ctx->Separable2D.Width, 1) - 1);
1474 *height = *height - (MAX2(ctx->Separable2D.Height, 1) - 1);
1475 break;
1476 case GL_CONSTANT_BORDER:
1477 convolve_sep_constant(*width, *height,
1478 (const GLfloat (*)[4]) srcImage,
1479 ctx->Separable2D.Width,
1480 ctx->Separable2D.Height,
1481 (const GLfloat (*)[4]) rowFilter,
1482 (const GLfloat (*)[4]) colFilter,
1483 (GLfloat (*)[4]) dstImage,
1484 ctx->Pixel.ConvolutionBorderColor[2]);
1485 break;
1486 case GL_REPLICATE_BORDER:
1487 convolve_sep_replicate(*width, *height,
1488 (const GLfloat (*)[4]) srcImage,
1489 ctx->Separable2D.Width,
1490 ctx->Separable2D.Height,
1491 (const GLfloat (*)[4]) rowFilter,
1492 (const GLfloat (*)[4]) colFilter,
1493 (GLfloat (*)[4]) dstImage);
1494 break;
1495 default:
1496 ;
1497 }
1498 }
1499
1500
1501
1502 /*
1503 * This function computes an image's size after convolution.
1504 * If the convolution border mode is GL_REDUCE, the post-convolution
1505 * image will be smaller than the original.
1506 */
1507 void
1508 _mesa_adjust_image_for_convolution(const GLcontext *ctx, GLuint dimensions,
1509 GLsizei *width, GLsizei *height)
1510 {
1511 if (ctx->Pixel.Convolution1DEnabled
1512 && dimensions == 1
1513 && ctx->Pixel.ConvolutionBorderMode[0] == GL_REDUCE) {
1514 *width = *width - (MAX2(ctx->Convolution1D.Width, 1) - 1);
1515 }
1516 else if (ctx->Pixel.Convolution2DEnabled
1517 && dimensions > 1
1518 && ctx->Pixel.ConvolutionBorderMode[1] == GL_REDUCE) {
1519 *width = *width - (MAX2(ctx->Convolution2D.Width, 1) - 1);
1520 *height = *height - (MAX2(ctx->Convolution2D.Height, 1) - 1);
1521 }
1522 else if (ctx->Pixel.Separable2DEnabled
1523 && dimensions > 1
1524 && ctx->Pixel.ConvolutionBorderMode[2] == GL_REDUCE) {
1525 *width = *width - (MAX2(ctx->Separable2D.Width, 1) - 1);
1526 *height = *height - (MAX2(ctx->Separable2D.Height, 1) - 1);
1527 }
1528 }