Image_process.cpp (16277B)
1 /* 2 =========================================================================== 3 4 Doom 3 BFG Edition GPL Source Code 5 Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company. 6 7 This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code"). 8 9 Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify 10 it under the terms of the GNU General Public License as published by 11 the Free Software Foundation, either version 3 of the License, or 12 (at your option) any later version. 13 14 Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful, 15 but WITHOUT ANY WARRANTY; without even the implied warranty of 16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 GNU General Public License for more details. 18 19 You should have received a copy of the GNU General Public License 20 along with Doom 3 BFG Edition Source Code. If not, see <http://www.gnu.org/licenses/>. 21 22 In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below. 23 24 If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. 25 26 =========================================================================== 27 */ 28 #pragma hdrstop 29 #include "../idlib/precompiled.h" 30 31 32 #include "tr_local.h" 33 34 /* 35 ================ 36 R_ResampleTexture 37 38 Used to resample images in a more general than quartering fashion. 39 40 This will only have filter coverage if the resampled size 41 is greater than half the original size. 42 43 If a larger shrinking is needed, use the mipmap function 44 after resampling to the next lower power of two. 45 ================ 46 */ 47 #define MAX_DIMENSION 4096 48 byte *R_ResampleTexture( const byte *in, int inwidth, int inheight, 49 int outwidth, int outheight ) { 50 int i, j; 51 const byte *inrow, *inrow2; 52 unsigned int frac, fracstep; 53 unsigned int p1[MAX_DIMENSION], p2[MAX_DIMENSION]; 54 const byte *pix1, *pix2, *pix3, *pix4; 55 byte *out, *out_p; 56 57 if ( outwidth > MAX_DIMENSION ) { 58 outwidth = MAX_DIMENSION; 59 } 60 if ( outheight > MAX_DIMENSION ) { 61 outheight = MAX_DIMENSION; 62 } 63 64 out = (byte *)R_StaticAlloc( outwidth * outheight * 4, TAG_IMAGE ); 65 out_p = out; 66 67 fracstep = inwidth*0x10000/outwidth; 68 69 frac = fracstep>>2; 70 for ( i=0 ; i<outwidth ; i++ ) { 71 p1[i] = 4*(frac>>16); 72 frac += fracstep; 73 } 74 frac = 3*(fracstep>>2); 75 for ( i=0 ; i<outwidth ; i++ ) { 76 p2[i] = 4*(frac>>16); 77 frac += fracstep; 78 } 79 80 for (i=0 ; i<outheight ; i++, out_p += outwidth*4 ) { 81 inrow = in + 4 * inwidth * (int)( ( i + 0.25f ) * inheight / outheight ); 82 inrow2 = in + 4 * inwidth * (int)( ( i + 0.75f ) * inheight / outheight ); 83 frac = fracstep >> 1; 84 for (j=0 ; j<outwidth ; j++) { 85 pix1 = inrow + p1[j]; 86 pix2 = inrow + p2[j]; 87 pix3 = inrow2 + p1[j]; 88 pix4 = inrow2 + p2[j]; 89 out_p[j*4+0] = (pix1[0] + pix2[0] + pix3[0] + pix4[0])>>2; 90 out_p[j*4+1] = (pix1[1] + pix2[1] + pix3[1] + pix4[1])>>2; 91 out_p[j*4+2] = (pix1[2] + pix2[2] + pix3[2] + pix4[2])>>2; 92 out_p[j*4+3] = (pix1[3] + pix2[3] + pix3[3] + pix4[3])>>2; 93 } 94 } 95 96 return out; 97 } 98 99 /* 100 ================ 101 R_Dropsample 102 103 Used to resample images in a more general than quartering fashion. 104 Normal maps and such should not be bilerped. 105 ================ 106 */ 107 byte *R_Dropsample( const byte *in, int inwidth, int inheight, 108 int outwidth, int outheight ) { 109 int i, j, k; 110 const byte *inrow; 111 const byte *pix1; 112 byte *out, *out_p; 113 114 out = (byte *)R_StaticAlloc( outwidth * outheight * 4, TAG_IMAGE ); 115 out_p = out; 116 117 for (i=0 ; i<outheight ; i++, out_p += outwidth*4 ) { 118 inrow = in + 4*inwidth*(int)((i+0.25)*inheight/outheight); 119 for (j=0 ; j<outwidth ; j++) { 120 k = j * inwidth / outwidth; 121 pix1 = inrow + k * 4; 122 out_p[j*4+0] = pix1[0]; 123 out_p[j*4+1] = pix1[1]; 124 out_p[j*4+2] = pix1[2]; 125 out_p[j*4+3] = pix1[3]; 126 } 127 } 128 129 return out; 130 } 131 132 /* 133 ================ 134 R_SetAlphaNormalDivergence 135 136 If any of the angles inside the cone would directly reflect to the light, there will be 137 a specular highlight. The intensity of the highlight is inversely proportional to the 138 area of the spread. 139 140 Light source area is important for the base size. 141 142 area subtended in light is the divergence times the distance 143 144 Shininess value is subtracted from the divergence 145 146 Sets the alpha channel to the greatest divergence dot product of the surrounding texels. 147 1.0 = flat, 0.0 = turns a 90 degree angle 148 Lower values give less shiny specular 149 With mip maps, the lowest samnpled value will be retained 150 151 Should we rewrite the normal as the centered average? 152 ================ 153 */ 154 void R_SetAlphaNormalDivergence( byte *in, int width, int height ) { 155 for ( int y = 0 ; y < height ; y++ ) { 156 for ( int x = 0 ; x < width ; x++ ) { 157 // the divergence is the smallest dot product of any of the eight surrounding texels 158 byte *pic_p = in + ( y * width + x ) * 4; 159 idVec3 center; 160 center[0] = ( pic_p[0] - 128 ) / 127; 161 center[1] = ( pic_p[1] - 128 ) / 127; 162 center[2] = ( pic_p[2] - 128 ) / 127; 163 center.Normalize(); 164 165 float maxDiverge = 1.0; 166 167 // FIXME: this assumes wrap mode, but should handle clamp modes and border colors 168 for ( int yy = -1 ; yy <= 1 ; yy++ ) { 169 for ( int xx = -1 ; xx <= 1 ; xx++ ) { 170 if ( yy == 0 && xx == 0 ) { 171 continue; 172 } 173 byte *corner_p = in + ( ((y+yy)&(height-1)) * width + ((x+xx)&width-1) ) * 4; 174 idVec3 corner; 175 corner[0] = ( corner_p[0] - 128 ) / 127; 176 corner[1] = ( corner_p[1] - 128 ) / 127; 177 corner[2] = ( corner_p[2] - 128 ) / 127; 178 corner.Normalize(); 179 180 float diverge = corner * center; 181 if ( diverge < maxDiverge ) { 182 maxDiverge = diverge; 183 } 184 } 185 } 186 187 // we can get a diverge < 0 in some extreme cases 188 if ( maxDiverge < 0 ) { 189 maxDiverge = 0; 190 } 191 pic_p[3] = maxDiverge * 255; 192 } 193 } 194 } 195 196 /* 197 ================ 198 R_MipMapWithAlphaSpecularity 199 200 Returns a new copy of the texture, quartered in size and filtered. 201 The alpha channel is taken to be the minimum of the dots of all surrounding normals. 202 ================ 203 */ 204 #define MIP_MIN(a,b) (a<b?a:b) 205 206 byte *R_MipMapWithAlphaSpecularity( const byte *in, int width, int height ) { 207 int i, j, c, x, y, sx, sy; 208 const byte *in_p; 209 byte *out, *out_p; 210 int row; 211 int newWidth, newHeight; 212 float *fbuf, *fbuf_p; 213 214 if ( width < 1 || height < 1 || ( width + height == 2 ) ) { 215 common->FatalError( "R_MipMapWithAlphaMin called with size %i,%i", width, height ); 216 } 217 218 // convert the incoming texture to centered floating point 219 c = width * height; 220 fbuf = (float *)_alloca( c * 4 * sizeof( *fbuf ) ); 221 in_p = in; 222 fbuf_p = fbuf; 223 for ( i = 0 ; i < c ; i++, in_p+=4, fbuf_p += 4 ) { 224 fbuf_p[0] = ( in_p[0] / 255.0 ) * 2.0 - 1.0; // convert to a normal 225 fbuf_p[1] = ( in_p[1] / 255.0 ) * 2.0 - 1.0; 226 fbuf_p[2] = ( in_p[2] / 255.0 ) * 2.0 - 1.0; 227 fbuf_p[3] = ( in_p[3] / 255.0 ); // filtered divegence / specularity 228 } 229 230 row = width * 4; 231 232 newWidth = width >> 1; 233 newHeight = height >> 1; 234 if ( !newWidth ) { 235 newWidth = 1; 236 } 237 if ( !newHeight ) { 238 newHeight = 1; 239 } 240 out = (byte *)R_StaticAlloc( newWidth * newHeight * 4, TAG_IMAGE ); 241 out_p = out; 242 243 in_p = in; 244 245 for ( i=0 ; i<newHeight ; i++ ) { 246 for ( j=0 ; j<newWidth ; j++, out_p+=4 ) { 247 idVec3 total; 248 float totalSpec; 249 250 total.Zero(); 251 totalSpec = 0; 252 // find the average normal 253 for ( x = -1 ; x <= 1 ; x++ ) { 254 sx = ( j * 2 + x ) & (width-1); 255 for ( y = -1 ; y <= 1 ; y++ ) { 256 sy = ( i * 2 + y ) & (height-1); 257 fbuf_p = fbuf + ( sy * width + sx ) * 4; 258 259 total[0] += fbuf_p[0]; 260 total[1] += fbuf_p[1]; 261 total[2] += fbuf_p[2]; 262 263 totalSpec += fbuf_p[3]; 264 } 265 } 266 total.Normalize(); 267 totalSpec /= 9.0; 268 269 // find the maximum divergence 270 for ( x = -1 ; x <= 1 ; x++ ) { 271 for ( y = -1 ; y <= 1 ; y++ ) { 272 } 273 } 274 275 // store the average normal and divergence 276 } 277 } 278 279 return out; 280 } 281 282 float mip_gammaTable[256] = { 283 0.000000f, 0.000005f, 0.000023f, 0.000057f, 0.000107f, 0.000175f, 0.000262f, 0.000367f, 0.000493f, 0.000638f, 0.000805f, 0.000992f, 0.001202f, 0.001433f, 0.001687f, 0.001963f, 284 0.002263f, 0.002586f, 0.002932f, 0.003303f, 0.003697f, 0.004116f, 0.004560f, 0.005028f, 0.005522f, 0.006041f, 0.006585f, 0.007155f, 0.007751f, 0.008373f, 0.009021f, 0.009696f, 285 0.010398f, 0.011126f, 0.011881f, 0.012664f, 0.013473f, 0.014311f, 0.015175f, 0.016068f, 0.016988f, 0.017936f, 0.018913f, 0.019918f, 0.020951f, 0.022013f, 0.023104f, 0.024223f, 286 0.025371f, 0.026549f, 0.027755f, 0.028991f, 0.030257f, 0.031551f, 0.032876f, 0.034230f, 0.035614f, 0.037029f, 0.038473f, 0.039947f, 0.041452f, 0.042987f, 0.044553f, 0.046149f, 287 0.047776f, 0.049433f, 0.051122f, 0.052842f, 0.054592f, 0.056374f, 0.058187f, 0.060032f, 0.061907f, 0.063815f, 0.065754f, 0.067725f, 0.069727f, 0.071761f, 0.073828f, 0.075926f, 288 0.078057f, 0.080219f, 0.082414f, 0.084642f, 0.086901f, 0.089194f, 0.091518f, 0.093876f, 0.096266f, 0.098689f, 0.101145f, 0.103634f, 0.106156f, 0.108711f, 0.111299f, 0.113921f, 289 0.116576f, 0.119264f, 0.121986f, 0.124741f, 0.127530f, 0.130352f, 0.133209f, 0.136099f, 0.139022f, 0.141980f, 0.144972f, 0.147998f, 0.151058f, 0.154152f, 0.157281f, 0.160444f, 290 0.163641f, 0.166872f, 0.170138f, 0.173439f, 0.176774f, 0.180144f, 0.183549f, 0.186989f, 0.190463f, 0.193972f, 0.197516f, 0.201096f, 0.204710f, 0.208360f, 0.212044f, 0.215764f, 291 0.219520f, 0.223310f, 0.227137f, 0.230998f, 0.234895f, 0.238828f, 0.242796f, 0.246800f, 0.250840f, 0.254916f, 0.259027f, 0.263175f, 0.267358f, 0.271577f, 0.275833f, 0.280124f, 292 0.284452f, 0.288816f, 0.293216f, 0.297653f, 0.302126f, 0.306635f, 0.311181f, 0.315763f, 0.320382f, 0.325037f, 0.329729f, 0.334458f, 0.339223f, 0.344026f, 0.348865f, 0.353741f, 293 0.358654f, 0.363604f, 0.368591f, 0.373615f, 0.378676f, 0.383775f, 0.388910f, 0.394083f, 0.399293f, 0.404541f, 0.409826f, 0.415148f, 0.420508f, 0.425905f, 0.431340f, 0.436813f, 294 0.442323f, 0.447871f, 0.453456f, 0.459080f, 0.464741f, 0.470440f, 0.476177f, 0.481952f, 0.487765f, 0.493616f, 0.499505f, 0.505432f, 0.511398f, 0.517401f, 0.523443f, 0.529523f, 295 0.535642f, 0.541798f, 0.547994f, 0.554227f, 0.560499f, 0.566810f, 0.573159f, 0.579547f, 0.585973f, 0.592438f, 0.598942f, 0.605484f, 0.612066f, 0.618686f, 0.625345f, 0.632043f, 296 0.638779f, 0.645555f, 0.652370f, 0.659224f, 0.666117f, 0.673049f, 0.680020f, 0.687031f, 0.694081f, 0.701169f, 0.708298f, 0.715465f, 0.722672f, 0.729919f, 0.737205f, 0.744530f, 297 0.751895f, 0.759300f, 0.766744f, 0.774227f, 0.781751f, 0.789314f, 0.796917f, 0.804559f, 0.812241f, 0.819964f, 0.827726f, 0.835528f, 0.843370f, 0.851252f, 0.859174f, 0.867136f, 298 0.875138f, 0.883180f, 0.891262f, 0.899384f, 0.907547f, 0.915750f, 0.923993f, 0.932277f, 0.940601f, 0.948965f, 0.957370f, 0.965815f, 0.974300f, 0.982826f, 0.991393f, 1.000000f 299 }; 300 301 /* 302 ================ 303 R_MipMapGamma 304 305 Returns a new copy of the texture, quartered in size with gamma correction. 306 ================ 307 */ 308 byte * R_MipMapWithGamma( const byte *in, int width, int height ) { 309 int i, j; 310 const byte *in_p; 311 byte *out, *out_p; 312 int row; 313 int newWidth, newHeight; 314 315 if ( width < 1 || height < 1 || ( width + height == 2 ) ) { 316 return NULL; 317 } 318 319 row = width * 4; 320 321 newWidth = width >> 1; 322 newHeight = height >> 1; 323 if ( !newWidth ) { 324 newWidth = 1; 325 } 326 if ( !newHeight ) { 327 newHeight = 1; 328 } 329 out = (byte *)R_StaticAlloc( newWidth * newHeight * 4, TAG_IMAGE ); 330 out_p = out; 331 332 in_p = in; 333 334 width >>= 1; 335 height >>= 1; 336 337 if ( width == 0 || height == 0 ) { 338 width += height; // get largest 339 for (i=0 ; i<width ; i++, out_p+=4, in_p+=8 ) { 340 out_p[0] = idMath::Ftob( 255.0f * idMath::Pow( 0.5f * ( mip_gammaTable[in_p[0]] + mip_gammaTable[in_p[4]] ), 1.0f / 2.2f ) ); 341 out_p[1] = idMath::Ftob( 255.0f * idMath::Pow( 0.5f * ( mip_gammaTable[in_p[1]] + mip_gammaTable[in_p[5]] ), 1.0f / 2.2f ) ); 342 out_p[2] = idMath::Ftob( 255.0f * idMath::Pow( 0.5f * ( mip_gammaTable[in_p[2]] + mip_gammaTable[in_p[6]] ), 1.0f / 2.2f ) ); 343 out_p[3] = idMath::Ftob( 255.0f * idMath::Pow( 0.5f * ( mip_gammaTable[in_p[3]] + mip_gammaTable[in_p[7]] ), 1.0f / 2.2f ) ); 344 } 345 return out; 346 } 347 for (i=0 ; i<height ; i++, in_p+=row) { 348 for (j=0 ; j<width ; j++, out_p+=4, in_p+=8) { 349 out_p[0] = idMath::Ftob( 255.0f * idMath::Pow( 0.25f * ( mip_gammaTable[in_p[0]] + mip_gammaTable[in_p[4]] + mip_gammaTable[in_p[row+0]] + mip_gammaTable[in_p[row+4]] ), 1.0f / 2.2f ) ); 350 out_p[1] = idMath::Ftob( 255.0f * idMath::Pow( 0.25f * ( mip_gammaTable[in_p[1]] + mip_gammaTable[in_p[5]] + mip_gammaTable[in_p[row+1]] + mip_gammaTable[in_p[row+5]] ), 1.0f / 2.2f ) ); 351 out_p[2] = idMath::Ftob( 255.0f * idMath::Pow( 0.25f * ( mip_gammaTable[in_p[2]] + mip_gammaTable[in_p[6]] + mip_gammaTable[in_p[row+2]] + mip_gammaTable[in_p[row+6]] ), 1.0f / 2.2f ) ); 352 out_p[3] = idMath::Ftob( 255.0f * idMath::Pow( 0.25f * ( mip_gammaTable[in_p[3]] + mip_gammaTable[in_p[7]] + mip_gammaTable[in_p[row+3]] + mip_gammaTable[in_p[row+7]] ), 1.0f / 2.2f ) ); 353 } 354 } 355 356 return out; 357 } 358 359 /* 360 ================ 361 R_MipMap 362 363 Returns a new copy of the texture, quartered in size and filtered. 364 ================ 365 */ 366 byte * R_MipMap( const byte *in, int width, int height ) { 367 int i, j; 368 const byte *in_p; 369 byte *out, *out_p; 370 int row; 371 int newWidth, newHeight; 372 373 if ( width < 1 || height < 1 || ( width + height == 2 ) ) { 374 return NULL; 375 } 376 377 row = width * 4; 378 379 newWidth = width >> 1; 380 newHeight = height >> 1; 381 if ( !newWidth ) { 382 newWidth = 1; 383 } 384 if ( !newHeight ) { 385 newHeight = 1; 386 } 387 out = (byte *)R_StaticAlloc( newWidth * newHeight * 4, TAG_IMAGE ); 388 out_p = out; 389 390 in_p = in; 391 392 width >>= 1; 393 height >>= 1; 394 395 if ( width == 0 || height == 0 ) { 396 width += height; // get largest 397 for (i=0 ; i<width ; i++, out_p+=4, in_p+=8 ) { 398 out_p[0] = ( in_p[0] + in_p[4] )>>1; 399 out_p[1] = ( in_p[1] + in_p[5] )>>1; 400 out_p[2] = ( in_p[2] + in_p[6] )>>1; 401 out_p[3] = ( in_p[3] + in_p[7] )>>1; 402 } 403 return out; 404 } 405 406 for (i=0 ; i<height ; i++, in_p+=row) { 407 for (j=0 ; j<width ; j++, out_p+=4, in_p+=8) { 408 out_p[0] = (in_p[0] + in_p[4] + in_p[row+0] + in_p[row+4])>>2; 409 out_p[1] = (in_p[1] + in_p[5] + in_p[row+1] + in_p[row+5])>>2; 410 out_p[2] = (in_p[2] + in_p[6] + in_p[row+2] + in_p[row+6])>>2; 411 out_p[3] = (in_p[3] + in_p[7] + in_p[row+3] + in_p[row+7])>>2; 412 } 413 } 414 415 return out; 416 } 417 418 /* 419 ================== 420 R_BlendOverTexture 421 422 Apply a color blend over a set of pixels 423 ================== 424 */ 425 void R_BlendOverTexture( byte *data, int pixelCount, const byte blend[4] ) { 426 int i; 427 int inverseAlpha; 428 int premult[3]; 429 430 inverseAlpha = 255 - blend[3]; 431 premult[0] = blend[0] * blend[3]; 432 premult[1] = blend[1] * blend[3]; 433 premult[2] = blend[2] * blend[3]; 434 435 for ( i = 0 ; i < pixelCount ; i++, data+=4 ) { 436 data[0] = ( data[0] * inverseAlpha + premult[0] ) >> 9; 437 data[1] = ( data[1] * inverseAlpha + premult[1] ) >> 9; 438 data[2] = ( data[2] * inverseAlpha + premult[2] ) >> 9; 439 } 440 } 441 442 443 /* 444 ================== 445 R_HorizontalFlip 446 447 Flip the image in place 448 ================== 449 */ 450 void R_HorizontalFlip( byte *data, int width, int height ) { 451 int i, j; 452 int temp; 453 454 for ( i = 0 ; i < height ; i++ ) { 455 for ( j = 0 ; j < width / 2 ; j++ ) { 456 temp = *( (int *)data + i * width + j ); 457 *( (int *)data + i * width + j ) = *( (int *)data + i * width + width - 1 - j ); 458 *( (int *)data + i * width + width - 1 - j ) = temp; 459 } 460 } 461 } 462 463 void R_VerticalFlip( byte *data, int width, int height ) { 464 int i, j; 465 int temp; 466 467 for ( i = 0 ; i < width ; i++ ) { 468 for ( j = 0 ; j < height / 2 ; j++ ) { 469 temp = *( (int *)data + j * width + i ); 470 *( (int *)data + j * width + i ) = *( (int *)data + ( height - 1 - j ) * width + i ); 471 *( (int *)data + ( height - 1 - j ) * width + i ) = temp; 472 } 473 } 474 } 475 476 void R_RotatePic( byte *data, int width ) { 477 int i, j; 478 int *temp; 479 480 temp = (int *)R_StaticAlloc( width * width * 4, TAG_IMAGE ); 481 482 for ( i = 0 ; i < width ; i++ ) { 483 for ( j = 0 ; j < width ; j++ ) { 484 *( temp + i * width + j ) = *( (int *)data + j * width + i ); 485 } 486 } 487 488 memcpy( data, temp, width * width * 4 ); 489 490 R_StaticFree( temp ); 491 } 492