mirror of
https://github.com/id-Software/DOOM-3-BFG.git
synced 2024-12-02 17:02:17 +00:00
493 lines
16 KiB
C++
493 lines
16 KiB
C++
|
/*
|
||
|
===========================================================================
|
||
|
|
||
|
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"
|
||
|
|
||
|
|
||
|
#include "tr_local.h"
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
R_ResampleTexture
|
||
|
|
||
|
Used to resample images in a more general than quartering fashion.
|
||
|
|
||
|
This will only have filter coverage if the resampled size
|
||
|
is greater than half the original size.
|
||
|
|
||
|
If a larger shrinking is needed, use the mipmap function
|
||
|
after resampling to the next lower power of two.
|
||
|
================
|
||
|
*/
|
||
|
#define MAX_DIMENSION 4096
|
||
|
byte *R_ResampleTexture( const byte *in, int inwidth, int inheight,
|
||
|
int outwidth, int outheight ) {
|
||
|
int i, j;
|
||
|
const byte *inrow, *inrow2;
|
||
|
unsigned int frac, fracstep;
|
||
|
unsigned int p1[MAX_DIMENSION], p2[MAX_DIMENSION];
|
||
|
const byte *pix1, *pix2, *pix3, *pix4;
|
||
|
byte *out, *out_p;
|
||
|
|
||
|
if ( outwidth > MAX_DIMENSION ) {
|
||
|
outwidth = MAX_DIMENSION;
|
||
|
}
|
||
|
if ( outheight > MAX_DIMENSION ) {
|
||
|
outheight = MAX_DIMENSION;
|
||
|
}
|
||
|
|
||
|
out = (byte *)R_StaticAlloc( outwidth * outheight * 4, TAG_IMAGE );
|
||
|
out_p = out;
|
||
|
|
||
|
fracstep = inwidth*0x10000/outwidth;
|
||
|
|
||
|
frac = fracstep>>2;
|
||
|
for ( i=0 ; i<outwidth ; i++ ) {
|
||
|
p1[i] = 4*(frac>>16);
|
||
|
frac += fracstep;
|
||
|
}
|
||
|
frac = 3*(fracstep>>2);
|
||
|
for ( i=0 ; i<outwidth ; i++ ) {
|
||
|
p2[i] = 4*(frac>>16);
|
||
|
frac += fracstep;
|
||
|
}
|
||
|
|
||
|
for (i=0 ; i<outheight ; i++, out_p += outwidth*4 ) {
|
||
|
inrow = in + 4 * inwidth * (int)( ( i + 0.25f ) * inheight / outheight );
|
||
|
inrow2 = in + 4 * inwidth * (int)( ( i + 0.75f ) * inheight / outheight );
|
||
|
frac = fracstep >> 1;
|
||
|
for (j=0 ; j<outwidth ; j++) {
|
||
|
pix1 = inrow + p1[j];
|
||
|
pix2 = inrow + p2[j];
|
||
|
pix3 = inrow2 + p1[j];
|
||
|
pix4 = inrow2 + p2[j];
|
||
|
out_p[j*4+0] = (pix1[0] + pix2[0] + pix3[0] + pix4[0])>>2;
|
||
|
out_p[j*4+1] = (pix1[1] + pix2[1] + pix3[1] + pix4[1])>>2;
|
||
|
out_p[j*4+2] = (pix1[2] + pix2[2] + pix3[2] + pix4[2])>>2;
|
||
|
out_p[j*4+3] = (pix1[3] + pix2[3] + pix3[3] + pix4[3])>>2;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return out;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
R_Dropsample
|
||
|
|
||
|
Used to resample images in a more general than quartering fashion.
|
||
|
Normal maps and such should not be bilerped.
|
||
|
================
|
||
|
*/
|
||
|
byte *R_Dropsample( const byte *in, int inwidth, int inheight,
|
||
|
int outwidth, int outheight ) {
|
||
|
int i, j, k;
|
||
|
const byte *inrow;
|
||
|
const byte *pix1;
|
||
|
byte *out, *out_p;
|
||
|
|
||
|
out = (byte *)R_StaticAlloc( outwidth * outheight * 4, TAG_IMAGE );
|
||
|
out_p = out;
|
||
|
|
||
|
for (i=0 ; i<outheight ; i++, out_p += outwidth*4 ) {
|
||
|
inrow = in + 4*inwidth*(int)((i+0.25)*inheight/outheight);
|
||
|
for (j=0 ; j<outwidth ; j++) {
|
||
|
k = j * inwidth / outwidth;
|
||
|
pix1 = inrow + k * 4;
|
||
|
out_p[j*4+0] = pix1[0];
|
||
|
out_p[j*4+1] = pix1[1];
|
||
|
out_p[j*4+2] = pix1[2];
|
||
|
out_p[j*4+3] = pix1[3];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return out;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
R_SetAlphaNormalDivergence
|
||
|
|
||
|
If any of the angles inside the cone would directly reflect to the light, there will be
|
||
|
a specular highlight. The intensity of the highlight is inversely proportional to the
|
||
|
area of the spread.
|
||
|
|
||
|
Light source area is important for the base size.
|
||
|
|
||
|
area subtended in light is the divergence times the distance
|
||
|
|
||
|
Shininess value is subtracted from the divergence
|
||
|
|
||
|
Sets the alpha channel to the greatest divergence dot product of the surrounding texels.
|
||
|
1.0 = flat, 0.0 = turns a 90 degree angle
|
||
|
Lower values give less shiny specular
|
||
|
With mip maps, the lowest samnpled value will be retained
|
||
|
|
||
|
Should we rewrite the normal as the centered average?
|
||
|
================
|
||
|
*/
|
||
|
void R_SetAlphaNormalDivergence( byte *in, int width, int height ) {
|
||
|
for ( int y = 0 ; y < height ; y++ ) {
|
||
|
for ( int x = 0 ; x < width ; x++ ) {
|
||
|
// the divergence is the smallest dot product of any of the eight surrounding texels
|
||
|
byte *pic_p = in + ( y * width + x ) * 4;
|
||
|
idVec3 center;
|
||
|
center[0] = ( pic_p[0] - 128 ) / 127;
|
||
|
center[1] = ( pic_p[1] - 128 ) / 127;
|
||
|
center[2] = ( pic_p[2] - 128 ) / 127;
|
||
|
center.Normalize();
|
||
|
|
||
|
float maxDiverge = 1.0;
|
||
|
|
||
|
// FIXME: this assumes wrap mode, but should handle clamp modes and border colors
|
||
|
for ( int yy = -1 ; yy <= 1 ; yy++ ) {
|
||
|
for ( int xx = -1 ; xx <= 1 ; xx++ ) {
|
||
|
if ( yy == 0 && xx == 0 ) {
|
||
|
continue;
|
||
|
}
|
||
|
byte *corner_p = in + ( ((y+yy)&(height-1)) * width + ((x+xx)&width-1) ) * 4;
|
||
|
idVec3 corner;
|
||
|
corner[0] = ( corner_p[0] - 128 ) / 127;
|
||
|
corner[1] = ( corner_p[1] - 128 ) / 127;
|
||
|
corner[2] = ( corner_p[2] - 128 ) / 127;
|
||
|
corner.Normalize();
|
||
|
|
||
|
float diverge = corner * center;
|
||
|
if ( diverge < maxDiverge ) {
|
||
|
maxDiverge = diverge;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// we can get a diverge < 0 in some extreme cases
|
||
|
if ( maxDiverge < 0 ) {
|
||
|
maxDiverge = 0;
|
||
|
}
|
||
|
pic_p[3] = maxDiverge * 255;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
R_MipMapWithAlphaSpecularity
|
||
|
|
||
|
Returns a new copy of the texture, quartered in size and filtered.
|
||
|
The alpha channel is taken to be the minimum of the dots of all surrounding normals.
|
||
|
================
|
||
|
*/
|
||
|
#define MIP_MIN(a,b) (a<b?a:b)
|
||
|
|
||
|
byte *R_MipMapWithAlphaSpecularity( const byte *in, int width, int height ) {
|
||
|
int i, j, c, x, y, sx, sy;
|
||
|
const byte *in_p;
|
||
|
byte *out, *out_p;
|
||
|
int row;
|
||
|
int newWidth, newHeight;
|
||
|
float *fbuf, *fbuf_p;
|
||
|
|
||
|
if ( width < 1 || height < 1 || ( width + height == 2 ) ) {
|
||
|
common->FatalError( "R_MipMapWithAlphaMin called with size %i,%i", width, height );
|
||
|
}
|
||
|
|
||
|
// convert the incoming texture to centered floating point
|
||
|
c = width * height;
|
||
|
fbuf = (float *)_alloca( c * 4 * sizeof( *fbuf ) );
|
||
|
in_p = in;
|
||
|
fbuf_p = fbuf;
|
||
|
for ( i = 0 ; i < c ; i++, in_p+=4, fbuf_p += 4 ) {
|
||
|
fbuf_p[0] = ( in_p[0] / 255.0 ) * 2.0 - 1.0; // convert to a normal
|
||
|
fbuf_p[1] = ( in_p[1] / 255.0 ) * 2.0 - 1.0;
|
||
|
fbuf_p[2] = ( in_p[2] / 255.0 ) * 2.0 - 1.0;
|
||
|
fbuf_p[3] = ( in_p[3] / 255.0 ); // filtered divegence / specularity
|
||
|
}
|
||
|
|
||
|
row = width * 4;
|
||
|
|
||
|
newWidth = width >> 1;
|
||
|
newHeight = height >> 1;
|
||
|
if ( !newWidth ) {
|
||
|
newWidth = 1;
|
||
|
}
|
||
|
if ( !newHeight ) {
|
||
|
newHeight = 1;
|
||
|
}
|
||
|
out = (byte *)R_StaticAlloc( newWidth * newHeight * 4, TAG_IMAGE );
|
||
|
out_p = out;
|
||
|
|
||
|
in_p = in;
|
||
|
|
||
|
for ( i=0 ; i<newHeight ; i++ ) {
|
||
|
for ( j=0 ; j<newWidth ; j++, out_p+=4 ) {
|
||
|
idVec3 total;
|
||
|
float totalSpec;
|
||
|
|
||
|
total.Zero();
|
||
|
totalSpec = 0;
|
||
|
// find the average normal
|
||
|
for ( x = -1 ; x <= 1 ; x++ ) {
|
||
|
sx = ( j * 2 + x ) & (width-1);
|
||
|
for ( y = -1 ; y <= 1 ; y++ ) {
|
||
|
sy = ( i * 2 + y ) & (height-1);
|
||
|
fbuf_p = fbuf + ( sy * width + sx ) * 4;
|
||
|
|
||
|
total[0] += fbuf_p[0];
|
||
|
total[1] += fbuf_p[1];
|
||
|
total[2] += fbuf_p[2];
|
||
|
|
||
|
totalSpec += fbuf_p[3];
|
||
|
}
|
||
|
}
|
||
|
total.Normalize();
|
||
|
totalSpec /= 9.0;
|
||
|
|
||
|
// find the maximum divergence
|
||
|
for ( x = -1 ; x <= 1 ; x++ ) {
|
||
|
for ( y = -1 ; y <= 1 ; y++ ) {
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// store the average normal and divergence
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return out;
|
||
|
}
|
||
|
|
||
|
float mip_gammaTable[256] = {
|
||
|
0.000000f, 0.000005f, 0.000023f, 0.000057f, 0.000107f, 0.000175f, 0.000262f, 0.000367f, 0.000493f, 0.000638f, 0.000805f, 0.000992f, 0.001202f, 0.001433f, 0.001687f, 0.001963f,
|
||
|
0.002263f, 0.002586f, 0.002932f, 0.003303f, 0.003697f, 0.004116f, 0.004560f, 0.005028f, 0.005522f, 0.006041f, 0.006585f, 0.007155f, 0.007751f, 0.008373f, 0.009021f, 0.009696f,
|
||
|
0.010398f, 0.011126f, 0.011881f, 0.012664f, 0.013473f, 0.014311f, 0.015175f, 0.016068f, 0.016988f, 0.017936f, 0.018913f, 0.019918f, 0.020951f, 0.022013f, 0.023104f, 0.024223f,
|
||
|
0.025371f, 0.026549f, 0.027755f, 0.028991f, 0.030257f, 0.031551f, 0.032876f, 0.034230f, 0.035614f, 0.037029f, 0.038473f, 0.039947f, 0.041452f, 0.042987f, 0.044553f, 0.046149f,
|
||
|
0.047776f, 0.049433f, 0.051122f, 0.052842f, 0.054592f, 0.056374f, 0.058187f, 0.060032f, 0.061907f, 0.063815f, 0.065754f, 0.067725f, 0.069727f, 0.071761f, 0.073828f, 0.075926f,
|
||
|
0.078057f, 0.080219f, 0.082414f, 0.084642f, 0.086901f, 0.089194f, 0.091518f, 0.093876f, 0.096266f, 0.098689f, 0.101145f, 0.103634f, 0.106156f, 0.108711f, 0.111299f, 0.113921f,
|
||
|
0.116576f, 0.119264f, 0.121986f, 0.124741f, 0.127530f, 0.130352f, 0.133209f, 0.136099f, 0.139022f, 0.141980f, 0.144972f, 0.147998f, 0.151058f, 0.154152f, 0.157281f, 0.160444f,
|
||
|
0.163641f, 0.166872f, 0.170138f, 0.173439f, 0.176774f, 0.180144f, 0.183549f, 0.186989f, 0.190463f, 0.193972f, 0.197516f, 0.201096f, 0.204710f, 0.208360f, 0.212044f, 0.215764f,
|
||
|
0.219520f, 0.223310f, 0.227137f, 0.230998f, 0.234895f, 0.238828f, 0.242796f, 0.246800f, 0.250840f, 0.254916f, 0.259027f, 0.263175f, 0.267358f, 0.271577f, 0.275833f, 0.280124f,
|
||
|
0.284452f, 0.288816f, 0.293216f, 0.297653f, 0.302126f, 0.306635f, 0.311181f, 0.315763f, 0.320382f, 0.325037f, 0.329729f, 0.334458f, 0.339223f, 0.344026f, 0.348865f, 0.353741f,
|
||
|
0.358654f, 0.363604f, 0.368591f, 0.373615f, 0.378676f, 0.383775f, 0.388910f, 0.394083f, 0.399293f, 0.404541f, 0.409826f, 0.415148f, 0.420508f, 0.425905f, 0.431340f, 0.436813f,
|
||
|
0.442323f, 0.447871f, 0.453456f, 0.459080f, 0.464741f, 0.470440f, 0.476177f, 0.481952f, 0.487765f, 0.493616f, 0.499505f, 0.505432f, 0.511398f, 0.517401f, 0.523443f, 0.529523f,
|
||
|
0.535642f, 0.541798f, 0.547994f, 0.554227f, 0.560499f, 0.566810f, 0.573159f, 0.579547f, 0.585973f, 0.592438f, 0.598942f, 0.605484f, 0.612066f, 0.618686f, 0.625345f, 0.632043f,
|
||
|
0.638779f, 0.645555f, 0.652370f, 0.659224f, 0.666117f, 0.673049f, 0.680020f, 0.687031f, 0.694081f, 0.701169f, 0.708298f, 0.715465f, 0.722672f, 0.729919f, 0.737205f, 0.744530f,
|
||
|
0.751895f, 0.759300f, 0.766744f, 0.774227f, 0.781751f, 0.789314f, 0.796917f, 0.804559f, 0.812241f, 0.819964f, 0.827726f, 0.835528f, 0.843370f, 0.851252f, 0.859174f, 0.867136f,
|
||
|
0.875138f, 0.883180f, 0.891262f, 0.899384f, 0.907547f, 0.915750f, 0.923993f, 0.932277f, 0.940601f, 0.948965f, 0.957370f, 0.965815f, 0.974300f, 0.982826f, 0.991393f, 1.000000f
|
||
|
};
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
R_MipMapGamma
|
||
|
|
||
|
Returns a new copy of the texture, quartered in size with gamma correction.
|
||
|
================
|
||
|
*/
|
||
|
byte * R_MipMapWithGamma( const byte *in, int width, int height ) {
|
||
|
int i, j;
|
||
|
const byte *in_p;
|
||
|
byte *out, *out_p;
|
||
|
int row;
|
||
|
int newWidth, newHeight;
|
||
|
|
||
|
if ( width < 1 || height < 1 || ( width + height == 2 ) ) {
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
row = width * 4;
|
||
|
|
||
|
newWidth = width >> 1;
|
||
|
newHeight = height >> 1;
|
||
|
if ( !newWidth ) {
|
||
|
newWidth = 1;
|
||
|
}
|
||
|
if ( !newHeight ) {
|
||
|
newHeight = 1;
|
||
|
}
|
||
|
out = (byte *)R_StaticAlloc( newWidth * newHeight * 4, TAG_IMAGE );
|
||
|
out_p = out;
|
||
|
|
||
|
in_p = in;
|
||
|
|
||
|
width >>= 1;
|
||
|
height >>= 1;
|
||
|
|
||
|
if ( width == 0 || height == 0 ) {
|
||
|
width += height; // get largest
|
||
|
for (i=0 ; i<width ; i++, out_p+=4, in_p+=8 ) {
|
||
|
out_p[0] = idMath::Ftob( 255.0f * idMath::Pow( 0.5f * ( mip_gammaTable[in_p[0]] + mip_gammaTable[in_p[4]] ), 1.0f / 2.2f ) );
|
||
|
out_p[1] = idMath::Ftob( 255.0f * idMath::Pow( 0.5f * ( mip_gammaTable[in_p[1]] + mip_gammaTable[in_p[5]] ), 1.0f / 2.2f ) );
|
||
|
out_p[2] = idMath::Ftob( 255.0f * idMath::Pow( 0.5f * ( mip_gammaTable[in_p[2]] + mip_gammaTable[in_p[6]] ), 1.0f / 2.2f ) );
|
||
|
out_p[3] = idMath::Ftob( 255.0f * idMath::Pow( 0.5f * ( mip_gammaTable[in_p[3]] + mip_gammaTable[in_p[7]] ), 1.0f / 2.2f ) );
|
||
|
}
|
||
|
return out;
|
||
|
}
|
||
|
for (i=0 ; i<height ; i++, in_p+=row) {
|
||
|
for (j=0 ; j<width ; j++, out_p+=4, in_p+=8) {
|
||
|
out_p[0] = idMath::Ftob( 255.0f * idMath::Pow( 0.25f * ( mip_gammaTable[in_p[0]] + mip_gammaTable[in_p[4]] + mip_gammaTable[in_p[row+0]] + mip_gammaTable[in_p[row+4]] ), 1.0f / 2.2f ) );
|
||
|
out_p[1] = idMath::Ftob( 255.0f * idMath::Pow( 0.25f * ( mip_gammaTable[in_p[1]] + mip_gammaTable[in_p[5]] + mip_gammaTable[in_p[row+1]] + mip_gammaTable[in_p[row+5]] ), 1.0f / 2.2f ) );
|
||
|
out_p[2] = idMath::Ftob( 255.0f * idMath::Pow( 0.25f * ( mip_gammaTable[in_p[2]] + mip_gammaTable[in_p[6]] + mip_gammaTable[in_p[row+2]] + mip_gammaTable[in_p[row+6]] ), 1.0f / 2.2f ) );
|
||
|
out_p[3] = idMath::Ftob( 255.0f * idMath::Pow( 0.25f * ( mip_gammaTable[in_p[3]] + mip_gammaTable[in_p[7]] + mip_gammaTable[in_p[row+3]] + mip_gammaTable[in_p[row+7]] ), 1.0f / 2.2f ) );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return out;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
R_MipMap
|
||
|
|
||
|
Returns a new copy of the texture, quartered in size and filtered.
|
||
|
================
|
||
|
*/
|
||
|
byte * R_MipMap( const byte *in, int width, int height ) {
|
||
|
int i, j;
|
||
|
const byte *in_p;
|
||
|
byte *out, *out_p;
|
||
|
int row;
|
||
|
int newWidth, newHeight;
|
||
|
|
||
|
if ( width < 1 || height < 1 || ( width + height == 2 ) ) {
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
row = width * 4;
|
||
|
|
||
|
newWidth = width >> 1;
|
||
|
newHeight = height >> 1;
|
||
|
if ( !newWidth ) {
|
||
|
newWidth = 1;
|
||
|
}
|
||
|
if ( !newHeight ) {
|
||
|
newHeight = 1;
|
||
|
}
|
||
|
out = (byte *)R_StaticAlloc( newWidth * newHeight * 4, TAG_IMAGE );
|
||
|
out_p = out;
|
||
|
|
||
|
in_p = in;
|
||
|
|
||
|
width >>= 1;
|
||
|
height >>= 1;
|
||
|
|
||
|
if ( width == 0 || height == 0 ) {
|
||
|
width += height; // get largest
|
||
|
for (i=0 ; i<width ; i++, out_p+=4, in_p+=8 ) {
|
||
|
out_p[0] = ( in_p[0] + in_p[4] )>>1;
|
||
|
out_p[1] = ( in_p[1] + in_p[5] )>>1;
|
||
|
out_p[2] = ( in_p[2] + in_p[6] )>>1;
|
||
|
out_p[3] = ( in_p[3] + in_p[7] )>>1;
|
||
|
}
|
||
|
return out;
|
||
|
}
|
||
|
|
||
|
for (i=0 ; i<height ; i++, in_p+=row) {
|
||
|
for (j=0 ; j<width ; j++, out_p+=4, in_p+=8) {
|
||
|
out_p[0] = (in_p[0] + in_p[4] + in_p[row+0] + in_p[row+4])>>2;
|
||
|
out_p[1] = (in_p[1] + in_p[5] + in_p[row+1] + in_p[row+5])>>2;
|
||
|
out_p[2] = (in_p[2] + in_p[6] + in_p[row+2] + in_p[row+6])>>2;
|
||
|
out_p[3] = (in_p[3] + in_p[7] + in_p[row+3] + in_p[row+7])>>2;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return out;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
==================
|
||
|
R_BlendOverTexture
|
||
|
|
||
|
Apply a color blend over a set of pixels
|
||
|
==================
|
||
|
*/
|
||
|
void R_BlendOverTexture( byte *data, int pixelCount, const byte blend[4] ) {
|
||
|
int i;
|
||
|
int inverseAlpha;
|
||
|
int premult[3];
|
||
|
|
||
|
inverseAlpha = 255 - blend[3];
|
||
|
premult[0] = blend[0] * blend[3];
|
||
|
premult[1] = blend[1] * blend[3];
|
||
|
premult[2] = blend[2] * blend[3];
|
||
|
|
||
|
for ( i = 0 ; i < pixelCount ; i++, data+=4 ) {
|
||
|
data[0] = ( data[0] * inverseAlpha + premult[0] ) >> 9;
|
||
|
data[1] = ( data[1] * inverseAlpha + premult[1] ) >> 9;
|
||
|
data[2] = ( data[2] * inverseAlpha + premult[2] ) >> 9;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
==================
|
||
|
R_HorizontalFlip
|
||
|
|
||
|
Flip the image in place
|
||
|
==================
|
||
|
*/
|
||
|
void R_HorizontalFlip( byte *data, int width, int height ) {
|
||
|
int i, j;
|
||
|
int temp;
|
||
|
|
||
|
for ( i = 0 ; i < height ; i++ ) {
|
||
|
for ( j = 0 ; j < width / 2 ; j++ ) {
|
||
|
temp = *( (int *)data + i * width + j );
|
||
|
*( (int *)data + i * width + j ) = *( (int *)data + i * width + width - 1 - j );
|
||
|
*( (int *)data + i * width + width - 1 - j ) = temp;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void R_VerticalFlip( byte *data, int width, int height ) {
|
||
|
int i, j;
|
||
|
int temp;
|
||
|
|
||
|
for ( i = 0 ; i < width ; i++ ) {
|
||
|
for ( j = 0 ; j < height / 2 ; j++ ) {
|
||
|
temp = *( (int *)data + j * width + i );
|
||
|
*( (int *)data + j * width + i ) = *( (int *)data + ( height - 1 - j ) * width + i );
|
||
|
*( (int *)data + ( height - 1 - j ) * width + i ) = temp;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void R_RotatePic( byte *data, int width ) {
|
||
|
int i, j;
|
||
|
int *temp;
|
||
|
|
||
|
temp = (int *)R_StaticAlloc( width * width * 4, TAG_IMAGE );
|
||
|
|
||
|
for ( i = 0 ; i < width ; i++ ) {
|
||
|
for ( j = 0 ; j < width ; j++ ) {
|
||
|
*( temp + i * width + j ) = *( (int *)data + j * width + i );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
memcpy( data, temp, width * width * 4 );
|
||
|
|
||
|
R_StaticFree( temp );
|
||
|
}
|
||
|
|