DOOM-3-BFG

DOOM 3 BFG Edition
Log | Files | Refs

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 }