ColorSpace.cpp (22034B)
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 ================================================================================================ 33 Contains the ColorSpace conversion implementation. 34 ================================================================================================ 35 */ 36 37 #include "ColorSpace.h" 38 39 /* 40 ================================================================================================ 41 To *Color-Convert RGB and YCoCg* ColorSpaces, use the following conversions: 42 43 44 Y = [ 1/4 1/2 1/4] [R] 45 Co = [ 1/2 0 -1/2] [G] + 128 46 CG = [-1/4 1/2 -1/4] [B] + 128 47 48 R = [ 1 1 -1] [Y] 49 G = [ 1 0 1] [Co - 128] 50 B = [ 1 -1 -1] [Cg - 128] 51 52 ================================================================================================ 53 */ 54 55 #define RGB_TO_YCOCG_Y( r, g, b ) ( ( ( r + (g<<1) + b ) + 2 ) >> 2 ) 56 #define RGB_TO_YCOCG_CO( r, g, b ) ( ( ( (r<<1) - (b<<1) ) + 2 ) >> 2 ) 57 #define RGB_TO_YCOCG_CG( r, g, b ) ( ( ( - r + (g<<1) - b ) + 2 ) >> 2 ) 58 59 #define COCG_TO_R( co, cg ) ( co - cg ) 60 #define COCG_TO_G( co, cg ) ( cg ) 61 #define COCG_TO_B( co, cg ) ( - co - cg ) 62 63 /* 64 ======================== 65 idColorSpace::ConvertRGBToYCoCg 66 ======================== 67 */ 68 void idColorSpace::ConvertRGBToYCoCg( byte *dst, const byte *src, int width, int height ) { 69 for ( int i = 0; i < width * height; i++ ) { 70 int r = src[i*4+0]; 71 int g = src[i*4+1]; 72 int b = src[i*4+2]; 73 int a = src[i*4+3]; 74 dst[i*4+0] = CLAMP_BYTE( RGB_TO_YCOCG_Y( r, g, b ) ); 75 dst[i*4+1] = CLAMP_BYTE( RGB_TO_YCOCG_CO( r, g, b ) + 128 ); 76 dst[i*4+2] = CLAMP_BYTE( RGB_TO_YCOCG_CG( r, g, b ) + 128 ); 77 dst[i*4+3] = a; 78 } 79 } 80 81 /* 82 ======================== 83 idColorSpace::ConvertYCoCgToRGB 84 ======================== 85 */ 86 void idColorSpace::ConvertYCoCgToRGB( byte *dst, const byte *src, int width, int height ) { 87 for ( int i = 0; i < width * height; i++ ) { 88 int y = src[i*4+0]; 89 int co = src[i*4+1] - 128; 90 int cg = src[i*4+2] - 128; 91 int a = src[i*4+3]; 92 dst[i*4+0] = CLAMP_BYTE( y + COCG_TO_R( co, cg ) ); 93 dst[i*4+1] = CLAMP_BYTE( y + COCG_TO_G( co, cg ) ); 94 dst[i*4+2] = CLAMP_BYTE( y + COCG_TO_B( co, cg ) ); 95 dst[i*4+3] = a; 96 } 97 } 98 99 /* 100 ======================== 101 idColorSpace::ConvertRGBToCoCg_Y 102 ======================== 103 */ 104 void idColorSpace::ConvertRGBToCoCg_Y( byte *dst, const byte *src, int width, int height ) { 105 for ( int i = 0; i < width * height; i++ ) { 106 int r = src[i*4+0]; 107 int g = src[i*4+1]; 108 int b = src[i*4+2]; 109 //int a = src[i*4+3]; 110 dst[i*4+0] = CLAMP_BYTE( RGB_TO_YCOCG_CO( r, g, b ) + 128 ); 111 dst[i*4+1] = CLAMP_BYTE( RGB_TO_YCOCG_CG( r, g, b ) + 128 ); 112 dst[i*4+2] = 0; 113 dst[i*4+3] = CLAMP_BYTE( RGB_TO_YCOCG_Y( r, g, b ) ); 114 } 115 } 116 117 /* 118 ======================== 119 idColorSpace::ConvertCoCg_YToRGB 120 ======================== 121 */ 122 void idColorSpace::ConvertCoCg_YToRGB( byte *dst, const byte *src, int width, int height ) { 123 for ( int i = 0; i < width * height; i++ ) { 124 int co = src[i*4+0] - 128; 125 int cg = src[i*4+1] - 128; 126 int a = src[i*4+2]; 127 int y = src[i*4+3]; 128 dst[i*4+0] = CLAMP_BYTE( y + COCG_TO_R( co, cg ) ); 129 dst[i*4+1] = CLAMP_BYTE( y + COCG_TO_G( co, cg ) ); 130 dst[i*4+2] = CLAMP_BYTE( y + COCG_TO_B( co, cg ) ); 131 dst[i*4+3] = a; 132 } 133 } 134 135 /* 136 ======================== 137 idColorSpace::ConvertCoCgSYToRGB 138 139 A scale factor is encoded in the Z value to give better compression of 140 the color channels. 141 ======================== 142 */ 143 void idColorSpace::ConvertCoCgSYToRGB( byte * dst, const byte * src, int width, int height ) { 144 for ( int i = 0; i < width * height; i++ ) { 145 int co = src[i*4+0] - 128; 146 int cg = src[i*4+1] - 128; 147 int a = src[i*4+2]; 148 int y = src[i*4+3]; 149 float scale = 1.0f / ( 1.0f + a * ( 31.875f / 255.0f ) ) ; 150 co = idMath::Ftoi( co * scale ); 151 cg = idMath::Ftoi( cg * scale ); 152 dst[i*4+0] = CLAMP_BYTE( y + COCG_TO_R( co, cg ) ); 153 dst[i*4+1] = CLAMP_BYTE( y + COCG_TO_G( co, cg ) ); 154 dst[i*4+2] = CLAMP_BYTE( y + COCG_TO_B( co, cg ) ); 155 dst[i*4+3] = 255; 156 } 157 } 158 159 /* 160 ======================== 161 idColorSpace::ConvertRGBToYCoCg420 162 ======================== 163 */ 164 void idColorSpace::ConvertRGBToYCoCg420( byte *dst, const byte *src, int width, int height ) { 165 int numSamples = 0; 166 for ( int j = 0; j < height; j += 2 ) { 167 for ( int i = 0; i < width; i += 2 ) { 168 int r0 = src[((j+0)*width+i+0)*4+0]; 169 int g0 = src[((j+0)*width+i+0)*4+1]; 170 int b0 = src[((j+0)*width+i+0)*4+2]; 171 int r1 = src[((j+0)*width+i+1)*4+0]; 172 int g1 = src[((j+0)*width+i+1)*4+1]; 173 int b1 = src[((j+0)*width+i+1)*4+2]; 174 int r2 = src[((j+1)*width+i+0)*4+0]; 175 int g2 = src[((j+1)*width+i+0)*4+1]; 176 int b2 = src[((j+1)*width+i+0)*4+2]; 177 int r3 = src[((j+1)*width+i+1)*4+0]; 178 int g3 = src[((j+1)*width+i+1)*4+1]; 179 int b3 = src[((j+1)*width+i+1)*4+2]; 180 int y0 = CLAMP_BYTE( RGB_TO_YCOCG_Y( r0, g0, b0 ) ); 181 int co0 = CLAMP_BYTE( RGB_TO_YCOCG_CO( r0, g0, b0 ) + 128 ); 182 int cg0 = CLAMP_BYTE( RGB_TO_YCOCG_CG( r0, g0, b0 ) + 128 ); 183 int y1 = CLAMP_BYTE( RGB_TO_YCOCG_Y( r1, g1, b1 ) ); 184 int co1 = CLAMP_BYTE( RGB_TO_YCOCG_CO( r1, g1, b1 ) + 128 ); 185 int cg1 = CLAMP_BYTE( RGB_TO_YCOCG_CG( r1, g1, b1 ) + 128 ); 186 int y2 = CLAMP_BYTE( RGB_TO_YCOCG_Y( r2, g2, b2 ) ); 187 int co2 = CLAMP_BYTE( RGB_TO_YCOCG_CO( r2, g2, b2 ) + 128 ); 188 int cg2 = CLAMP_BYTE( RGB_TO_YCOCG_CG( r2, g2, b2 ) + 128 ); 189 int y3 = CLAMP_BYTE( RGB_TO_YCOCG_Y( r3, g3, b3 ) ); 190 int co3 = CLAMP_BYTE( RGB_TO_YCOCG_CO( r3, g3, b3 ) + 128 ); 191 int cg3 = CLAMP_BYTE( RGB_TO_YCOCG_CG( r3, g3, b3 ) + 128 ); 192 dst[numSamples+0] = y0; 193 dst[numSamples+1] = y1; 194 dst[numSamples+2] = y2; 195 dst[numSamples+3] = y3; 196 dst[numSamples+4] = ( co0 + co1 + co2 + co3 ) >> 2; 197 dst[numSamples+5] = ( cg0 + cg1 + cg2 + cg3 ) >> 2; 198 numSamples += 6; 199 } 200 numSamples += width; 201 } 202 } 203 204 /* 205 ======================== 206 idColorSpace::ConvertYCoCg420ToRGB 207 ======================== 208 */ 209 void idColorSpace::ConvertYCoCg420ToRGB( byte *dst, const byte *src, int width, int height ) { 210 int numSamples = width * height * 2 - width; 211 for ( int j = height - 2; j >= 0; j -= 2 ) { 212 for ( int i = width - 2; i >= 0; i -= 2 ) { 213 int y0 = src[numSamples-6]; 214 int y1 = src[numSamples-5]; 215 int y2 = src[numSamples-4]; 216 int y3 = src[numSamples-3]; 217 int co = src[numSamples-2] - 128; 218 int cg = src[numSamples-1] - 128; 219 numSamples -= 6; 220 int r = COCG_TO_R( co, cg ); 221 int g = COCG_TO_G( co, cg ); 222 int b = COCG_TO_B( co, cg ); 223 dst[((j+0)*width+i+0)*4+0] = CLAMP_BYTE( y0 + r ); 224 dst[((j+0)*width+i+0)*4+1] = CLAMP_BYTE( y0 + g ); 225 dst[((j+0)*width+i+0)*4+2] = CLAMP_BYTE( y0 + b ); 226 dst[((j+0)*width+i+1)*4+0] = CLAMP_BYTE( y1 + r ); 227 dst[((j+0)*width+i+1)*4+1] = CLAMP_BYTE( y1 + g ); 228 dst[((j+0)*width+i+1)*4+2] = CLAMP_BYTE( y1 + b ); 229 dst[((j+1)*width+i+0)*4+0] = CLAMP_BYTE( y2 + r ); 230 dst[((j+1)*width+i+0)*4+1] = CLAMP_BYTE( y2 + g ); 231 dst[((j+1)*width+i+0)*4+2] = CLAMP_BYTE( y2 + b ); 232 dst[((j+1)*width+i+1)*4+0] = CLAMP_BYTE( y3 + r ); 233 dst[((j+1)*width+i+1)*4+1] = CLAMP_BYTE( y3 + g ); 234 dst[((j+1)*width+i+1)*4+2] = CLAMP_BYTE( y3 + b ); 235 } 236 numSamples -= width; 237 } 238 } 239 240 /* 241 ================================================================================================ 242 To *Color-Convert RGB and YCbCr* ColorSpaces, note that YCbCr is defined per 243 CCIR 601-1, except that Cb and Cr are normalized to the range 0 -> 255 rather than -0.5 -> 0.5. 244 The conversion equations to be implemented are therefore: 245 246 247 Y = [ 0.29900 0.58700 0.11400] [R] 248 Cb = [-0.16874 -0.33126 0.50000] [G] + 128 249 Cr = [ 0.50000 -0.41869 -0.08131] [B] + 128 250 251 R = [ 1.00000 0.00000 1.40200] [Y] 252 G = [ 1.00000 -0.34414 -0.71414] [Cb - 128] 253 B = [ 1.00000 1.77200 0.00000] [Cr - 128] 254 255 256 These numbers are derived from TIFF 6.0 section 21, dated 3-June-92. To avoid floating-point 257 arithmetic, we represent the fractional constants as integers scaled up by 2^16 (about 4 digits 258 precision); we have to divide the products by 2^16, with appropriate rounding, to get the 259 correct answer. 260 ================================================================================================ 261 */ 262 263 const int ycbcr_shift = 16; 264 const int ycbcr_round = 1 << ( ycbcr_shift - 1 ); 265 266 const int r029900 = 19595; // int( 0.29900 * (1<<16) + 0.5 ) 267 const int g058700 = 38470; // int( 0.58700 * (1<<16) + 0.5 ) 268 const int b011400 = 7471; // int( 0.11400 * (1<<16) + 0.5 ) 269 270 const int r016874 = 11059; // int( 0.16874 * (1<<16) + 0.5 ) 271 const int g033126 = 21709; // int( 0.33126 * (1<<16) + 0.5 ) 272 const int b050000 = 32768; // int( 0.50000 * (1<<16) + 0.5 ) 273 274 const int r050000 = 32768; // int( 0.50000 * (1<<16) + 0.5 ) 275 const int g041869 = 27439; // int( 0.41869 * (1<<16) + 0.5 ) 276 const int b008131 = 5329; // int( 0.08131 * (1<<16) + 0.5 ) 277 278 const int r140200 = 91881; // int( 1.40200 * (1<<16) + 0.5 ) 279 const int b177200 = 116130; // int( 1.77200 * (1<<16) + 0.5 ) 280 const int g071414 = 46802; // int( 0.71414 * (1<<16) + 0.5 ) 281 const int g034414 = 22554; // int( 0.34414 * (1<<16) + 0.5 ) 282 283 #define RGB_TO_YCBCR_Y( r, g, b ) ( ( ( r * r029900 + g * g058700 + b * b011400 ) + ycbcr_round ) >> ycbcr_shift ) 284 #define RGB_TO_YCBCR_CB( r, g, b ) ( ( ( - r * r016874 - g * g033126 + b * b050000 ) + ycbcr_round ) >> ycbcr_shift ) 285 #define RGB_TO_YCBCR_CR( r, g, b ) ( ( ( r * r050000 - g * g041869 - b * b008131 ) + ycbcr_round ) >> ycbcr_shift ) 286 287 #define CBCR_TO_R( cb, cr ) ( ( ycbcr_round + cr * r140200 ) >> ycbcr_shift ) 288 #define CBCR_TO_G( cb, cr ) ( ( ycbcr_round - cb * g034414 - cr * g071414 ) >> ycbcr_shift ) 289 #define CBCR_TO_B( cb, cr ) ( ( ycbcr_round + cb * b177200 ) >> ycbcr_shift ) 290 291 /* 292 ======================== 293 idColorSpace::ConvertRGBToYCbCr 294 ======================== 295 */ 296 void idColorSpace::ConvertRGBToYCbCr( byte *dst, const byte *src, int width, int height ) { 297 for ( int i = 0; i < width * height; i++ ) { 298 int r = src[i*4+0]; 299 int g = src[i*4+1]; 300 int b = src[i*4+2]; 301 int a = src[i*4+3]; 302 dst[i*4+0] = CLAMP_BYTE( RGB_TO_YCBCR_Y( r, g, b ) ); 303 dst[i*4+1] = CLAMP_BYTE( RGB_TO_YCBCR_CB( r, g, b ) + 128 ); 304 dst[i*4+2] = CLAMP_BYTE( RGB_TO_YCBCR_CR( r, g, b ) + 128 ); 305 dst[i*4+3] = a; 306 } 307 } 308 309 /* 310 ======================== 311 idColorSpace::ConvertYCbCrToRGB 312 ======================== 313 */ 314 void idColorSpace::ConvertYCbCrToRGB( byte *dst, const byte *src, int width, int height ) { 315 for ( int i = 0; i < width * height; i++ ) { 316 int y = src[i*4+0]; 317 int cb = src[i*4+1] - 128; 318 int cr = src[i*4+2] - 128; 319 dst[i*4+0] = CLAMP_BYTE( y + CBCR_TO_R( cb, cr ) ); 320 dst[i*4+1] = CLAMP_BYTE( y + CBCR_TO_G( cb, cr ) ); 321 dst[i*4+2] = CLAMP_BYTE( y + CBCR_TO_B( cb, cr ) ); 322 } 323 } 324 325 /* 326 ======================== 327 idColorSpace::ConvertRGBToCbCr_Y 328 ======================== 329 */ 330 void idColorSpace::ConvertRGBToCbCr_Y( byte *dst, const byte *src, int width, int height ) { 331 for ( int i = 0; i < width * height; i++ ) { 332 int r = src[i*4+0]; 333 int g = src[i*4+1]; 334 int b = src[i*4+2]; 335 int a = src[i*4+3]; 336 dst[i*4+0] = CLAMP_BYTE( RGB_TO_YCBCR_CB( r, g, b ) + 128 ); 337 dst[i*4+1] = CLAMP_BYTE( RGB_TO_YCBCR_CR( r, g, b ) + 128 ); 338 dst[i*4+2] = a; 339 dst[i*4+3] = CLAMP_BYTE( RGB_TO_YCBCR_Y( r, g, b ) ); 340 } 341 } 342 343 /* 344 ======================== 345 idColorSpace::ConvertCbCr_YToRGB 346 ======================== 347 */ 348 void idColorSpace::ConvertCbCr_YToRGB( byte *dst, const byte *src, int width, int height ) { 349 for ( int i = 0; i < width * height; i++ ) { 350 int cb = src[i*4+0] - 128; 351 int cr = src[i*4+1] - 128; 352 int a = src[i*4+2]; 353 int y = src[i*4+3]; 354 dst[i*4+0] = CLAMP_BYTE( y + CBCR_TO_R( cb, cr ) ); 355 dst[i*4+1] = CLAMP_BYTE( y + CBCR_TO_G( cb, cr ) ); 356 dst[i*4+2] = CLAMP_BYTE( y + CBCR_TO_B( cb, cr ) ); 357 dst[i*4+3] = a; 358 } 359 } 360 361 /* 362 ======================== 363 idColorSpace::ConvertRGBToYCbCr420 364 ======================== 365 */ 366 void idColorSpace::ConvertRGBToYCbCr420( byte *dst, const byte *src, int width, int height ) { 367 int numSamples = 0; 368 for ( int j = 0; j < height; j += 2 ) { 369 for ( int i = 0; i < width; i += 2 ) { 370 int r0 = src[((j+0)*width+i+0)*4+0]; 371 int g0 = src[((j+0)*width+i+0)*4+1]; 372 int b0 = src[((j+0)*width+i+0)*4+2]; 373 int r1 = src[((j+0)*width+i+1)*4+0]; 374 int g1 = src[((j+0)*width+i+1)*4+1]; 375 int b1 = src[((j+0)*width+i+1)*4+2]; 376 int r2 = src[((j+1)*width+i+0)*4+0]; 377 int g2 = src[((j+1)*width+i+0)*4+1]; 378 int b2 = src[((j+1)*width+i+0)*4+2]; 379 int r3 = src[((j+1)*width+i+1)*4+0]; 380 int g3 = src[((j+1)*width+i+1)*4+1]; 381 int b3 = src[((j+1)*width+i+1)*4+2]; 382 int y0 = CLAMP_BYTE( RGB_TO_YCBCR_Y( r0, g0, b0 ) ); 383 int cb0 = CLAMP_BYTE( RGB_TO_YCBCR_CB( r0, g0, b0 ) + 128 ); 384 int cr0 = CLAMP_BYTE( RGB_TO_YCBCR_CR( r0, g0, b0 ) + 128 ); 385 int y1 = CLAMP_BYTE( RGB_TO_YCBCR_Y( r1, g1, b1 ) ); 386 int cb1 = CLAMP_BYTE( RGB_TO_YCBCR_CB( r1, g1, b1 ) + 128 ); 387 int cr1 = CLAMP_BYTE( RGB_TO_YCBCR_CR( r1, g1, b1 ) + 128 ); 388 int y2 = CLAMP_BYTE( RGB_TO_YCBCR_Y( r2, g2, b2 ) ); 389 int cb2 = CLAMP_BYTE( RGB_TO_YCBCR_CB( r2, g2, b2 ) + 128 ); 390 int cr2 = CLAMP_BYTE( RGB_TO_YCBCR_CR( r2, g2, b2 ) + 128 ); 391 int y3 = CLAMP_BYTE( RGB_TO_YCBCR_Y( r3, g3, b3 ) ); 392 int cb3 = CLAMP_BYTE( RGB_TO_YCBCR_CB( r3, g3, b3 ) + 128 ); 393 int cr3 = CLAMP_BYTE( RGB_TO_YCBCR_CR( r3, g3, b3 ) + 128 ); 394 dst[numSamples+0] = y0; 395 dst[numSamples+1] = y1; 396 dst[numSamples+2] = y2; 397 dst[numSamples+3] = y3; 398 dst[numSamples+4] = ( cb0 + cb1 + cb2 + cb3 ) >> 2; 399 dst[numSamples+5] = ( cr0 + cr1 + cr2 + cr3 ) >> 2; 400 numSamples += 6; 401 } 402 numSamples += width; 403 } 404 } 405 406 /* 407 ======================== 408 idColorSpace::ConvertYCbCr420ToRGB 409 ======================== 410 */ 411 void idColorSpace::ConvertYCbCr420ToRGB( byte *dst, const byte *src, int width, int height ) { 412 int numSamples = width * height * 2 - width; 413 for ( int j = height - 2; j >= 0; j -= 2 ) { 414 for ( int i = width - 2; i >= 0; i -= 2 ) { 415 int y0 = src[numSamples-6]; 416 int y1 = src[numSamples-5]; 417 int y2 = src[numSamples-4]; 418 int y3 = src[numSamples-3]; 419 int co = src[numSamples-2] - 128; 420 int cg = src[numSamples-1] - 128; 421 numSamples -= 6; 422 int r = CBCR_TO_R( co, cg ); 423 int g = CBCR_TO_G( co, cg ); 424 int b = CBCR_TO_B( co, cg ); 425 dst[((j+0)*width+i+0)*4+0] = CLAMP_BYTE( y0 + r ); 426 dst[((j+0)*width+i+0)*4+1] = CLAMP_BYTE( y0 + g ); 427 dst[((j+0)*width+i+0)*4+2] = CLAMP_BYTE( y0 + b ); 428 dst[((j+0)*width+i+1)*4+0] = CLAMP_BYTE( y1 + r ); 429 dst[((j+0)*width+i+1)*4+1] = CLAMP_BYTE( y1 + g ); 430 dst[((j+0)*width+i+1)*4+2] = CLAMP_BYTE( y1 + b ); 431 dst[((j+1)*width+i+0)*4+0] = CLAMP_BYTE( y2 + r ); 432 dst[((j+1)*width+i+0)*4+1] = CLAMP_BYTE( y2 + g ); 433 dst[((j+1)*width+i+0)*4+2] = CLAMP_BYTE( y2 + b ); 434 dst[((j+1)*width+i+1)*4+0] = CLAMP_BYTE( y3 + r ); 435 dst[((j+1)*width+i+1)*4+1] = CLAMP_BYTE( y3 + g ); 436 dst[((j+1)*width+i+1)*4+2] = CLAMP_BYTE( y3 + b ); 437 } 438 numSamples -= width; 439 } 440 } 441 442 /* 443 ======================== 444 idColorSpace::ConvertNormalMapToStereographicHeightMap 445 446 Converts a tangent space normal map to a height map. 447 The iterative algorithm is pretty crappy but it's reasonably fast and good enough for testing purposes. 448 The algorithm uses a stereographic projection of the normals to reduce the entropy and preserve 449 significantly more detail. 450 451 A better approach would be to solve the massive but rather sparse matrix system: 452 453 [ c(1,0) c(1,1) ... c(1,w*h) ] [ H(1,1) ] [ Nx(1,1) ] 454 [ c(2,0) c(2,1) ... c(2,w*h) ] [ H(1,2) ] = [ Ny(1,1) ] 455 [ ... ] [ ... ] [ ... ] 456 [ ... ] [ H(w,h) ] [ Nx(w,h) ] 457 [ c(w*h*2,0) c(w*h*2,1) ... c(w*h*2,w*h)] [ Ny(w,h) ] 458 459 Where: w = width, h = height, H(i,j) = height, Nx(i,j) = (normal.x/(1+normal.z), Ny(i,j) = (normal.y/(1+normal.z) 460 The c(i,j) are setup such that: 461 462 Nx(i,j) = H(i,j) - H(i,j+1) 463 Ny(i,j) = H(i,j) - H(i+1,j) 464 Nx(i,w) = H(i,w) 465 Ny(h,j) = H(h,j) 466 467 ======================== 468 */ 469 void idColorSpace::ConvertNormalMapToStereographicHeightMap( byte *heightMap, const byte *normalMap, int width, int height, float &scale ) { 470 471 idTempArray<float> buffer( (width+1) * (height+1) * sizeof( float ) ); 472 float * temp = (float *)buffer.Ptr(); 473 memset( temp, 0, (width+1) * (height+1) * sizeof( float ) ); 474 475 const int NUM_ITERATIONS = 32; 476 477 float scale0 = 0.1f; 478 float scale1 = 0.9f; 479 480 for ( int n = 0; n < NUM_ITERATIONS; n++ ) { 481 for ( int i = 0; i < height; i++ ) { 482 for ( int j = 1; j < width; j++ ) { 483 float x = NORMALMAP_BYTE_TO_FLOAT( normalMap[( (i+0) * width + (j+0) ) * 4 + 0] ); 484 float z = NORMALMAP_BYTE_TO_FLOAT( normalMap[( (i+0) * width + (j+0) ) * 4 + 2] ); 485 temp[i * width + j] = scale0 * temp[i * width + j] + scale1 * ( temp[(i+0) * width + (j-1)] - ( x / (1+z) ) ); 486 } 487 } 488 for ( int i = 1; i < height; i++ ) { 489 for ( int j = 0; j < width; j++ ) { 490 float y = NORMALMAP_BYTE_TO_FLOAT( normalMap[( (i+0) * width + (j+0) ) * 4 + 1] ); 491 float z = NORMALMAP_BYTE_TO_FLOAT( normalMap[( (i+0) * width + (j+0) ) * 4 + 2] ); 492 temp[i * width + j] = scale0 * temp[i * width + j] + scale1 * ( temp[(i-1) * width + (j+0)] - ( y / (1+z)) ); 493 } 494 } 495 for ( int i = 0; i < height; i++ ) { 496 for ( int j = width - 1; j > 0; j-- ) { 497 float x = NORMALMAP_BYTE_TO_FLOAT( normalMap[( (i+0) * width + (j-1) ) * 4 + 0] ); 498 float z = NORMALMAP_BYTE_TO_FLOAT( normalMap[( (i+0) * width + (j-1) ) * 4 + 2] ); 499 temp[i * width + (j-1)] = scale0 * temp[i * width + (j-1)] + scale1 * ( temp[(i+0) * width + (j+0)] + ( x / (1+z) ) ); 500 } 501 } 502 for ( int i = height - 1; i > 0; i-- ) { 503 for ( int j = 0; j < width; j++ ) { 504 float y = NORMALMAP_BYTE_TO_FLOAT( normalMap[( (i-1) * width + (j+0) ) * 4 + 1] ); 505 float z = NORMALMAP_BYTE_TO_FLOAT( normalMap[( (i-1) * width + (j+0) ) * 4 + 2] ); 506 temp[(i-1) * width + j] = scale0 * temp[(i-1) * width + j] + scale1 * ( temp[(i+0) * width + (j+0)] + ( y / (1+z) ) ); 507 } 508 } 509 510 scale1 *= 0.99f; 511 scale0 = 1.0f - scale1; 512 } 513 514 float minHeight = idMath::INFINITY; 515 float maxHeight = -idMath::INFINITY; 516 for ( int j = 0; j < height; j++ ) { 517 for ( int i = 0; i < width; i++ ) { 518 if ( temp[j*width+i] < minHeight ) { 519 minHeight = temp[j*width+i]; 520 } 521 if ( temp[j*width+i] > maxHeight ) { 522 maxHeight = temp[j*width+i]; 523 } 524 } 525 } 526 527 scale = ( maxHeight - minHeight ); 528 529 float s = 255.0f / scale; 530 for ( int j = 0; j < height; j++ ) { 531 for ( int i = 0; i < width; i++ ) { 532 heightMap[j*width+i] = idMath::Ftob( ( temp[j*width+i] - minHeight ) * s ); 533 } 534 } 535 } 536 537 /* 538 ======================== 539 idColorSpace::ConvertStereographicHeightMapToNormalMap 540 541 This converts a heightmap of a stereographically projected normal map back into a regular normal map. 542 ======================== 543 */ 544 void idColorSpace::ConvertStereographicHeightMapToNormalMap( byte *normalMap, const byte *heightMap, int width, int height, float scale ) { 545 for ( int i = 0; i < height; i++ ) { 546 int previ = Max( i, 0 ); 547 int nexti = Min( i + 1, height - 1 ); 548 549 for ( int j = 0; j < width; j++ ) { 550 int prevj = Max( j, 0 ); 551 int nextj = Min( j + 1, width - 1 ); 552 553 idVec3 normal; 554 float pX = scale * ( heightMap[i * width + prevj] - heightMap[i * width + nextj] ) / 255.0f; 555 float pY = scale * ( heightMap[previ * width + j] - heightMap[nexti * width + j] ) / 255.0f; 556 float denom = 2.0f / ( 1.0f + pX * pX + pY * pY ); 557 normal.x = pX * denom; 558 normal.y = pY * denom; 559 normal.z = denom - 1.0f; 560 561 normalMap[ ( i * width + j ) * 4 + 0 ] = NORMALMAP_FLOAT_TO_BYTE( normal[0] ); 562 normalMap[ ( i * width + j ) * 4 + 1 ] = NORMALMAP_FLOAT_TO_BYTE( normal[1] ); 563 normalMap[ ( i * width + j ) * 4 + 2 ] = NORMALMAP_FLOAT_TO_BYTE( normal[2] ); 564 normalMap[ ( i * width + j ) * 4 + 3 ] = 255; 565 } 566 } 567 } 568 569 /* 570 ======================== 571 idColorSpace::ConvertRGBToMonochrome 572 ======================== 573 */ 574 void idColorSpace::ConvertRGBToMonochrome( byte *mono, const byte *rgb, int width, int height ) { 575 for ( int i = 0; i < height; i++ ) { 576 for ( int j = 0; j < width; j++ ) { 577 mono[i * width + j] = ( rgb[( i * width + j ) * 4 + 0] + 578 rgb[( i * width + j ) * 4 + 1] + 579 rgb[( i * width + j ) * 4 + 2] ) / 3; 580 } 581 } 582 } 583 584 /* 585 ======================== 586 idColorSpace::ConvertMonochromeToRGB 587 ======================== 588 */ 589 void idColorSpace::ConvertMonochromeToRGB( byte *rgb, const byte *mono, int width, int height ) { 590 for ( int i = 0; i < height; i++ ) { 591 for ( int j = 0; j < width; j++ ) { 592 rgb[( i * width + j ) * 4 + 0] = mono[i * width + j]; 593 rgb[( i * width + j ) * 4 + 1] = mono[i * width + j]; 594 rgb[( i * width + j ) * 4 + 2] = mono[i * width + j]; 595 } 596 } 597 }