123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598 |
- /*
- ===========================================================================
- 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 <http://www.gnu.org/licenses/>.
- 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<float> 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];
- }
- }
- }
|