]> icculus.org git repositories - icculus/iodoom3.git/blob - neo/renderer/Image_process.cpp
hello world
[icculus/iodoom3.git] / neo / renderer / Image_process.cpp
1 /*
2 ===========================================================================
3
4 Doom 3 GPL Source Code
5 Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. 
6
7 This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).  
8
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.
13
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.
18
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/>.
21
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.
23
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.
25
26 ===========================================================================
27 */
28 #include "../idlib/precompiled.h"
29 #pragma hdrstop
30
31 #include "tr_local.h"
32
33 /*
34 ================
35 R_ResampleTexture
36
37 Used to resample images in a more general than quartering fashion.
38
39 This will only have filter coverage if the resampled size
40 is greater than half the original size.
41
42 If a larger shrinking is needed, use the mipmap function 
43 after resampling to the next lower power of two.
44 ================
45 */
46 #define MAX_DIMENSION   4096
47 byte *R_ResampleTexture( const byte *in, int inwidth, int inheight,  
48                                                         int outwidth, int outheight ) {
49         int             i, j;
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;
54         byte            *out, *out_p;
55
56         if ( outwidth > MAX_DIMENSION ) {
57                 outwidth = MAX_DIMENSION;
58         }
59         if ( outheight > MAX_DIMENSION ) {
60                 outheight = MAX_DIMENSION;
61         }
62
63         out = (byte *)R_StaticAlloc( outwidth * outheight * 4 );
64         out_p = out;
65
66         fracstep = inwidth*0x10000/outwidth;
67
68         frac = fracstep>>2;
69         for ( i=0 ; i<outwidth ; i++ ) {
70                 p1[i] = 4*(frac>>16);
71                 frac += fracstep;
72         }
73         frac = 3*(fracstep>>2);
74         for ( i=0 ; i<outwidth ; i++ ) {
75                 p2[i] = 4*(frac>>16);
76                 frac += fracstep;
77         }
78
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 );
82                 frac = fracstep >> 1;
83                 for (j=0 ; j<outwidth ; j++) {
84                         pix1 = inrow + p1[j];
85                         pix2 = inrow + p2[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;
92                 }
93         }
94
95         return out;
96 }
97
98 /*
99 ================
100 R_Dropsample
101
102 Used to resample images in a more general than quartering fashion.
103 Normal maps and such should not be bilerped.
104 ================
105 */
106 byte *R_Dropsample( const byte *in, int inwidth, int inheight,  
107                                                         int outwidth, int outheight ) {
108         int             i, j, k;
109         const byte      *inrow;
110         const byte      *pix1;
111         byte            *out, *out_p;
112
113         out = (byte *)R_StaticAlloc( outwidth * outheight * 4 );
114         out_p = out;
115
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];
125                 }
126         }
127
128         return out;
129 }
130
131
132 /*
133 ===============
134 R_SetBorderTexels
135
136 ===============
137 */
138 void R_SetBorderTexels( byte *inBase, int width, int height, const byte border[4] ) {
139         int             i;
140         byte    *out;
141
142         out = inBase;
143         for (i=0 ; i<height ; i++, out+=width*4) {
144                 out[0] = border[0];
145                 out[1] = border[1];
146                 out[2] = border[2];
147                 out[3] = border[3];
148         }
149         out = inBase+(width-1)*4;
150         for (i=0 ; i<height ; i++, out+=width*4) {
151                 out[0] = border[0];
152                 out[1] = border[1];
153                 out[2] = border[2];
154                 out[3] = border[3];
155         }
156         out = inBase;
157         for (i=0 ; i<width ; i++, out+=4) {
158                 out[0] = border[0];
159                 out[1] = border[1];
160                 out[2] = border[2];
161                 out[3] = border[3];
162         }
163         out = inBase+width*4*(height-1);
164         for (i=0 ; i<width ; i++, out+=4) {
165                 out[0] = border[0];
166                 out[1] = border[1];
167                 out[2] = border[2];
168                 out[3] = border[3];
169         }
170 }
171
172 /*
173 ===============
174 R_SetBorderTexels3D
175
176 ===============
177 */
178 void R_SetBorderTexels3D( byte *inBase, int width, int height, int depth, const byte border[4] ) {
179         int             i, j;
180         byte    *out;
181         int             row, plane;
182
183         row = width * 4;
184         plane = row * depth;
185
186         for ( j = 1 ; j < depth - 1 ; j++ ) {
187                 out = inBase + j * plane;
188                 for (i=0 ; i<height ; i++, out+=row) {
189                         out[0] = border[0];
190                         out[1] = border[1];
191                         out[2] = border[2];
192                         out[3] = border[3];
193                 }
194                 out = inBase+(width-1)*4 + j * plane;
195                 for (i=0 ; i<height ; i++, out+=row) {
196                         out[0] = border[0];
197                         out[1] = border[1];
198                         out[2] = border[2];
199                         out[3] = border[3];
200                 }
201                 out = inBase + j * plane;
202                 for (i=0 ; i<width ; i++, out+=4) {
203                         out[0] = border[0];
204                         out[1] = border[1];
205                         out[2] = border[2];
206                         out[3] = border[3];
207                 }
208                 out = inBase+width*4*(height-1) + j * plane;
209                 for (i=0 ; i<width ; i++, out+=4) {
210                         out[0] = border[0];
211                         out[1] = border[1];
212                         out[2] = border[2];
213                         out[3] = border[3];
214                 }
215         }
216
217         out = inBase;
218         for ( i = 0 ; i < plane ; i += 4, out += 4 ) {
219                 out[0] = border[0];
220                 out[1] = border[1];
221                 out[2] = border[2];
222                 out[3] = border[3];
223         }
224         out = inBase+(depth-1)*plane;
225         for ( i = 0 ; i < plane ; i += 4, out += 4 ) {
226                 out[0] = border[0];
227                 out[1] = border[1];
228                 out[2] = border[2];
229                 out[3] = border[3];
230         }
231 }
232
233 /*
234 ================
235 R_SetAlphaNormalDivergence
236
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
239 area of the spread.
240
241 Light source area is important for the base size.
242
243 area subtended in light is the divergence times the distance
244
245 Shininess value is subtracted from the divergence
246
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
251
252 Should we rewrite the normal as the centered average?
253 ================
254 */
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;
260                         idVec3  center;
261                         center[0] = ( pic_p[0] - 128 ) / 127;
262                         center[1] = ( pic_p[1] - 128 ) / 127;
263                         center[2] = ( pic_p[2] - 128 ) / 127;
264                         center.Normalize();
265
266                         float   maxDiverge = 1.0;
267
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 ) {
272                                                 continue;
273                                         }
274                                         byte    *corner_p = in + ( ((y+yy)&(height-1)) * width + ((x+xx)&width-1) ) * 4;
275                                         idVec3  corner;
276                                         corner[0] = ( corner_p[0] - 128 ) / 127;
277                                         corner[1] = ( corner_p[1] - 128 ) / 127;
278                                         corner[2] = ( corner_p[2] - 128 ) / 127;
279                                         corner.Normalize();
280
281                                         float   diverge = corner * center;
282                                         if ( diverge < maxDiverge ) {
283                                                 maxDiverge = diverge;
284                                         }
285                                 }
286                         }
287
288                         // we can get a diverge < 0 in some extreme cases
289                         if ( maxDiverge < 0 ) {
290                                 maxDiverge = 0;
291                         }
292                         pic_p[3] = maxDiverge * 255;
293                 }
294         }
295 }
296
297 /*
298 ================
299 R_MipMapWithAlphaSpecularity
300
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.
303 ================
304 */
305 #define MIP_MIN(a,b) (a<b?a:b)
306
307 byte *R_MipMapWithAlphaSpecularity( const byte *in, int width, int height ) {
308         int             i, j, c, x, y, sx, sy;
309         const byte      *in_p;
310         byte    *out, *out_p;
311         int             row;
312         int             newWidth, newHeight;
313         float   *fbuf, *fbuf_p;
314
315         if ( width < 1 || height < 1 || ( width + height == 2 ) ) {
316                 common->FatalError( "R_MipMapWithAlphaMin called with size %i,%i", width, height );
317         }
318
319         // convert the incoming texture to centered floating point
320         c = width * height;
321         fbuf = (float *)_alloca( c * 4 * sizeof( *fbuf ) );
322         in_p = in;
323         fbuf_p = 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
329         }
330
331         row = width * 4;
332
333         newWidth = width >> 1;
334         newHeight = height >> 1;
335         if ( !newWidth ) {
336                 newWidth = 1;
337         }
338         if ( !newHeight ) {
339                 newHeight = 1;
340         }
341         out = (byte *)R_StaticAlloc( newWidth * newHeight * 4 );
342         out_p = out;
343
344         in_p = in;
345
346         for ( i=0 ; i<newHeight ; i++ ) {
347                 for ( j=0 ; j<newWidth ; j++, out_p+=4 ) {
348                         idVec3  total;
349                         float   totalSpec;
350
351                         total.Zero();
352                         totalSpec = 0;
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;
359
360                                         total[0] += fbuf_p[0];
361                                         total[1] += fbuf_p[1];
362                                         total[2] += fbuf_p[2];
363
364                                         totalSpec += fbuf_p[3];
365                                 }
366                         }
367                         total.Normalize();
368                         totalSpec /= 9.0;
369
370                         // find the maximum divergence
371                         for ( x = -1 ; x <= 1 ; x++ ) {
372                                 for ( y = -1 ; y <= 1 ; y++ ) {
373                                 }
374                         }
375
376                         // store the average normal and divergence
377                 }
378         }
379
380         return out;
381 }
382
383 /*
384 ================
385 R_MipMap
386
387 Returns a new copy of the texture, quartered in size and filtered.
388
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
393 smeared clamps...
394 ================
395 */
396 byte *R_MipMap( const byte *in, int width, int height, bool preserveBorder ) {
397         int             i, j;
398         const byte      *in_p;
399         byte    *out, *out_p;
400         int             row;
401         byte    border[4];
402         int             newWidth, newHeight;
403
404         if ( width < 1 || height < 1 || ( width + height == 2 ) ) {
405                 common->FatalError( "R_MipMap called with size %i,%i", width, height );
406         }
407
408         border[0] = in[0];
409         border[1] = in[1];
410         border[2] = in[2];
411         border[3] = in[3];
412
413         row = width * 4;
414
415         newWidth = width >> 1;
416         newHeight = height >> 1;
417         if ( !newWidth ) {
418                 newWidth = 1;
419         }
420         if ( !newHeight ) {
421                 newHeight = 1;
422         }
423         out = (byte *)R_StaticAlloc( newWidth * newHeight * 4 );
424         out_p = out;
425
426         in_p = in;
427
428         width >>= 1;
429         height >>= 1;
430
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];
439                         }
440                 } else {
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;
446                         }
447                 }
448                 return out;
449         }
450
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;
457                 }
458         }
459
460         // copy the old border texel back around if desired
461         if ( preserveBorder ) {
462                 R_SetBorderTexels( out, width, height, border );
463         }
464
465         return out;
466 }
467
468 /*
469 ================
470 R_MipMap3D
471
472 Returns a new copy of the texture, eigthed in size and filtered.
473
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
478 smeared clamps...
479 ================
480 */
481 byte *R_MipMap3D( const byte *in, int width, int height, int depth, bool preserveBorder ) {
482         int             i, j, k;
483         const byte      *in_p;
484         byte    *out, *out_p;
485         int             row, plane;
486         byte    border[4];
487         int             newWidth, newHeight, newDepth;
488
489         if ( depth == 1 ) {
490                 return R_MipMap( in, width, height, preserveBorder );
491         }
492
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 );
496         }
497
498         border[0] = in[0];
499         border[1] = in[1];
500         border[2] = in[2];
501         border[3] = in[3];
502
503         row = width * 4;
504         plane = row * height;
505
506         newWidth = width >> 1;
507         newHeight = height >> 1;
508         newDepth = depth >> 1;
509
510         out = (byte *)R_StaticAlloc( newWidth * newHeight * newDepth * 4 );
511         out_p = out;
512
513         in_p = in;
514
515         width >>= 1;
516         height >>= 1;
517         depth >>= 1;
518
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]
524                                         )>>3;
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]
527                                         )>>3;
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]
530                                         )>>3;
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]
533                                         )>>3;
534                         }
535                 }
536         }
537
538         // copy the old border texel back around if desired
539         if ( preserveBorder ) {
540                 R_SetBorderTexels3D( out, width, height, depth, border );
541         }
542
543         return out;
544 }
545
546
547 /*
548 ==================
549 R_BlendOverTexture
550
551 Apply a color blend over a set of pixels
552 ==================
553 */
554 void R_BlendOverTexture( byte *data, int pixelCount, const byte blend[4] ) {
555         int             i;
556         int             inverseAlpha;
557         int             premult[3];
558
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];
563
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;
568         }
569 }
570
571
572 /*
573 ==================
574 R_HorizontalFlip
575
576 Flip the image in place
577 ==================
578 */
579 void R_HorizontalFlip( byte *data, int width, int height ) {
580         int             i, j;
581         int             temp;
582
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;
588                 }
589         }
590 }
591
592 void R_VerticalFlip( byte *data, int width, int height ) {
593         int             i, j;
594         int             temp;
595
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;
601                 }
602         }
603 }
604
605 void R_RotatePic( byte *data, int width ) {
606         int             i, j;
607         int             *temp;
608
609         temp = (int *)R_StaticAlloc( width * width * 4 );
610
611         for ( i = 0 ; i < width ; i++ ) {
612                 for ( j = 0 ; j < width ; j++ ) {
613                         *( temp + i * width + j ) = *( (int *)data + j * width + i );
614                 }
615         }
616
617         memcpy( data, temp, width * width * 4 );
618
619         R_StaticFree( temp );
620 }
621