2 ===========================================================================
5 Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
7 This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
9 Doom 3 Source Code is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
14 Doom 3 Source Code is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
22 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.
24 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.
26 ===========================================================================
28 #include "../idlib/precompiled.h"
37 Used to resample images in a more general than quartering fashion.
39 This will only have filter coverage if the resampled size
40 is greater than half the original size.
42 If a larger shrinking is needed, use the mipmap function
43 after resampling to the next lower power of two.
46 #define MAX_DIMENSION 4096
47 byte *R_ResampleTexture( const byte *in, int inwidth, int inheight,
48 int outwidth, int outheight ) {
50 const byte *inrow, *inrow2;
51 unsigned int frac, fracstep;
52 unsigned int p1[MAX_DIMENSION], p2[MAX_DIMENSION];
53 const byte *pix1, *pix2, *pix3, *pix4;
56 if ( outwidth > MAX_DIMENSION ) {
57 outwidth = MAX_DIMENSION;
59 if ( outheight > MAX_DIMENSION ) {
60 outheight = MAX_DIMENSION;
63 out = (byte *)R_StaticAlloc( outwidth * outheight * 4 );
66 fracstep = inwidth*0x10000/outwidth;
69 for ( i=0 ; i<outwidth ; i++ ) {
73 frac = 3*(fracstep>>2);
74 for ( i=0 ; i<outwidth ; i++ ) {
79 for (i=0 ; i<outheight ; i++, out_p += outwidth*4 ) {
80 inrow = in + 4 * inwidth * (int)( ( i + 0.25f ) * inheight / outheight );
81 inrow2 = in + 4 * inwidth * (int)( ( i + 0.75f ) * inheight / outheight );
83 for (j=0 ; j<outwidth ; j++) {
86 pix3 = inrow2 + p1[j];
87 pix4 = inrow2 + p2[j];
88 out_p[j*4+0] = (pix1[0] + pix2[0] + pix3[0] + pix4[0])>>2;
89 out_p[j*4+1] = (pix1[1] + pix2[1] + pix3[1] + pix4[1])>>2;
90 out_p[j*4+2] = (pix1[2] + pix2[2] + pix3[2] + pix4[2])>>2;
91 out_p[j*4+3] = (pix1[3] + pix2[3] + pix3[3] + pix4[3])>>2;
102 Used to resample images in a more general than quartering fashion.
103 Normal maps and such should not be bilerped.
106 byte *R_Dropsample( const byte *in, int inwidth, int inheight,
107 int outwidth, int outheight ) {
113 out = (byte *)R_StaticAlloc( outwidth * outheight * 4 );
116 for (i=0 ; i<outheight ; i++, out_p += outwidth*4 ) {
117 inrow = in + 4*inwidth*(int)((i+0.25)*inheight/outheight);
118 for (j=0 ; j<outwidth ; j++) {
119 k = j * inwidth / outwidth;
120 pix1 = inrow + k * 4;
121 out_p[j*4+0] = pix1[0];
122 out_p[j*4+1] = pix1[1];
123 out_p[j*4+2] = pix1[2];
124 out_p[j*4+3] = pix1[3];
138 void R_SetBorderTexels( byte *inBase, int width, int height, const byte border[4] ) {
143 for (i=0 ; i<height ; i++, out+=width*4) {
149 out = inBase+(width-1)*4;
150 for (i=0 ; i<height ; i++, out+=width*4) {
157 for (i=0 ; i<width ; i++, out+=4) {
163 out = inBase+width*4*(height-1);
164 for (i=0 ; i<width ; i++, out+=4) {
178 void R_SetBorderTexels3D( byte *inBase, int width, int height, int depth, const byte border[4] ) {
186 for ( j = 1 ; j < depth - 1 ; j++ ) {
187 out = inBase + j * plane;
188 for (i=0 ; i<height ; i++, out+=row) {
194 out = inBase+(width-1)*4 + j * plane;
195 for (i=0 ; i<height ; i++, out+=row) {
201 out = inBase + j * plane;
202 for (i=0 ; i<width ; i++, out+=4) {
208 out = inBase+width*4*(height-1) + j * plane;
209 for (i=0 ; i<width ; i++, out+=4) {
218 for ( i = 0 ; i < plane ; i += 4, out += 4 ) {
224 out = inBase+(depth-1)*plane;
225 for ( i = 0 ; i < plane ; i += 4, out += 4 ) {
235 R_SetAlphaNormalDivergence
237 If any of the angles inside the cone would directly reflect to the light, there will be
238 a specular highlight. The intensity of the highlight is inversely proportional to the
241 Light source area is important for the base size.
243 area subtended in light is the divergence times the distance
245 Shininess value is subtracted from the divergence
247 Sets the alpha channel to the greatest divergence dot product of the surrounding texels.
248 1.0 = flat, 0.0 = turns a 90 degree angle
249 Lower values give less shiny specular
250 With mip maps, the lowest samnpled value will be retained
252 Should we rewrite the normal as the centered average?
255 void R_SetAlphaNormalDivergence( byte *in, int width, int height ) {
256 for ( int y = 0 ; y < height ; y++ ) {
257 for ( int x = 0 ; x < width ; x++ ) {
258 // the divergence is the smallest dot product of any of the eight surrounding texels
259 byte *pic_p = in + ( y * width + x ) * 4;
261 center[0] = ( pic_p[0] - 128 ) / 127;
262 center[1] = ( pic_p[1] - 128 ) / 127;
263 center[2] = ( pic_p[2] - 128 ) / 127;
266 float maxDiverge = 1.0;
268 // FIXME: this assumes wrap mode, but should handle clamp modes and border colors
269 for ( int yy = -1 ; yy <= 1 ; yy++ ) {
270 for ( int xx = -1 ; xx <= 1 ; xx++ ) {
271 if ( yy == 0 && xx == 0 ) {
274 byte *corner_p = in + ( ((y+yy)&(height-1)) * width + ((x+xx)&width-1) ) * 4;
276 corner[0] = ( corner_p[0] - 128 ) / 127;
277 corner[1] = ( corner_p[1] - 128 ) / 127;
278 corner[2] = ( corner_p[2] - 128 ) / 127;
281 float diverge = corner * center;
282 if ( diverge < maxDiverge ) {
283 maxDiverge = diverge;
288 // we can get a diverge < 0 in some extreme cases
289 if ( maxDiverge < 0 ) {
292 pic_p[3] = maxDiverge * 255;
299 R_MipMapWithAlphaSpecularity
301 Returns a new copy of the texture, quartered in size and filtered.
302 The alpha channel is taken to be the minimum of the dots of all surrounding normals.
305 #define MIP_MIN(a,b) (a<b?a:b)
307 byte *R_MipMapWithAlphaSpecularity( const byte *in, int width, int height ) {
308 int i, j, c, x, y, sx, sy;
312 int newWidth, newHeight;
313 float *fbuf, *fbuf_p;
315 if ( width < 1 || height < 1 || ( width + height == 2 ) ) {
316 common->FatalError( "R_MipMapWithAlphaMin called with size %i,%i", width, height );
319 // convert the incoming texture to centered floating point
321 fbuf = (float *)_alloca( c * 4 * sizeof( *fbuf ) );
324 for ( i = 0 ; i < c ; i++, in_p+=4, fbuf_p += 4 ) {
325 fbuf_p[0] = ( in_p[0] / 255.0 ) * 2.0 - 1.0; // convert to a normal
326 fbuf_p[1] = ( in_p[1] / 255.0 ) * 2.0 - 1.0;
327 fbuf_p[2] = ( in_p[2] / 255.0 ) * 2.0 - 1.0;
328 fbuf_p[3] = ( in_p[3] / 255.0 ); // filtered divegence / specularity
333 newWidth = width >> 1;
334 newHeight = height >> 1;
341 out = (byte *)R_StaticAlloc( newWidth * newHeight * 4 );
346 for ( i=0 ; i<newHeight ; i++ ) {
347 for ( j=0 ; j<newWidth ; j++, out_p+=4 ) {
353 // find the average normal
354 for ( x = -1 ; x <= 1 ; x++ ) {
355 sx = ( j * 2 + x ) & (width-1);
356 for ( y = -1 ; y <= 1 ; y++ ) {
357 sy = ( i * 2 + y ) & (height-1);
358 fbuf_p = fbuf + ( sy * width + sx ) * 4;
360 total[0] += fbuf_p[0];
361 total[1] += fbuf_p[1];
362 total[2] += fbuf_p[2];
364 totalSpec += fbuf_p[3];
370 // find the maximum divergence
371 for ( x = -1 ; x <= 1 ; x++ ) {
372 for ( y = -1 ; y <= 1 ; y++ ) {
376 // store the average normal and divergence
387 Returns a new copy of the texture, quartered in size and filtered.
389 If a texture is intended to be used in GL_CLAMP or GL_CLAMP_TO_EDGE mode with
390 a completely transparent border, we must prevent any blurring into the outer
391 ring of texels by filling it with the border from the previous level. This
392 will result in a slight shrinking of the texture as it mips, but better than
396 byte *R_MipMap( const byte *in, int width, int height, bool preserveBorder ) {
402 int newWidth, newHeight;
404 if ( width < 1 || height < 1 || ( width + height == 2 ) ) {
405 common->FatalError( "R_MipMap called with size %i,%i", width, height );
415 newWidth = width >> 1;
416 newHeight = height >> 1;
423 out = (byte *)R_StaticAlloc( newWidth * newHeight * 4 );
431 if ( width == 0 || height == 0 ) {
432 width += height; // get largest
433 if ( preserveBorder ) {
434 for (i=0 ; i<width ; i++, out_p+=4 ) {
435 out_p[0] = border[0];
436 out_p[1] = border[1];
437 out_p[2] = border[2];
438 out_p[3] = border[3];
441 for (i=0 ; i<width ; i++, out_p+=4, in_p+=8 ) {
442 out_p[0] = ( in_p[0] + in_p[4] )>>1;
443 out_p[1] = ( in_p[1] + in_p[5] )>>1;
444 out_p[2] = ( in_p[2] + in_p[6] )>>1;
445 out_p[3] = ( in_p[3] + in_p[7] )>>1;
451 for (i=0 ; i<height ; i++, in_p+=row) {
452 for (j=0 ; j<width ; j++, out_p+=4, in_p+=8) {
453 out_p[0] = (in_p[0] + in_p[4] + in_p[row+0] + in_p[row+4])>>2;
454 out_p[1] = (in_p[1] + in_p[5] + in_p[row+1] + in_p[row+5])>>2;
455 out_p[2] = (in_p[2] + in_p[6] + in_p[row+2] + in_p[row+6])>>2;
456 out_p[3] = (in_p[3] + in_p[7] + in_p[row+3] + in_p[row+7])>>2;
460 // copy the old border texel back around if desired
461 if ( preserveBorder ) {
462 R_SetBorderTexels( out, width, height, border );
472 Returns a new copy of the texture, eigthed in size and filtered.
474 If a texture is intended to be used in GL_CLAMP or GL_CLAMP_TO_EDGE mode with
475 a completely transparent border, we must prevent any blurring into the outer
476 ring of texels by filling it with the border from the previous level. This
477 will result in a slight shrinking of the texture as it mips, but better than
481 byte *R_MipMap3D( const byte *in, int width, int height, int depth, bool preserveBorder ) {
487 int newWidth, newHeight, newDepth;
490 return R_MipMap( in, width, height, preserveBorder );
493 // assume symetric for now
494 if ( width < 2 || height < 2 || depth < 2 ) {
495 common->FatalError( "R_MipMap3D called with size %i,%i,%i", width, height, depth );
504 plane = row * height;
506 newWidth = width >> 1;
507 newHeight = height >> 1;
508 newDepth = depth >> 1;
510 out = (byte *)R_StaticAlloc( newWidth * newHeight * newDepth * 4 );
519 for (k=0 ; k<depth ; k++, in_p+=plane) {
520 for (i=0 ; i<height ; i++, in_p+=row) {
521 for (j=0 ; j<width ; j++, out_p+=4, in_p+=8) {
522 out_p[0] = (in_p[0] + in_p[4] + in_p[row+0] + in_p[row+4] +
523 in_p[plane+0] + in_p[plane+4] + in_p[plane+row+0] + in_p[plane+row+4]
525 out_p[1] = (in_p[1] + in_p[5] + in_p[row+1] + in_p[row+5] +
526 in_p[plane+1] + in_p[plane+5] + in_p[plane+row+1] + in_p[plane+row+5]
528 out_p[2] = (in_p[2] + in_p[6] + in_p[row+2] + in_p[row+6] +
529 in_p[plane+2] + in_p[plane+6] + in_p[plane+row+2] + in_p[plane+row+6]
531 out_p[3] = (in_p[3] + in_p[7] + in_p[row+3] + in_p[row+7] +
532 in_p[plane+3] + in_p[plane+6] + in_p[plane+row+3] + in_p[plane+row+6]
538 // copy the old border texel back around if desired
539 if ( preserveBorder ) {
540 R_SetBorderTexels3D( out, width, height, depth, border );
551 Apply a color blend over a set of pixels
554 void R_BlendOverTexture( byte *data, int pixelCount, const byte blend[4] ) {
559 inverseAlpha = 255 - blend[3];
560 premult[0] = blend[0] * blend[3];
561 premult[1] = blend[1] * blend[3];
562 premult[2] = blend[2] * blend[3];
564 for ( i = 0 ; i < pixelCount ; i++, data+=4 ) {
565 data[0] = ( data[0] * inverseAlpha + premult[0] ) >> 9;
566 data[1] = ( data[1] * inverseAlpha + premult[1] ) >> 9;
567 data[2] = ( data[2] * inverseAlpha + premult[2] ) >> 9;
576 Flip the image in place
579 void R_HorizontalFlip( byte *data, int width, int height ) {
583 for ( i = 0 ; i < height ; i++ ) {
584 for ( j = 0 ; j < width / 2 ; j++ ) {
585 temp = *( (int *)data + i * width + j );
586 *( (int *)data + i * width + j ) = *( (int *)data + i * width + width - 1 - j );
587 *( (int *)data + i * width + width - 1 - j ) = temp;
592 void R_VerticalFlip( byte *data, int width, int height ) {
596 for ( i = 0 ; i < width ; i++ ) {
597 for ( j = 0 ; j < height / 2 ; j++ ) {
598 temp = *( (int *)data + j * width + i );
599 *( (int *)data + j * width + i ) = *( (int *)data + ( height - 1 - j ) * width + i );
600 *( (int *)data + ( height - 1 - j ) * width + i ) = temp;
605 void R_RotatePic( byte *data, int width ) {
609 temp = (int *)R_StaticAlloc( width * width * 4 );
611 for ( i = 0 ; i < width ; i++ ) {
612 for ( j = 0 ; j < width ; j++ ) {
613 *( temp + i * width + j ) = *( (int *)data + j * width + i );
617 memcpy( data, temp, width * width * 4 );
619 R_StaticFree( temp );