convolve.c revision 176501dfff14b5bec78af2b3487207d42c26d37a
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 */ 50static GLint 51base_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 105void 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 196void 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 299void 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 342void 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 394void 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 437void 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 502void 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 530void 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 562void 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 639void 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 700void 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 770void 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 861void 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 987static void 988convolve_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 1022static void 1023convolve_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 1058static void 1059convolve_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 1099static void 1100convolve_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 1147static void 1148convolve_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 1195static void 1196convolve_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 1242static void 1243convolve_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 1290static void 1291convolve_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 1339static void 1340convolve_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 1387void 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 1418void 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 1456void 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 */ 1507void 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} 1529