doom3-gpl
Doom 3 GPL source release
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
Image_process.cpp
Go to the documentation of this file.
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 */
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 
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 
float Normalize(void)
Definition: Vector.h:646
void R_RotatePic(byte *data, int width)
void R_BlendOverTexture(byte *data, int pixelCount, const byte blend[4])
GLenum GLint GLint y
Definition: glext.h:2849
case const int
Definition: Callbacks.cpp:52
void R_SetAlphaNormalDivergence(byte *in, int width, int height)
byte * R_MipMap(const byte *in, int width, int height, bool preserveBorder)
Definition: Vector.h:316
GLint GLint GLsizei GLsizei GLsizei depth
Definition: glext.h:2878
byte * R_MipMap3D(const byte *in, int width, int height, int depth, bool preserveBorder)
GLenum GLint x
Definition: glext.h:2849
int i
Definition: process.py:33
byte * R_ResampleTexture(const byte *in, int inwidth, int inheight, int outwidth, int outheight)
const GLubyte * c
Definition: glext.h:4677
byte * R_MipMapWithAlphaSpecularity(const byte *in, int width, int height)
idCommon * common
Definition: Common.cpp:206
GLsizei GLsizei GLenum GLenum const GLvoid * data
Definition: glext.h:2853
virtual void virtual void FatalError(const char *fmt,...) id_attribute((format(printf
void R_SetBorderTexels3D(byte *inBase, int width, int height, int depth, const byte border[4])
GLenum GLsizei width
Definition: glext.h:2846
void R_VerticalFlip(byte *data, int width, int height)
GLenum GLsizei GLsizei height
Definition: glext.h:2856
GLint GLint GLsizei GLsizei GLsizei GLint border
Definition: glext.h:2878
byte * R_Dropsample(const byte *in, int inwidth, int inheight, int outwidth, int outheight)
GLenum GLenum GLvoid * row
Definition: glext.h:2866
tuple f
Definition: idal.py:89
GLuint in
Definition: glext.h:5388
void R_SetBorderTexels(byte *inBase, int width, int height, const byte border[4])
unsigned char byte
Definition: Lib.h:75
void * R_StaticAlloc(int bytes)
Definition: tr_main.cpp:301
void R_HorizontalFlip(byte *data, int width, int height)
GLint j
Definition: qgl.h:264
#define MAX_DIMENSION
void R_StaticFree(void *data)
Definition: tr_main.cpp:335
void Zero(void)
Definition: Vector.h:415