/*
===========================================================================
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 "../idlib/precompiled.h"
#pragma hdrstop
#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 );
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
}
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;
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 );
}