/* =========================================================================== Doom 3 BFG Edition GPL Source Code Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company. This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code"). Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Doom 3 BFG Edition Source Code. If not, see . 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. 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. =========================================================================== */ #pragma hdrstop #include "../../idlib/precompiled.h" /* ================================================================================================ Contains the ColorSpace conversion implementation. ================================================================================================ */ #include "ColorSpace.h" /* ================================================================================================ To *Color-Convert RGB and YCoCg* ColorSpaces, use the following conversions: Y = [ 1/4 1/2 1/4] [R] Co = [ 1/2 0 -1/2] [G] + 128 CG = [-1/4 1/2 -1/4] [B] + 128 R = [ 1 1 -1] [Y] G = [ 1 0 1] [Co - 128] B = [ 1 -1 -1] [Cg - 128] ================================================================================================ */ #define RGB_TO_YCOCG_Y( r, g, b ) ( ( ( r + (g<<1) + b ) + 2 ) >> 2 ) #define RGB_TO_YCOCG_CO( r, g, b ) ( ( ( (r<<1) - (b<<1) ) + 2 ) >> 2 ) #define RGB_TO_YCOCG_CG( r, g, b ) ( ( ( - r + (g<<1) - b ) + 2 ) >> 2 ) #define COCG_TO_R( co, cg ) ( co - cg ) #define COCG_TO_G( co, cg ) ( cg ) #define COCG_TO_B( co, cg ) ( - co - cg ) /* ======================== idColorSpace::ConvertRGBToYCoCg ======================== */ void idColorSpace::ConvertRGBToYCoCg( byte *dst, const byte *src, int width, int height ) { for ( int i = 0; i < width * height; i++ ) { int r = src[i*4+0]; int g = src[i*4+1]; int b = src[i*4+2]; int a = src[i*4+3]; dst[i*4+0] = CLAMP_BYTE( RGB_TO_YCOCG_Y( r, g, b ) ); dst[i*4+1] = CLAMP_BYTE( RGB_TO_YCOCG_CO( r, g, b ) + 128 ); dst[i*4+2] = CLAMP_BYTE( RGB_TO_YCOCG_CG( r, g, b ) + 128 ); dst[i*4+3] = a; } } /* ======================== idColorSpace::ConvertYCoCgToRGB ======================== */ void idColorSpace::ConvertYCoCgToRGB( byte *dst, const byte *src, int width, int height ) { for ( int i = 0; i < width * height; i++ ) { int y = src[i*4+0]; int co = src[i*4+1] - 128; int cg = src[i*4+2] - 128; int a = src[i*4+3]; dst[i*4+0] = CLAMP_BYTE( y + COCG_TO_R( co, cg ) ); dst[i*4+1] = CLAMP_BYTE( y + COCG_TO_G( co, cg ) ); dst[i*4+2] = CLAMP_BYTE( y + COCG_TO_B( co, cg ) ); dst[i*4+3] = a; } } /* ======================== idColorSpace::ConvertRGBToCoCg_Y ======================== */ void idColorSpace::ConvertRGBToCoCg_Y( byte *dst, const byte *src, int width, int height ) { for ( int i = 0; i < width * height; i++ ) { int r = src[i*4+0]; int g = src[i*4+1]; int b = src[i*4+2]; //int a = src[i*4+3]; dst[i*4+0] = CLAMP_BYTE( RGB_TO_YCOCG_CO( r, g, b ) + 128 ); dst[i*4+1] = CLAMP_BYTE( RGB_TO_YCOCG_CG( r, g, b ) + 128 ); dst[i*4+2] = 0; dst[i*4+3] = CLAMP_BYTE( RGB_TO_YCOCG_Y( r, g, b ) ); } } /* ======================== idColorSpace::ConvertCoCg_YToRGB ======================== */ void idColorSpace::ConvertCoCg_YToRGB( byte *dst, const byte *src, int width, int height ) { for ( int i = 0; i < width * height; i++ ) { int co = src[i*4+0] - 128; int cg = src[i*4+1] - 128; int a = src[i*4+2]; int y = src[i*4+3]; dst[i*4+0] = CLAMP_BYTE( y + COCG_TO_R( co, cg ) ); dst[i*4+1] = CLAMP_BYTE( y + COCG_TO_G( co, cg ) ); dst[i*4+2] = CLAMP_BYTE( y + COCG_TO_B( co, cg ) ); dst[i*4+3] = a; } } /* ======================== idColorSpace::ConvertCoCgSYToRGB A scale factor is encoded in the Z value to give better compression of the color channels. ======================== */ void idColorSpace::ConvertCoCgSYToRGB( byte * dst, const byte * src, int width, int height ) { for ( int i = 0; i < width * height; i++ ) { int co = src[i*4+0] - 128; int cg = src[i*4+1] - 128; int a = src[i*4+2]; int y = src[i*4+3]; float scale = 1.0f / ( 1.0f + a * ( 31.875f / 255.0f ) ) ; co = idMath::Ftoi( co * scale ); cg = idMath::Ftoi( cg * scale ); dst[i*4+0] = CLAMP_BYTE( y + COCG_TO_R( co, cg ) ); dst[i*4+1] = CLAMP_BYTE( y + COCG_TO_G( co, cg ) ); dst[i*4+2] = CLAMP_BYTE( y + COCG_TO_B( co, cg ) ); dst[i*4+3] = 255; } } /* ======================== idColorSpace::ConvertRGBToYCoCg420 ======================== */ void idColorSpace::ConvertRGBToYCoCg420( byte *dst, const byte *src, int width, int height ) { int numSamples = 0; for ( int j = 0; j < height; j += 2 ) { for ( int i = 0; i < width; i += 2 ) { int r0 = src[((j+0)*width+i+0)*4+0]; int g0 = src[((j+0)*width+i+0)*4+1]; int b0 = src[((j+0)*width+i+0)*4+2]; int r1 = src[((j+0)*width+i+1)*4+0]; int g1 = src[((j+0)*width+i+1)*4+1]; int b1 = src[((j+0)*width+i+1)*4+2]; int r2 = src[((j+1)*width+i+0)*4+0]; int g2 = src[((j+1)*width+i+0)*4+1]; int b2 = src[((j+1)*width+i+0)*4+2]; int r3 = src[((j+1)*width+i+1)*4+0]; int g3 = src[((j+1)*width+i+1)*4+1]; int b3 = src[((j+1)*width+i+1)*4+2]; int y0 = CLAMP_BYTE( RGB_TO_YCOCG_Y( r0, g0, b0 ) ); int co0 = CLAMP_BYTE( RGB_TO_YCOCG_CO( r0, g0, b0 ) + 128 ); int cg0 = CLAMP_BYTE( RGB_TO_YCOCG_CG( r0, g0, b0 ) + 128 ); int y1 = CLAMP_BYTE( RGB_TO_YCOCG_Y( r1, g1, b1 ) ); int co1 = CLAMP_BYTE( RGB_TO_YCOCG_CO( r1, g1, b1 ) + 128 ); int cg1 = CLAMP_BYTE( RGB_TO_YCOCG_CG( r1, g1, b1 ) + 128 ); int y2 = CLAMP_BYTE( RGB_TO_YCOCG_Y( r2, g2, b2 ) ); int co2 = CLAMP_BYTE( RGB_TO_YCOCG_CO( r2, g2, b2 ) + 128 ); int cg2 = CLAMP_BYTE( RGB_TO_YCOCG_CG( r2, g2, b2 ) + 128 ); int y3 = CLAMP_BYTE( RGB_TO_YCOCG_Y( r3, g3, b3 ) ); int co3 = CLAMP_BYTE( RGB_TO_YCOCG_CO( r3, g3, b3 ) + 128 ); int cg3 = CLAMP_BYTE( RGB_TO_YCOCG_CG( r3, g3, b3 ) + 128 ); dst[numSamples+0] = y0; dst[numSamples+1] = y1; dst[numSamples+2] = y2; dst[numSamples+3] = y3; dst[numSamples+4] = ( co0 + co1 + co2 + co3 ) >> 2; dst[numSamples+5] = ( cg0 + cg1 + cg2 + cg3 ) >> 2; numSamples += 6; } numSamples += width; } } /* ======================== idColorSpace::ConvertYCoCg420ToRGB ======================== */ void idColorSpace::ConvertYCoCg420ToRGB( byte *dst, const byte *src, int width, int height ) { int numSamples = width * height * 2 - width; for ( int j = height - 2; j >= 0; j -= 2 ) { for ( int i = width - 2; i >= 0; i -= 2 ) { int y0 = src[numSamples-6]; int y1 = src[numSamples-5]; int y2 = src[numSamples-4]; int y3 = src[numSamples-3]; int co = src[numSamples-2] - 128; int cg = src[numSamples-1] - 128; numSamples -= 6; int r = COCG_TO_R( co, cg ); int g = COCG_TO_G( co, cg ); int b = COCG_TO_B( co, cg ); dst[((j+0)*width+i+0)*4+0] = CLAMP_BYTE( y0 + r ); dst[((j+0)*width+i+0)*4+1] = CLAMP_BYTE( y0 + g ); dst[((j+0)*width+i+0)*4+2] = CLAMP_BYTE( y0 + b ); dst[((j+0)*width+i+1)*4+0] = CLAMP_BYTE( y1 + r ); dst[((j+0)*width+i+1)*4+1] = CLAMP_BYTE( y1 + g ); dst[((j+0)*width+i+1)*4+2] = CLAMP_BYTE( y1 + b ); dst[((j+1)*width+i+0)*4+0] = CLAMP_BYTE( y2 + r ); dst[((j+1)*width+i+0)*4+1] = CLAMP_BYTE( y2 + g ); dst[((j+1)*width+i+0)*4+2] = CLAMP_BYTE( y2 + b ); dst[((j+1)*width+i+1)*4+0] = CLAMP_BYTE( y3 + r ); dst[((j+1)*width+i+1)*4+1] = CLAMP_BYTE( y3 + g ); dst[((j+1)*width+i+1)*4+2] = CLAMP_BYTE( y3 + b ); } numSamples -= width; } } /* ================================================================================================ To *Color-Convert RGB and YCbCr* ColorSpaces, note that YCbCr is defined per CCIR 601-1, except that Cb and Cr are normalized to the range 0 -> 255 rather than -0.5 -> 0.5. The conversion equations to be implemented are therefore: Y = [ 0.29900 0.58700 0.11400] [R] Cb = [-0.16874 -0.33126 0.50000] [G] + 128 Cr = [ 0.50000 -0.41869 -0.08131] [B] + 128 R = [ 1.00000 0.00000 1.40200] [Y] G = [ 1.00000 -0.34414 -0.71414] [Cb - 128] B = [ 1.00000 1.77200 0.00000] [Cr - 128] These numbers are derived from TIFF 6.0 section 21, dated 3-June-92. To avoid floating-point arithmetic, we represent the fractional constants as integers scaled up by 2^16 (about 4 digits precision); we have to divide the products by 2^16, with appropriate rounding, to get the correct answer. ================================================================================================ */ const int ycbcr_shift = 16; const int ycbcr_round = 1 << ( ycbcr_shift - 1 ); const int r029900 = 19595; // int( 0.29900 * (1<<16) + 0.5 ) const int g058700 = 38470; // int( 0.58700 * (1<<16) + 0.5 ) const int b011400 = 7471; // int( 0.11400 * (1<<16) + 0.5 ) const int r016874 = 11059; // int( 0.16874 * (1<<16) + 0.5 ) const int g033126 = 21709; // int( 0.33126 * (1<<16) + 0.5 ) const int b050000 = 32768; // int( 0.50000 * (1<<16) + 0.5 ) const int r050000 = 32768; // int( 0.50000 * (1<<16) + 0.5 ) const int g041869 = 27439; // int( 0.41869 * (1<<16) + 0.5 ) const int b008131 = 5329; // int( 0.08131 * (1<<16) + 0.5 ) const int r140200 = 91881; // int( 1.40200 * (1<<16) + 0.5 ) const int b177200 = 116130; // int( 1.77200 * (1<<16) + 0.5 ) const int g071414 = 46802; // int( 0.71414 * (1<<16) + 0.5 ) const int g034414 = 22554; // int( 0.34414 * (1<<16) + 0.5 ) #define RGB_TO_YCBCR_Y( r, g, b ) ( ( ( r * r029900 + g * g058700 + b * b011400 ) + ycbcr_round ) >> ycbcr_shift ) #define RGB_TO_YCBCR_CB( r, g, b ) ( ( ( - r * r016874 - g * g033126 + b * b050000 ) + ycbcr_round ) >> ycbcr_shift ) #define RGB_TO_YCBCR_CR( r, g, b ) ( ( ( r * r050000 - g * g041869 - b * b008131 ) + ycbcr_round ) >> ycbcr_shift ) #define CBCR_TO_R( cb, cr ) ( ( ycbcr_round + cr * r140200 ) >> ycbcr_shift ) #define CBCR_TO_G( cb, cr ) ( ( ycbcr_round - cb * g034414 - cr * g071414 ) >> ycbcr_shift ) #define CBCR_TO_B( cb, cr ) ( ( ycbcr_round + cb * b177200 ) >> ycbcr_shift ) /* ======================== idColorSpace::ConvertRGBToYCbCr ======================== */ void idColorSpace::ConvertRGBToYCbCr( byte *dst, const byte *src, int width, int height ) { for ( int i = 0; i < width * height; i++ ) { int r = src[i*4+0]; int g = src[i*4+1]; int b = src[i*4+2]; int a = src[i*4+3]; dst[i*4+0] = CLAMP_BYTE( RGB_TO_YCBCR_Y( r, g, b ) ); dst[i*4+1] = CLAMP_BYTE( RGB_TO_YCBCR_CB( r, g, b ) + 128 ); dst[i*4+2] = CLAMP_BYTE( RGB_TO_YCBCR_CR( r, g, b ) + 128 ); dst[i*4+3] = a; } } /* ======================== idColorSpace::ConvertYCbCrToRGB ======================== */ void idColorSpace::ConvertYCbCrToRGB( byte *dst, const byte *src, int width, int height ) { for ( int i = 0; i < width * height; i++ ) { int y = src[i*4+0]; int cb = src[i*4+1] - 128; int cr = src[i*4+2] - 128; dst[i*4+0] = CLAMP_BYTE( y + CBCR_TO_R( cb, cr ) ); dst[i*4+1] = CLAMP_BYTE( y + CBCR_TO_G( cb, cr ) ); dst[i*4+2] = CLAMP_BYTE( y + CBCR_TO_B( cb, cr ) ); } } /* ======================== idColorSpace::ConvertRGBToCbCr_Y ======================== */ void idColorSpace::ConvertRGBToCbCr_Y( byte *dst, const byte *src, int width, int height ) { for ( int i = 0; i < width * height; i++ ) { int r = src[i*4+0]; int g = src[i*4+1]; int b = src[i*4+2]; int a = src[i*4+3]; dst[i*4+0] = CLAMP_BYTE( RGB_TO_YCBCR_CB( r, g, b ) + 128 ); dst[i*4+1] = CLAMP_BYTE( RGB_TO_YCBCR_CR( r, g, b ) + 128 ); dst[i*4+2] = a; dst[i*4+3] = CLAMP_BYTE( RGB_TO_YCBCR_Y( r, g, b ) ); } } /* ======================== idColorSpace::ConvertCbCr_YToRGB ======================== */ void idColorSpace::ConvertCbCr_YToRGB( byte *dst, const byte *src, int width, int height ) { for ( int i = 0; i < width * height; i++ ) { int cb = src[i*4+0] - 128; int cr = src[i*4+1] - 128; int a = src[i*4+2]; int y = src[i*4+3]; dst[i*4+0] = CLAMP_BYTE( y + CBCR_TO_R( cb, cr ) ); dst[i*4+1] = CLAMP_BYTE( y + CBCR_TO_G( cb, cr ) ); dst[i*4+2] = CLAMP_BYTE( y + CBCR_TO_B( cb, cr ) ); dst[i*4+3] = a; } } /* ======================== idColorSpace::ConvertRGBToYCbCr420 ======================== */ void idColorSpace::ConvertRGBToYCbCr420( byte *dst, const byte *src, int width, int height ) { int numSamples = 0; for ( int j = 0; j < height; j += 2 ) { for ( int i = 0; i < width; i += 2 ) { int r0 = src[((j+0)*width+i+0)*4+0]; int g0 = src[((j+0)*width+i+0)*4+1]; int b0 = src[((j+0)*width+i+0)*4+2]; int r1 = src[((j+0)*width+i+1)*4+0]; int g1 = src[((j+0)*width+i+1)*4+1]; int b1 = src[((j+0)*width+i+1)*4+2]; int r2 = src[((j+1)*width+i+0)*4+0]; int g2 = src[((j+1)*width+i+0)*4+1]; int b2 = src[((j+1)*width+i+0)*4+2]; int r3 = src[((j+1)*width+i+1)*4+0]; int g3 = src[((j+1)*width+i+1)*4+1]; int b3 = src[((j+1)*width+i+1)*4+2]; int y0 = CLAMP_BYTE( RGB_TO_YCBCR_Y( r0, g0, b0 ) ); int cb0 = CLAMP_BYTE( RGB_TO_YCBCR_CB( r0, g0, b0 ) + 128 ); int cr0 = CLAMP_BYTE( RGB_TO_YCBCR_CR( r0, g0, b0 ) + 128 ); int y1 = CLAMP_BYTE( RGB_TO_YCBCR_Y( r1, g1, b1 ) ); int cb1 = CLAMP_BYTE( RGB_TO_YCBCR_CB( r1, g1, b1 ) + 128 ); int cr1 = CLAMP_BYTE( RGB_TO_YCBCR_CR( r1, g1, b1 ) + 128 ); int y2 = CLAMP_BYTE( RGB_TO_YCBCR_Y( r2, g2, b2 ) ); int cb2 = CLAMP_BYTE( RGB_TO_YCBCR_CB( r2, g2, b2 ) + 128 ); int cr2 = CLAMP_BYTE( RGB_TO_YCBCR_CR( r2, g2, b2 ) + 128 ); int y3 = CLAMP_BYTE( RGB_TO_YCBCR_Y( r3, g3, b3 ) ); int cb3 = CLAMP_BYTE( RGB_TO_YCBCR_CB( r3, g3, b3 ) + 128 ); int cr3 = CLAMP_BYTE( RGB_TO_YCBCR_CR( r3, g3, b3 ) + 128 ); dst[numSamples+0] = y0; dst[numSamples+1] = y1; dst[numSamples+2] = y2; dst[numSamples+3] = y3; dst[numSamples+4] = ( cb0 + cb1 + cb2 + cb3 ) >> 2; dst[numSamples+5] = ( cr0 + cr1 + cr2 + cr3 ) >> 2; numSamples += 6; } numSamples += width; } } /* ======================== idColorSpace::ConvertYCbCr420ToRGB ======================== */ void idColorSpace::ConvertYCbCr420ToRGB( byte *dst, const byte *src, int width, int height ) { int numSamples = width * height * 2 - width; for ( int j = height - 2; j >= 0; j -= 2 ) { for ( int i = width - 2; i >= 0; i -= 2 ) { int y0 = src[numSamples-6]; int y1 = src[numSamples-5]; int y2 = src[numSamples-4]; int y3 = src[numSamples-3]; int co = src[numSamples-2] - 128; int cg = src[numSamples-1] - 128; numSamples -= 6; int r = CBCR_TO_R( co, cg ); int g = CBCR_TO_G( co, cg ); int b = CBCR_TO_B( co, cg ); dst[((j+0)*width+i+0)*4+0] = CLAMP_BYTE( y0 + r ); dst[((j+0)*width+i+0)*4+1] = CLAMP_BYTE( y0 + g ); dst[((j+0)*width+i+0)*4+2] = CLAMP_BYTE( y0 + b ); dst[((j+0)*width+i+1)*4+0] = CLAMP_BYTE( y1 + r ); dst[((j+0)*width+i+1)*4+1] = CLAMP_BYTE( y1 + g ); dst[((j+0)*width+i+1)*4+2] = CLAMP_BYTE( y1 + b ); dst[((j+1)*width+i+0)*4+0] = CLAMP_BYTE( y2 + r ); dst[((j+1)*width+i+0)*4+1] = CLAMP_BYTE( y2 + g ); dst[((j+1)*width+i+0)*4+2] = CLAMP_BYTE( y2 + b ); dst[((j+1)*width+i+1)*4+0] = CLAMP_BYTE( y3 + r ); dst[((j+1)*width+i+1)*4+1] = CLAMP_BYTE( y3 + g ); dst[((j+1)*width+i+1)*4+2] = CLAMP_BYTE( y3 + b ); } numSamples -= width; } } /* ======================== idColorSpace::ConvertNormalMapToStereographicHeightMap Converts a tangent space normal map to a height map. The iterative algorithm is pretty crappy but it's reasonably fast and good enough for testing purposes. The algorithm uses a stereographic projection of the normals to reduce the entropy and preserve significantly more detail. A better approach would be to solve the massive but rather sparse matrix system: [ c(1,0) c(1,1) ... c(1,w*h) ] [ H(1,1) ] [ Nx(1,1) ] [ c(2,0) c(2,1) ... c(2,w*h) ] [ H(1,2) ] = [ Ny(1,1) ] [ ... ] [ ... ] [ ... ] [ ... ] [ H(w,h) ] [ Nx(w,h) ] [ c(w*h*2,0) c(w*h*2,1) ... c(w*h*2,w*h)] [ Ny(w,h) ] 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) The c(i,j) are setup such that: Nx(i,j) = H(i,j) - H(i,j+1) Ny(i,j) = H(i,j) - H(i+1,j) Nx(i,w) = H(i,w) Ny(h,j) = H(h,j) ======================== */ void idColorSpace::ConvertNormalMapToStereographicHeightMap( byte *heightMap, const byte *normalMap, int width, int height, float &scale ) { idTempArray buffer( (width+1) * (height+1) * sizeof( float ) ); float * temp = (float *)buffer.Ptr(); memset( temp, 0, (width+1) * (height+1) * sizeof( float ) ); const int NUM_ITERATIONS = 32; float scale0 = 0.1f; float scale1 = 0.9f; for ( int n = 0; n < NUM_ITERATIONS; n++ ) { for ( int i = 0; i < height; i++ ) { for ( int j = 1; j < width; j++ ) { float x = NORMALMAP_BYTE_TO_FLOAT( normalMap[( (i+0) * width + (j+0) ) * 4 + 0] ); float z = NORMALMAP_BYTE_TO_FLOAT( normalMap[( (i+0) * width + (j+0) ) * 4 + 2] ); temp[i * width + j] = scale0 * temp[i * width + j] + scale1 * ( temp[(i+0) * width + (j-1)] - ( x / (1+z) ) ); } } for ( int i = 1; i < height; i++ ) { for ( int j = 0; j < width; j++ ) { float y = NORMALMAP_BYTE_TO_FLOAT( normalMap[( (i+0) * width + (j+0) ) * 4 + 1] ); float z = NORMALMAP_BYTE_TO_FLOAT( normalMap[( (i+0) * width + (j+0) ) * 4 + 2] ); temp[i * width + j] = scale0 * temp[i * width + j] + scale1 * ( temp[(i-1) * width + (j+0)] - ( y / (1+z)) ); } } for ( int i = 0; i < height; i++ ) { for ( int j = width - 1; j > 0; j-- ) { float x = NORMALMAP_BYTE_TO_FLOAT( normalMap[( (i+0) * width + (j-1) ) * 4 + 0] ); float z = NORMALMAP_BYTE_TO_FLOAT( normalMap[( (i+0) * width + (j-1) ) * 4 + 2] ); temp[i * width + (j-1)] = scale0 * temp[i * width + (j-1)] + scale1 * ( temp[(i+0) * width + (j+0)] + ( x / (1+z) ) ); } } for ( int i = height - 1; i > 0; i-- ) { for ( int j = 0; j < width; j++ ) { float y = NORMALMAP_BYTE_TO_FLOAT( normalMap[( (i-1) * width + (j+0) ) * 4 + 1] ); float z = NORMALMAP_BYTE_TO_FLOAT( normalMap[( (i-1) * width + (j+0) ) * 4 + 2] ); temp[(i-1) * width + j] = scale0 * temp[(i-1) * width + j] + scale1 * ( temp[(i+0) * width + (j+0)] + ( y / (1+z) ) ); } } scale1 *= 0.99f; scale0 = 1.0f - scale1; } float minHeight = idMath::INFINITY; float maxHeight = -idMath::INFINITY; for ( int j = 0; j < height; j++ ) { for ( int i = 0; i < width; i++ ) { if ( temp[j*width+i] < minHeight ) { minHeight = temp[j*width+i]; } if ( temp[j*width+i] > maxHeight ) { maxHeight = temp[j*width+i]; } } } scale = ( maxHeight - minHeight ); float s = 255.0f / scale; for ( int j = 0; j < height; j++ ) { for ( int i = 0; i < width; i++ ) { heightMap[j*width+i] = idMath::Ftob( ( temp[j*width+i] - minHeight ) * s ); } } } /* ======================== idColorSpace::ConvertStereographicHeightMapToNormalMap This converts a heightmap of a stereographically projected normal map back into a regular normal map. ======================== */ void idColorSpace::ConvertStereographicHeightMapToNormalMap( byte *normalMap, const byte *heightMap, int width, int height, float scale ) { for ( int i = 0; i < height; i++ ) { int previ = Max( i, 0 ); int nexti = Min( i + 1, height - 1 ); for ( int j = 0; j < width; j++ ) { int prevj = Max( j, 0 ); int nextj = Min( j + 1, width - 1 ); idVec3 normal; float pX = scale * ( heightMap[i * width + prevj] - heightMap[i * width + nextj] ) / 255.0f; float pY = scale * ( heightMap[previ * width + j] - heightMap[nexti * width + j] ) / 255.0f; float denom = 2.0f / ( 1.0f + pX * pX + pY * pY ); normal.x = pX * denom; normal.y = pY * denom; normal.z = denom - 1.0f; normalMap[ ( i * width + j ) * 4 + 0 ] = NORMALMAP_FLOAT_TO_BYTE( normal[0] ); normalMap[ ( i * width + j ) * 4 + 1 ] = NORMALMAP_FLOAT_TO_BYTE( normal[1] ); normalMap[ ( i * width + j ) * 4 + 2 ] = NORMALMAP_FLOAT_TO_BYTE( normal[2] ); normalMap[ ( i * width + j ) * 4 + 3 ] = 255; } } } /* ======================== idColorSpace::ConvertRGBToMonochrome ======================== */ void idColorSpace::ConvertRGBToMonochrome( byte *mono, const byte *rgb, int width, int height ) { for ( int i = 0; i < height; i++ ) { for ( int j = 0; j < width; j++ ) { mono[i * width + j] = ( rgb[( i * width + j ) * 4 + 0] + rgb[( i * width + j ) * 4 + 1] + rgb[( i * width + j ) * 4 + 2] ) / 3; } } } /* ======================== idColorSpace::ConvertMonochromeToRGB ======================== */ void idColorSpace::ConvertMonochromeToRGB( byte *rgb, const byte *mono, int width, int height ) { for ( int i = 0; i < height; i++ ) { for ( int j = 0; j < width; j++ ) { rgb[( i * width + j ) * 4 + 0] = mono[i * width + j]; rgb[( i * width + j ) * 4 + 1] = mono[i * width + j]; rgb[( i * width + j ) * 4 + 2] = mono[i * width + j]; } } }