/* =========================================================================== Doom 3 GPL Source Code Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code"). Doom 3 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 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 Source Code. If not, see . In addition, the Doom 3 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 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. =========================================================================== */ #include "sys/platform.h" #include "renderer/tr_local.h" #include "renderer/Image.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 ); out_p = out; fracstep = inwidth*0x10000/outwidth; frac = fracstep>>2; for ( i=0 ; i>16); frac += fracstep; } frac = 3*(fracstep>>2); for ( i=0 ; i>16); frac += fracstep; } for (i=0 ; i> 1; for (j=0 ; j>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 ); out_p = out; for (i=0 ; iFatalError( "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 } newWidth = width >> 1; newHeight = height >> 1; if ( !newWidth ) { newWidth = 1; } if ( !newHeight ) { newHeight = 1; } out = (byte *)R_StaticAlloc( newWidth * newHeight * 4 ); out_p = out; in_p = in; for ( i=0 ; iFatalError( "R_MipMap called with size %i,%i", width, height ); } border[0] = in[0]; border[1] = in[1]; border[2] = in[2]; border[3] = in[3]; row = width * 4; newWidth = width >> 1; newHeight = height >> 1; if ( !newWidth ) { newWidth = 1; } if ( !newHeight ) { newHeight = 1; } out = (byte *)R_StaticAlloc( newWidth * newHeight * 4 ); out_p = out; in_p = in; width >>= 1; height >>= 1; if ( width == 0 || height == 0 ) { width += height; // get largest if ( preserveBorder ) { for (i=0 ; i>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>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; } } // copy the old border texel back around if desired if ( preserveBorder ) { R_SetBorderTexels( out, width, height, border ); } return out; } /* ================ R_MipMap3D Returns a new copy of the texture, eigthed in size and filtered. If a texture is intended to be used in GL_CLAMP or GL_CLAMP_TO_EDGE mode with a completely transparent border, we must prevent any blurring into the outer ring of texels by filling it with the border from the previous level. This will result in a slight shrinking of the texture as it mips, but better than smeared clamps... ================ */ byte *R_MipMap3D( const byte *in, int width, int height, int depth, bool preserveBorder ) { int i, j, k; const byte *in_p; byte *out, *out_p; int row, plane; byte border[4]; int newWidth, newHeight, newDepth; if ( depth == 1 ) { return R_MipMap( in, width, height, preserveBorder ); } // assume symetric for now if ( width < 2 || height < 2 || depth < 2 ) { common->FatalError( "R_MipMap3D called with size %i,%i,%i", width, height, depth ); } border[0] = in[0]; border[1] = in[1]; border[2] = in[2]; border[3] = in[3]; row = width * 4; plane = row * height; newWidth = width >> 1; newHeight = height >> 1; newDepth = depth >> 1; out = (byte *)R_StaticAlloc( newWidth * newHeight * newDepth * 4 ); out_p = out; in_p = in; width >>= 1; height >>= 1; depth >>= 1; for (k=0 ; k>3; out_p[1] = (in_p[1] + in_p[5] + in_p[row+1] + in_p[row+5] + in_p[plane+1] + in_p[plane+5] + in_p[plane+row+1] + in_p[plane+row+5] )>>3; out_p[2] = (in_p[2] + in_p[6] + in_p[row+2] + in_p[row+6] + in_p[plane+2] + in_p[plane+6] + in_p[plane+row+2] + in_p[plane+row+6] )>>3; out_p[3] = (in_p[3] + in_p[7] + in_p[row+3] + in_p[row+7] + in_p[plane+3] + in_p[plane+6] + in_p[plane+row+3] + in_p[plane+row+6] )>>3; } } } // copy the old border texel back around if desired if ( preserveBorder ) { R_SetBorderTexels3D( out, width, height, depth, border ); } 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 ); 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 ); }