]> icculus.org git repositories - icculus/iodoom3.git/blob - neo/renderer/Image_load.cpp
Use the same OpenAL headers on all platforms.
[icculus/iodoom3.git] / neo / renderer / Image_load.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
29 #include "../idlib/precompiled.h"
30 #pragma hdrstop
31
32 #include "tr_local.h"
33
34 /*
35 PROBLEM: compressed textures may break the zero clamp rule!
36 */
37
38 static bool FormatIsDXT( int internalFormat ) {
39         if ( internalFormat < GL_COMPRESSED_RGB_S3TC_DXT1_EXT 
40         || internalFormat > GL_COMPRESSED_RGBA_S3TC_DXT5_EXT ) {
41                 return false;
42         }
43         return true;
44 }
45
46 int MakePowerOfTwo( int num ) {
47         int             pot;
48         for (pot = 1 ; pot < num ; pot<<=1) {
49         }
50         return pot;
51 }
52
53 /*
54 ================
55 BitsForInternalFormat
56
57 Used for determining memory utilization
58 ================
59 */
60 int idImage::BitsForInternalFormat( int internalFormat ) const {
61         switch ( internalFormat ) {
62         case GL_INTENSITY8:
63         case 1:
64                 return 8;
65         case 2:
66         case GL_LUMINANCE8_ALPHA8:
67                 return 16;
68         case 3:
69                 return 32;              // on some future hardware, this may actually be 24, but be conservative
70         case 4:
71                 return 32;
72         case GL_LUMINANCE8:
73                 return 8;
74         case GL_ALPHA8:
75                 return 8;
76         case GL_RGBA8:
77                 return 32;
78         case GL_RGB8:
79                 return 32;              // on some future hardware, this may actually be 24, but be conservative
80         case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
81                 return 4;
82         case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
83                 return 4;
84         case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT:
85                 return 8;
86         case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT:
87                 return 8;
88         case GL_RGBA4:
89                 return 16;
90         case GL_RGB5:
91                 return 16;
92         case GL_COLOR_INDEX8_EXT:
93                 return 8;
94         case GL_COLOR_INDEX:
95                 return 8;
96         case GL_COMPRESSED_RGB_ARB:
97                 return 4;                       // not sure
98         case GL_COMPRESSED_RGBA_ARB:
99                 return 8;                       // not sure
100         default:
101                 common->Error( "R_BitsForInternalFormat: BAD FORMAT:%i", internalFormat );
102         }
103         return 0;
104 }
105
106 /*
107 ==================
108 UploadCompressedNormalMap
109
110 Create a 256 color palette to be used by compressed normal maps
111 ==================
112 */
113 void idImage::UploadCompressedNormalMap( int width, int height, const byte *rgba, int mipLevel ) {
114         byte    *normals;
115         const byte      *in;
116         byte    *out;
117         int             i, j;
118         int             x, y, z;
119         int             row;
120
121         // OpenGL's pixel packing rule
122         row = width < 4 ? 4 : width;
123
124         normals = (byte *)_alloca( row * height );
125         if ( !normals ) {
126                 common->Error( "R_UploadCompressedNormalMap: _alloca failed" );
127         }
128
129         in = rgba;
130         out = normals;
131         for ( i = 0 ; i < height ; i++, out += row, in += width * 4 ) {
132                 for ( j = 0 ; j < width ; j++ ) {
133                         x = in[ j * 4 + 0 ];
134                         y = in[ j * 4 + 1 ];
135                         z = in[ j * 4 + 2 ];
136
137                         int c;
138                         if ( x == 128 && y == 128 && z == 128 ) {
139                                 // the "nullnormal" color
140                                 c = 255;
141                         } else {
142                                 c = ( globalImages->originalToCompressed[x] << 4 ) | globalImages->originalToCompressed[y];
143                                 if ( c == 255 ) {
144                                         c = 254;        // don't use the nullnormal color
145                                 }
146                         }
147                         out[j] = c;
148                 }
149         }
150
151         if ( mipLevel == 0 ) {
152                 // Optionally write out the paletized normal map to a .tga
153                 if ( globalImages->image_writeNormalTGAPalletized.GetBool() ) {
154                         char filename[MAX_IMAGE_NAME];
155                         ImageProgramStringToCompressedFileName( imgName, filename );
156                         char *ext = strrchr(filename, '.');
157                         if ( ext ) {
158                                 strcpy(ext, "_pal.tga");
159                                 R_WritePalTGA( filename, normals, globalImages->compressedPalette, width, height);
160                         }
161                 }
162         }
163
164         if ( glConfig.sharedTexturePaletteAvailable ) {
165                 qglTexImage2D( GL_TEXTURE_2D,
166                                         mipLevel,
167                                         GL_COLOR_INDEX8_EXT,
168                                         width,
169                                         height,
170                                         0,
171                                         GL_COLOR_INDEX,
172                                         GL_UNSIGNED_BYTE,
173                                         normals );
174         }
175 }
176
177
178 //=======================================================================
179
180
181 static byte     mipBlendColors[16][4] = {
182         {0,0,0,0},
183         {255,0,0,128},
184         {0,255,0,128},
185         {0,0,255,128},
186         {255,0,0,128},
187         {0,255,0,128},
188         {0,0,255,128},
189         {255,0,0,128},
190         {0,255,0,128},
191         {0,0,255,128},
192         {255,0,0,128},
193         {0,255,0,128},
194         {0,0,255,128},
195         {255,0,0,128},
196         {0,255,0,128},
197         {0,0,255,128},
198 };
199
200 /*
201 ===============
202 SelectInternalFormat
203
204 This may need to scan six cube map images
205 ===============
206 */
207 GLenum idImage::SelectInternalFormat( const byte **dataPtrs, int numDataPtrs, int width, int height,
208                                                                          textureDepth_t minimumDepth, bool *monochromeResult ) const {
209         int             i, c;
210         const byte      *scan;
211         int             rgbOr, rgbAnd, aOr, aAnd;
212         int             rgbDiffer, rgbaDiffer;
213
214         // determine if the rgb channels are all the same
215         // and if either all rgb or all alpha are 255
216         c = width*height;
217         rgbDiffer = 0;
218         rgbaDiffer = 0;
219         rgbOr = 0;
220         rgbAnd = -1;
221         aOr = 0;
222         aAnd = -1;
223
224         *monochromeResult = true;       // until shown otherwise
225
226         for ( int side = 0 ; side < numDataPtrs ; side++ ) {
227                 scan = dataPtrs[side];
228                 for ( i = 0; i < c; i++, scan += 4 ) {
229                         int             cor, cand;
230
231                         aOr |= scan[3];
232                         aAnd &= scan[3];
233
234                         cor = scan[0] | scan[1] | scan[2];
235                         cand = scan[0] & scan[1] & scan[2];
236                         
237                         // if rgb are all the same, the or and and will match
238                         rgbDiffer |= ( cor ^ cand );
239
240                         // our "isMonochrome" test is more lax than rgbDiffer,
241                         // allowing the values to be off by several units and
242                         // still use the NV20 mono path
243                         if ( *monochromeResult ) {
244                                 if ( abs( scan[0] - scan[1] ) > 16
245                                         || abs( scan[0] - scan[2] ) > 16 ) {
246                                                 *monochromeResult = false;
247                                         }
248                         }
249
250                         rgbOr |= cor;
251                         rgbAnd &= cand;
252
253                         cor |= scan[3];
254                         cand &= scan[3];
255
256                         rgbaDiffer |= ( cor ^ cand );
257                 }
258         }
259
260         // we assume that all 0 implies that the alpha channel isn't needed,
261         // because some tools will spit out 32 bit images with a 0 alpha instead
262         // of 255 alpha, but if the alpha actually is referenced, there will be
263         // different behavior in the compressed vs uncompressed states.
264         bool needAlpha;
265         if ( aAnd == 255 || aOr == 0 ) {
266                 needAlpha = false;
267         } else {
268                 needAlpha = true;
269         }
270
271         // catch normal maps first
272         if ( minimumDepth == TD_BUMP ) {
273                 if ( globalImages->image_useCompression.GetBool() && globalImages->image_useNormalCompression.GetInteger() == 1 && glConfig.sharedTexturePaletteAvailable ) {
274                         // image_useNormalCompression should only be set to 1 on nv_10 and nv_20 paths
275                         return GL_COLOR_INDEX8_EXT;
276                 } else if ( globalImages->image_useCompression.GetBool() && globalImages->image_useNormalCompression.GetInteger() && glConfig.textureCompressionAvailable ) {
277                         // image_useNormalCompression == 2 uses rxgb format which produces really good quality for medium settings
278                         return GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
279                 } else {
280                         // we always need the alpha channel for bump maps for swizzling
281                         return GL_RGBA8; 
282                 }
283         }
284
285         // allow a complete override of image compression with a cvar
286         if ( !globalImages->image_useCompression.GetBool() ) {
287                 minimumDepth = TD_HIGH_QUALITY;
288         }
289
290         if ( minimumDepth == TD_SPECULAR ) {
291                 // we are assuming that any alpha channel is unintentional
292                 if ( glConfig.textureCompressionAvailable ) {
293                         return GL_COMPRESSED_RGB_S3TC_DXT1_EXT;
294                 } else {
295                         return GL_RGB5;
296                 }
297         }
298         if ( minimumDepth == TD_DIFFUSE ) {
299                 // we might intentionally have an alpha channel for alpha tested textures
300                 if ( glConfig.textureCompressionAvailable ) {
301                         if ( !needAlpha ) {
302                                 return GL_COMPRESSED_RGB_S3TC_DXT1_EXT;
303                         } else {
304                                 return GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
305                         }
306                 } else if ( ( aAnd == 255 || aOr == 0 ) ) {
307                         return GL_RGB5;
308                 } else {
309                         return GL_RGBA4;
310                 }
311         }
312
313         // there will probably be some drivers that don't
314         // correctly handle the intensity/alpha/luminance/luminance+alpha
315         // formats, so provide a fallback that only uses the rgb/rgba formats
316         if ( !globalImages->image_useAllFormats.GetBool() ) {
317                 // pretend rgb is varying and inconsistant, which
318                 // prevents any of the more compact forms
319                 rgbDiffer = 1;
320                 rgbaDiffer = 1;
321                 rgbAnd = 0;
322         }
323
324         // cases without alpha
325         if ( !needAlpha ) {
326                 if ( minimumDepth == TD_HIGH_QUALITY ) {
327                         return GL_RGB8;                 // four bytes
328                 }
329                 if ( glConfig.textureCompressionAvailable ) {
330                         return GL_COMPRESSED_RGB_S3TC_DXT1_EXT; // half byte
331                 }
332                 return GL_RGB5;                 // two bytes
333         }
334
335         // cases with alpha
336         if ( !rgbaDiffer ) {
337                 if ( minimumDepth != TD_HIGH_QUALITY && glConfig.textureCompressionAvailable ) {
338                         return GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;        // one byte
339                 }
340                 return GL_INTENSITY8;   // single byte for all channels
341         }
342
343 #if 0
344         // we don't support alpha textures any more, because there
345         // is a discrepancy in the definition of TEX_ENV_COMBINE that
346         // causes them to be treated as 0 0 0 A, instead of 1 1 1 A as
347         // normal texture modulation treats them
348         if ( rgbAnd == 255 ) {
349                 return GL_ALPHA8;               // single byte, only alpha
350         }
351 #endif
352
353         if ( minimumDepth == TD_HIGH_QUALITY ) {
354                 return GL_RGBA8;        // four bytes
355         }
356         if ( glConfig.textureCompressionAvailable ) {
357                 return GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;        // one byte
358         }
359         if ( !rgbDiffer ) {
360                 return GL_LUMINANCE8_ALPHA8;    // two bytes, max quality
361         }
362         return GL_RGBA4;        // two bytes
363 }
364
365 /*
366 ==================
367 SetImageFilterAndRepeat
368 ==================
369 */
370 void idImage::SetImageFilterAndRepeat() const {
371         // set the minimize / maximize filtering
372         switch( filter ) {
373         case TF_DEFAULT:
374                 qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, globalImages->textureMinFilter );
375                 qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, globalImages->textureMaxFilter );
376                 break;
377         case TF_LINEAR:
378                 qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
379                 qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
380                 break;
381         case TF_NEAREST:
382                 qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
383                 qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
384                 break;
385         default:
386                 common->FatalError( "R_CreateImage: bad texture filter" );
387         }
388
389         if ( glConfig.anisotropicAvailable ) {
390                 // only do aniso filtering on mip mapped images
391                 if ( filter == TF_DEFAULT ) {
392                         qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, globalImages->textureAnisotropy );
393                 } else {
394                         qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1 );
395                 }
396         }
397         if ( glConfig.textureLODBiasAvailable ) {
398                 qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_LOD_BIAS_EXT, globalImages->textureLODBias );
399         }
400
401         // set the wrap/clamp modes
402         switch( repeat ) {
403         case TR_REPEAT:
404                 qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT );
405                 qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT );
406                 break;
407         case TR_CLAMP_TO_BORDER:
408                 qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER );
409                 qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER );
410                 break;
411         case TR_CLAMP_TO_ZERO:
412         case TR_CLAMP_TO_ZERO_ALPHA:
413         case TR_CLAMP:
414                 qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
415                 qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
416                 break;
417         default:
418                 common->FatalError( "R_CreateImage: bad texture repeat" );
419         }
420 }
421
422 /*
423 ================
424 idImage::Downsize
425 helper function that takes the current width/height and might make them smaller
426 ================
427 */
428 void idImage::GetDownsize( int &scaled_width, int &scaled_height ) const {
429         int size = 0;
430
431         // perform optional picmip operation to save texture memory
432         if ( depth == TD_SPECULAR && globalImages->image_downSizeSpecular.GetInteger() ) {
433                 size = globalImages->image_downSizeSpecularLimit.GetInteger();
434                 if ( size == 0 ) {
435                         size = 64;
436                 }
437         } else if ( depth == TD_BUMP && globalImages->image_downSizeBump.GetInteger() ) {
438                 size = globalImages->image_downSizeBumpLimit.GetInteger();
439                 if ( size == 0 ) {
440                         size = 64;
441                 }
442         } else if ( ( allowDownSize || globalImages->image_forceDownSize.GetBool() ) && globalImages->image_downSize.GetInteger() ) {
443                 size = globalImages->image_downSizeLimit.GetInteger();
444                 if ( size == 0 ) {
445                         size = 256;
446                 }
447         }
448
449         if ( size > 0 ) {
450                 while ( scaled_width > size || scaled_height > size ) {
451                         if ( scaled_width > 1 ) {
452                                 scaled_width >>= 1;
453                         }
454                         if ( scaled_height > 1 ) {
455                                 scaled_height >>= 1;
456                         }
457                 }
458         }
459
460         // clamp to minimum size
461         if ( scaled_width < 1 ) {
462                 scaled_width = 1;
463         }
464         if ( scaled_height < 1 ) {
465                 scaled_height = 1;
466         }
467
468         // clamp size to the hardware specific upper limit
469         // scale both axis down equally so we don't have to
470         // deal with a half mip resampling
471         // This causes a 512*256 texture to sample down to
472         // 256*128 on a voodoo3, even though it could be 256*256
473         while ( scaled_width > glConfig.maxTextureSize
474                 || scaled_height > glConfig.maxTextureSize ) {
475                 scaled_width >>= 1;
476                 scaled_height >>= 1;
477         }
478 }
479
480 /*
481 ================
482 GenerateImage
483
484 The alpha channel bytes should be 255 if you don't
485 want the channel.
486
487 We need a material characteristic to ask for specific texture modes.
488
489 Designed limitations of flexibility:
490
491 No support for texture borders.
492
493 No support for texture border color.
494
495 No support for texture environment colors or GL_BLEND or GL_DECAL
496 texture environments, because the automatic optimization to single
497 or dual component textures makes those modes potentially undefined.
498
499 No non-power-of-two images.
500
501 No palettized textures.
502
503 There is no way to specify separate wrap/clamp values for S and T
504
505 There is no way to specify explicit mip map levels
506
507 ================
508 */
509 void idImage::GenerateImage( const byte *pic, int width, int height, 
510                                            textureFilter_t filterParm, bool allowDownSizeParm, 
511                                            textureRepeat_t repeatParm, textureDepth_t depthParm ) {
512         bool    preserveBorder;
513         byte            *scaledBuffer;
514         int                     scaled_width, scaled_height;
515         byte            *shrunk;
516
517         PurgeImage();
518
519         filter = filterParm;
520         allowDownSize = allowDownSizeParm;
521         repeat = repeatParm;
522         depth = depthParm;
523
524         // if we don't have a rendering context, just return after we
525         // have filled in the parms.  We must have the values set, or
526         // an image match from a shader before OpenGL starts would miss
527         // the generated texture
528         if ( !glConfig.isInitialized ) {
529                 return;
530         }
531
532         // don't let mip mapping smear the texture into the clamped border
533         if ( repeat == TR_CLAMP_TO_ZERO ) {
534                 preserveBorder = true;
535         } else {
536                 preserveBorder = false;
537         }
538
539         // make sure it is a power of 2
540         scaled_width = MakePowerOfTwo( width );
541         scaled_height = MakePowerOfTwo( height );
542
543         if ( scaled_width != width || scaled_height != height ) {
544                 common->Error( "R_CreateImage: not a power of 2 image" );
545         }
546
547         // Optionally modify our width/height based on options/hardware
548         GetDownsize( scaled_width, scaled_height );
549
550         scaledBuffer = NULL;
551
552         // generate the texture number
553         qglGenTextures( 1, &texnum );
554
555         // select proper internal format before we resample
556         internalFormat = SelectInternalFormat( &pic, 1, width, height, depth, &isMonochrome );
557
558         // copy or resample data as appropriate for first MIP level
559         if ( ( scaled_width == width ) && ( scaled_height == height ) ) {
560                 // we must copy even if unchanged, because the border zeroing
561                 // would otherwise modify const data
562                 scaledBuffer = (byte *)R_StaticAlloc( sizeof( unsigned ) * scaled_width * scaled_height );
563                 memcpy (scaledBuffer, pic, width*height*4);
564         } else {
565                 // resample down as needed (FIXME: this doesn't seem like it resamples anymore!)
566                 // scaledBuffer = R_ResampleTexture( pic, width, height, width >>= 1, height >>= 1 );
567                 scaledBuffer = R_MipMap( pic, width, height, preserveBorder );
568                 width >>= 1;
569                 height >>= 1;
570                 if ( width < 1 ) {
571                         width = 1;
572                 }
573                 if ( height < 1 ) {
574                         height = 1;
575                 }
576
577                 while ( width > scaled_width || height > scaled_height ) {
578                         shrunk = R_MipMap( scaledBuffer, width, height, preserveBorder );
579                         R_StaticFree( scaledBuffer );
580                         scaledBuffer = shrunk;
581
582                         width >>= 1;
583                         height >>= 1;
584                         if ( width < 1 ) {
585                                 width = 1;
586                         }
587                         if ( height < 1 ) {
588                                 height = 1;
589                         }
590                 }
591
592                 // one might have shrunk down below the target size
593                 scaled_width = width;
594                 scaled_height = height;
595         }
596
597         uploadHeight = scaled_height;
598         uploadWidth = scaled_width;
599         type = TT_2D;
600
601         // zero the border if desired, allowing clamped projection textures
602         // even after picmip resampling or careless artists.
603         if ( repeat == TR_CLAMP_TO_ZERO ) {
604                 byte    rgba[4];
605
606                 rgba[0] = rgba[1] = rgba[2] = 0;
607                 rgba[3] = 255;
608                 R_SetBorderTexels( (byte *)scaledBuffer, width, height, rgba );
609         }
610         if ( repeat == TR_CLAMP_TO_ZERO_ALPHA ) {
611                 byte    rgba[4];
612
613                 rgba[0] = rgba[1] = rgba[2] = 255;
614                 rgba[3] = 0;
615                 R_SetBorderTexels( (byte *)scaledBuffer, width, height, rgba );
616         }
617
618         if ( generatorFunction == NULL && ( depth == TD_BUMP && globalImages->image_writeNormalTGA.GetBool() || depth != TD_BUMP && globalImages->image_writeTGA.GetBool() ) ) {
619                 // Optionally write out the texture to a .tga
620                 char filename[MAX_IMAGE_NAME];
621                 ImageProgramStringToCompressedFileName( imgName, filename );
622                 char *ext = strrchr(filename, '.');
623                 if ( ext ) {
624                         strcpy( ext, ".tga" );
625                         // swap the red/alpha for the write
626                         /*
627                         if ( depth == TD_BUMP ) {
628                                 for ( int i = 0; i < scaled_width * scaled_height * 4; i += 4 ) {
629                                         scaledBuffer[ i ] = scaledBuffer[ i + 3 ];
630                                         scaledBuffer[ i + 3 ] = 0;
631                                 }
632                         }
633                         */
634                         R_WriteTGA( filename, scaledBuffer, scaled_width, scaled_height, false );
635
636                         // put it back
637                         /*
638                         if ( depth == TD_BUMP ) {
639                                 for ( int i = 0; i < scaled_width * scaled_height * 4; i += 4 ) {
640                                         scaledBuffer[ i + 3 ] = scaledBuffer[ i ];
641                                         scaledBuffer[ i ] = 0;
642                                 }
643                         }
644                         */
645                 }
646         }
647
648         // swap the red and alpha for rxgb support
649         // do this even on tga normal maps so we only have to use
650         // one fragment program
651         // if the image is precompressed ( either in palletized mode or true rxgb mode )
652         // then it is loaded above and the swap never happens here
653         if ( depth == TD_BUMP && globalImages->image_useNormalCompression.GetInteger() != 1 ) {
654                 for ( int i = 0; i < scaled_width * scaled_height * 4; i += 4 ) {
655                         scaledBuffer[ i + 3 ] = scaledBuffer[ i ];
656                         scaledBuffer[ i ] = 0;
657                 }
658         }
659         // upload the main image level
660         Bind();
661
662
663         if ( internalFormat == GL_COLOR_INDEX8_EXT ) {
664                 /*
665                 if ( depth == TD_BUMP ) {
666                         for ( int i = 0; i < scaled_width * scaled_height * 4; i += 4 ) {
667                                 scaledBuffer[ i ] = scaledBuffer[ i + 3 ];
668                                 scaledBuffer[ i + 3 ] = 0;
669                         }
670                 }
671                 */
672                 UploadCompressedNormalMap( scaled_width, scaled_height, scaledBuffer, 0 );
673         } else {
674                 qglTexImage2D( GL_TEXTURE_2D, 0, internalFormat, scaled_width, scaled_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, scaledBuffer );
675         }
676
677         // create and upload the mip map levels, which we do in all cases, even if we don't think they are needed
678         int             miplevel;
679
680         miplevel = 0;
681         while ( scaled_width > 1 || scaled_height > 1 ) {
682                 // preserve the border after mip map unless repeating
683                 shrunk = R_MipMap( scaledBuffer, scaled_width, scaled_height, preserveBorder );
684                 R_StaticFree( scaledBuffer );
685                 scaledBuffer = shrunk;
686
687                 scaled_width >>= 1;
688                 scaled_height >>= 1;
689                 if ( scaled_width < 1 ) {
690                         scaled_width = 1;
691                 }
692                 if ( scaled_height < 1 ) {
693                         scaled_height = 1;
694                 }
695                 miplevel++;
696
697                 // this is a visualization tool that shades each mip map
698                 // level with a different color so you can see the
699                 // rasterizer's texture level selection algorithm
700                 // Changing the color doesn't help with lumminance/alpha/intensity formats...
701                 if ( depth == TD_DIFFUSE && globalImages->image_colorMipLevels.GetBool() ) {
702                         R_BlendOverTexture( (byte *)scaledBuffer, scaled_width * scaled_height, mipBlendColors[miplevel] );
703                 }
704
705                 // upload the mip map
706                 if ( internalFormat == GL_COLOR_INDEX8_EXT ) {
707                         UploadCompressedNormalMap( scaled_width, scaled_height, scaledBuffer, miplevel );
708                 } else {
709                         qglTexImage2D( GL_TEXTURE_2D, miplevel, internalFormat, scaled_width, scaled_height, 
710                                 0, GL_RGBA, GL_UNSIGNED_BYTE, scaledBuffer );
711                 }
712         }
713
714         if ( scaledBuffer != 0 ) {
715                 R_StaticFree( scaledBuffer );
716         }
717
718         SetImageFilterAndRepeat();
719
720         // see if we messed anything up
721         GL_CheckErrors();
722 }
723
724
725 /*
726 ==================
727 Generate3DImage
728 ==================
729 */
730 void idImage::Generate3DImage( const byte *pic, int width, int height, int picDepth,
731                                            textureFilter_t filterParm, bool allowDownSizeParm, 
732                                            textureRepeat_t repeatParm, textureDepth_t minDepthParm ) {
733         int                     scaled_width, scaled_height, scaled_depth;
734
735         PurgeImage();
736
737         filter = filterParm;
738         allowDownSize = allowDownSizeParm;
739         repeat = repeatParm;
740         depth = minDepthParm;
741
742         // if we don't have a rendering context, just return after we
743         // have filled in the parms.  We must have the values set, or
744         // an image match from a shader before OpenGL starts would miss
745         // the generated texture
746         if ( !glConfig.isInitialized ) {
747                 return;
748         }
749
750         // make sure it is a power of 2
751         scaled_width = MakePowerOfTwo( width );
752         scaled_height = MakePowerOfTwo( height );
753         scaled_depth = MakePowerOfTwo( picDepth );
754         if ( scaled_width != width || scaled_height != height || scaled_depth != picDepth ) {
755                 common->Error( "R_Create3DImage: not a power of 2 image" );
756         }
757
758         // FIXME: allow picmip here
759
760         // generate the texture number
761         qglGenTextures( 1, &texnum );
762
763         // select proper internal format before we resample
764         // this function doesn't need to know it is 3D, so just make it very "tall"
765         internalFormat = SelectInternalFormat( &pic, 1, width, height * picDepth, minDepthParm, &isMonochrome );
766
767         uploadHeight = scaled_height;
768         uploadWidth = scaled_width;
769         uploadDepth = scaled_depth;
770
771
772         type = TT_3D;
773
774         // upload the main image level
775         Bind();
776
777         qglTexImage3D(GL_TEXTURE_3D, 0, internalFormat, scaled_width, scaled_height, scaled_depth,
778                 0, GL_RGBA, GL_UNSIGNED_BYTE, pic );
779
780         // create and upload the mip map levels
781         int             miplevel;
782         byte    *scaledBuffer, *shrunk;
783
784         scaledBuffer = (byte *)R_StaticAlloc( scaled_width * scaled_height * scaled_depth * 4 );
785         memcpy( scaledBuffer, pic, scaled_width * scaled_height * scaled_depth * 4 );
786         miplevel = 0;
787         while ( scaled_width > 1 || scaled_height > 1 || scaled_depth > 1 ) {
788                 // preserve the border after mip map unless repeating
789                 shrunk = R_MipMap3D( scaledBuffer, scaled_width, scaled_height, scaled_depth,
790                         (bool)(repeat != TR_REPEAT) );
791                 R_StaticFree( scaledBuffer );
792                 scaledBuffer = shrunk;
793
794                 scaled_width >>= 1;
795                 scaled_height >>= 1;
796                 scaled_depth >>= 1;
797                 if ( scaled_width < 1 ) {
798                         scaled_width = 1;
799                 }
800                 if ( scaled_height < 1 ) {
801                         scaled_height = 1;
802                 }
803                 if ( scaled_depth < 1 ) {
804                         scaled_depth = 1;
805                 }
806                 miplevel++;
807
808                 // upload the mip map
809                 qglTexImage3D(GL_TEXTURE_3D, miplevel, internalFormat, scaled_width, scaled_height, scaled_depth,
810                         0, GL_RGBA, GL_UNSIGNED_BYTE, scaledBuffer );
811         }
812         R_StaticFree( scaledBuffer );
813
814         // set the minimize / maximize filtering
815         switch( filter ) {
816         case TF_DEFAULT:
817                 qglTexParameterf(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, globalImages->textureMinFilter );
818                 qglTexParameterf(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, globalImages->textureMaxFilter );
819                 break;
820         case TF_LINEAR:
821                 qglTexParameterf(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
822                 qglTexParameterf(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
823                 break;
824         case TF_NEAREST:
825                 qglTexParameterf(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
826                 qglTexParameterf(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
827                 break;
828         default:
829                 common->FatalError( "R_CreateImage: bad texture filter" );
830         }
831
832         // set the wrap/clamp modes
833         switch( repeat ) {
834         case TR_REPEAT:
835                 qglTexParameterf( GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_REPEAT );
836                 qglTexParameterf( GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_REPEAT );
837                 qglTexParameterf( GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_REPEAT );
838                 break;
839         case TR_CLAMP_TO_BORDER:
840                 qglTexParameterf( GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER );
841                 qglTexParameterf( GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER );
842                 break;
843         case TR_CLAMP_TO_ZERO:
844         case TR_CLAMP_TO_ZERO_ALPHA:
845         case TR_CLAMP:
846                 qglTexParameterf( GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
847                 qglTexParameterf( GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
848                 qglTexParameterf( GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE );
849                 break;
850         default:
851                 common->FatalError( "R_CreateImage: bad texture repeat" );
852         }
853
854         // see if we messed anything up
855         GL_CheckErrors();
856 }
857
858
859 /*
860 ====================
861 GenerateCubeImage
862
863 Non-square cube sides are not allowed
864 ====================
865 */
866 void idImage::GenerateCubeImage( const byte *pic[6], int size, 
867                                            textureFilter_t filterParm, bool allowDownSizeParm, 
868                                            textureDepth_t depthParm ) {
869         int                     scaled_width, scaled_height;
870         int                     width, height;
871         int                     i;
872
873         PurgeImage();
874
875         filter = filterParm;
876         allowDownSize = allowDownSizeParm;
877         depth = depthParm;
878
879         type = TT_CUBIC;
880
881         // if we don't have a rendering context, just return after we
882         // have filled in the parms.  We must have the values set, or
883         // an image match from a shader before OpenGL starts would miss
884         // the generated texture
885         if ( !glConfig.isInitialized ) {
886                 return;
887         }
888
889         if ( ! glConfig.cubeMapAvailable ) {
890                 return;
891         }
892
893         width = height = size;
894
895         // generate the texture number
896         qglGenTextures( 1, &texnum );
897
898         // select proper internal format before we resample
899         internalFormat = SelectInternalFormat( pic, 6, width, height, depth, &isMonochrome );
900
901         // don't bother with downsample for now
902         scaled_width = width;
903         scaled_height = height;
904
905         uploadHeight = scaled_height;
906         uploadWidth = scaled_width;
907
908         Bind();
909
910         // no other clamp mode makes sense
911         qglTexParameteri(GL_TEXTURE_CUBE_MAP_EXT, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
912         qglTexParameteri(GL_TEXTURE_CUBE_MAP_EXT, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
913
914         // set the minimize / maximize filtering
915         switch( filter ) {
916         case TF_DEFAULT:
917                 qglTexParameterf(GL_TEXTURE_CUBE_MAP_EXT, GL_TEXTURE_MIN_FILTER, globalImages->textureMinFilter );
918                 qglTexParameterf(GL_TEXTURE_CUBE_MAP_EXT, GL_TEXTURE_MAG_FILTER, globalImages->textureMaxFilter );
919                 break;
920         case TF_LINEAR:
921                 qglTexParameterf(GL_TEXTURE_CUBE_MAP_EXT, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
922                 qglTexParameterf(GL_TEXTURE_CUBE_MAP_EXT, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
923                 break;
924         case TF_NEAREST:
925                 qglTexParameterf(GL_TEXTURE_CUBE_MAP_EXT, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
926                 qglTexParameterf(GL_TEXTURE_CUBE_MAP_EXT, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
927                 break;
928         default:
929                 common->FatalError( "R_CreateImage: bad texture filter" );
930         }
931
932         // upload the base level
933         // FIXME: support GL_COLOR_INDEX8_EXT?
934         for ( i = 0 ; i < 6 ; i++ ) {
935                 qglTexImage2D( GL_TEXTURE_CUBE_MAP_POSITIVE_X_EXT+i, 0, internalFormat, scaled_width, scaled_height, 0, 
936                         GL_RGBA, GL_UNSIGNED_BYTE, pic[i] );
937         }
938
939
940         // create and upload the mip map levels
941         int             miplevel;
942         byte    *shrunk[6];
943
944         for ( i = 0 ; i < 6 ; i++ ) {
945                 shrunk[i] = R_MipMap( pic[i], scaled_width, scaled_height, false );
946         }
947
948         miplevel = 1;
949         while ( scaled_width > 1 ) {
950                 for ( i = 0 ; i < 6 ; i++ ) {
951                         byte    *shrunken;
952
953                         qglTexImage2D( GL_TEXTURE_CUBE_MAP_POSITIVE_X_EXT+i, miplevel, internalFormat, 
954                                 scaled_width / 2, scaled_height / 2, 0, 
955                                 GL_RGBA, GL_UNSIGNED_BYTE, shrunk[i] );
956
957                         if ( scaled_width > 2 ) {
958                                 shrunken = R_MipMap( shrunk[i], scaled_width/2, scaled_height/2, false );
959                         } else {
960                                 shrunken = NULL;
961                         }
962
963                         R_StaticFree( shrunk[i] );
964                         shrunk[i] = shrunken;
965                 }
966
967                 scaled_width >>= 1;
968                 scaled_height >>= 1;
969                 miplevel++;
970         }
971
972         // see if we messed anything up
973         GL_CheckErrors();
974 }
975
976
977 /*
978 ================
979 ImageProgramStringToFileCompressedFileName
980 ================
981 */
982 void idImage::ImageProgramStringToCompressedFileName( const char *imageProg, char *fileName ) const {
983         const char      *s;
984         char    *f;
985
986         strcpy( fileName, "dds/" );
987         f = fileName + strlen( fileName );
988
989         int depth = 0;
990
991         // convert all illegal characters to underscores
992         // this could conceivably produce a duplicated mapping, but we aren't going to worry about it
993         for ( s = imageProg ; *s ; s++ ) {
994                 if ( *s == '/' || *s == '\\' || *s == '(') {
995                         if ( depth < 4 ) {
996                                 *f = '/';
997                                 depth ++;
998                         } else {
999                                 *f = ' ';
1000                         }
1001                         f++;
1002                 } else if ( *s == '<' || *s == '>' || *s == ':' || *s == '|' || *s == '"' || *s == '.' ) {
1003                         *f = '_';
1004                         f++;
1005                 } else if ( *s == ' ' && *(f-1) == '/' ) {      // ignore a space right after a slash
1006                 } else if ( *s == ')' || *s == ',' ) {          // always ignore these
1007                 } else {
1008                         *f = *s;
1009                         f++;
1010                 }
1011         }
1012         *f++ = 0;
1013         strcat( fileName, ".dds" );
1014 }
1015
1016 /*
1017 ==================
1018 NumLevelsForImageSize
1019 ==================
1020 */
1021 int     idImage::NumLevelsForImageSize( int width, int height ) const {
1022         int     numLevels = 1;
1023
1024         while ( width > 1 || height > 1 ) {
1025                 numLevels++;
1026                 width >>= 1;
1027                 height >>= 1;
1028         }
1029
1030         return numLevels;
1031 }
1032
1033 /*
1034 ================
1035 WritePrecompressedImage
1036
1037 When we are happy with our source data, we can write out precompressed
1038 versions of everything to speed future load times.
1039 ================
1040 */
1041 void idImage::WritePrecompressedImage() {
1042
1043         // Always write the precompressed image if we're making a build
1044         if ( !com_makingBuild.GetBool() ) {
1045                 if ( !globalImages->image_writePrecompressedTextures.GetBool() || !globalImages->image_usePrecompressedTextures.GetBool() ) {
1046                         return;
1047                 }
1048         }
1049
1050         if ( !glConfig.isInitialized ) {
1051                 return;
1052         }
1053
1054         char filename[MAX_IMAGE_NAME];
1055         ImageProgramStringToCompressedFileName( imgName, filename );
1056
1057
1058
1059         int numLevels = NumLevelsForImageSize( uploadWidth, uploadHeight );
1060         if ( numLevels > MAX_TEXTURE_LEVELS ) {
1061                 common->Warning( "R_WritePrecompressedImage: level > MAX_TEXTURE_LEVELS for image %s", filename );
1062                 return;
1063         }
1064
1065         // glGetTexImage only supports a small subset of all the available internal formats
1066         // We have to use BGRA because DDS is a windows based format
1067         int altInternalFormat = 0;
1068         int bitSize = 0;
1069         switch ( internalFormat ) {
1070                 case GL_COLOR_INDEX8_EXT:
1071                 case GL_COLOR_INDEX:
1072                         // this will not work with dds viewers but we need it in this format to save disk
1073                         // load speed ( i.e. size ) 
1074                         altInternalFormat = GL_COLOR_INDEX;
1075                         bitSize = 24;
1076                 break;
1077                 case 1:
1078                 case GL_INTENSITY8:
1079                 case GL_LUMINANCE8:
1080                 case 3:
1081                 case GL_RGB8:
1082                         altInternalFormat = GL_BGR_EXT;
1083                         bitSize = 24;
1084                 break;
1085                 case GL_LUMINANCE8_ALPHA8:
1086                 case 4:
1087                 case GL_RGBA8:
1088                         altInternalFormat = GL_BGRA_EXT;
1089                         bitSize = 32;
1090                 break;
1091                 case GL_ALPHA8:
1092                         altInternalFormat = GL_ALPHA;
1093                         bitSize = 8;
1094                 break;
1095                 default:
1096                         if ( FormatIsDXT( internalFormat ) ) {
1097                                 altInternalFormat = internalFormat;
1098                         } else {
1099                                 common->Warning("Unknown or unsupported format for %s", filename);
1100                                 return;
1101                         }
1102         }
1103
1104         if ( globalImages->image_useOffLineCompression.GetBool() && FormatIsDXT( altInternalFormat ) ) {
1105                 idStr outFile = fileSystem->RelativePathToOSPath( filename, "fs_basepath" );
1106                 idStr inFile = outFile;
1107                 inFile.StripFileExtension();
1108                 inFile.SetFileExtension( "tga" );
1109                 idStr format;
1110                 if ( depth == TD_BUMP ) {
1111                         format = "RXGB +red 0.0 +green 0.5 +blue 0.5";
1112                 } else {
1113                         switch ( altInternalFormat ) {
1114                                 case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
1115                                         format = "DXT1";
1116                                         break;
1117                                 case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
1118                                         format = "DXT1 -alpha_threshold";
1119                                         break;
1120                                 case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT:
1121                                         format = "DXT3";
1122                                         break;
1123                                 case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT:
1124                                         format = "DXT5";
1125                                         break;
1126                         }
1127                 }
1128                 globalImages->AddDDSCommand( va( "z:/d3xp/compressonator/thecompressonator -convert \"%s\" \"%s\" %s -mipmaps\n", inFile.c_str(), outFile.c_str(), format.c_str() ) );
1129                 return;
1130         }
1131
1132
1133         ddsFileHeader_t header;
1134         memset( &header, 0, sizeof(header) );
1135         header.dwSize = sizeof(header);
1136         header.dwFlags = DDSF_CAPS | DDSF_PIXELFORMAT | DDSF_WIDTH | DDSF_HEIGHT;
1137         header.dwHeight = uploadHeight;
1138         header.dwWidth = uploadWidth;
1139
1140         // hack in our monochrome flag for the NV20 optimization
1141         if ( isMonochrome ) {
1142                 header.dwFlags |= DDSF_ID_MONOCHROME;
1143         }
1144
1145         if ( FormatIsDXT( altInternalFormat ) ) {
1146                 // size (in bytes) of the compressed base image
1147                 header.dwFlags |= DDSF_LINEARSIZE;
1148                 header.dwPitchOrLinearSize = ( ( uploadWidth + 3 ) / 4 ) * ( ( uploadHeight + 3 ) / 4 )*
1149                         (altInternalFormat <= GL_COMPRESSED_RGBA_S3TC_DXT1_EXT ? 8 : 16);
1150         }
1151         else {
1152                 // 4 Byte aligned line width (from nv_dds)
1153                 header.dwFlags |= DDSF_PITCH;
1154                 header.dwPitchOrLinearSize = ( ( uploadWidth * bitSize + 31 ) & -32 ) >> 3;
1155         }
1156
1157         header.dwCaps1 = DDSF_TEXTURE;
1158
1159         if ( numLevels > 1 ) {
1160                 header.dwMipMapCount = numLevels;
1161                 header.dwFlags |= DDSF_MIPMAPCOUNT;
1162                 header.dwCaps1 |= DDSF_MIPMAP | DDSF_COMPLEX;
1163         }
1164
1165         header.ddspf.dwSize = sizeof(header.ddspf);
1166         if ( FormatIsDXT( altInternalFormat ) ) {
1167                 header.ddspf.dwFlags = DDSF_FOURCC;
1168                 switch ( altInternalFormat ) {
1169                 case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
1170                         header.ddspf.dwFourCC = DDS_MAKEFOURCC('D','X','T','1');
1171                         break;
1172                 case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
1173                         header.ddspf.dwFlags |= DDSF_ALPHAPIXELS;
1174                         header.ddspf.dwFourCC = DDS_MAKEFOURCC('D','X','T','1');
1175                         break;
1176                 case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT:
1177                         header.ddspf.dwFourCC = DDS_MAKEFOURCC('D','X','T','3');
1178                         break;
1179                 case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT:
1180                         header.ddspf.dwFourCC = DDS_MAKEFOURCC('D','X','T','5');
1181                         break;
1182                 }
1183         } else {
1184                 header.ddspf.dwFlags = ( internalFormat == GL_COLOR_INDEX8_EXT ) ? DDSF_RGB | DDSF_ID_INDEXCOLOR : DDSF_RGB;
1185                 header.ddspf.dwRGBBitCount = bitSize;
1186                 switch ( altInternalFormat ) {
1187                 case GL_BGRA_EXT:
1188                 case GL_LUMINANCE_ALPHA:
1189                         header.ddspf.dwFlags |= DDSF_ALPHAPIXELS;
1190                         header.ddspf.dwABitMask = 0xFF000000;
1191                         // Fall through
1192                 case GL_BGR_EXT:
1193                 case GL_LUMINANCE:
1194                 case GL_COLOR_INDEX:
1195                         header.ddspf.dwRBitMask = 0x00FF0000;
1196                         header.ddspf.dwGBitMask = 0x0000FF00;
1197                         header.ddspf.dwBBitMask = 0x000000FF;
1198                         break;
1199                 case GL_ALPHA:
1200                         header.ddspf.dwFlags = DDSF_ALPHAPIXELS;
1201                         header.ddspf.dwABitMask = 0xFF000000;
1202                         break;
1203                 default:
1204                         common->Warning( "Unknown or unsupported format for %s", filename );
1205                         return;
1206                 }
1207         }
1208
1209         idFile *f = fileSystem->OpenFileWrite( filename );
1210         if ( f == NULL ) {
1211                 common->Warning( "Could not open %s trying to write precompressed image", filename );
1212                 return;
1213         }
1214         common->Printf( "Writing precompressed image: %s\n", filename );
1215
1216         f->Write( "DDS ", 4 );
1217         f->Write( &header, sizeof(header) );
1218
1219         // bind to the image so we can read back the contents
1220         Bind();
1221
1222         qglPixelStorei( GL_PACK_ALIGNMENT, 1 ); // otherwise small rows get padded to 32 bits
1223
1224         int uw = uploadWidth;
1225         int uh = uploadHeight;
1226
1227         // Will be allocated first time through the loop
1228         byte *data = NULL;
1229
1230         for ( int level = 0 ; level < numLevels ; level++ ) {
1231
1232                 int size = 0;
1233                 if ( FormatIsDXT( altInternalFormat ) ) {
1234                         size = ( ( uw + 3 ) / 4 ) * ( ( uh + 3 ) / 4 ) *
1235                                 (altInternalFormat <= GL_COMPRESSED_RGBA_S3TC_DXT1_EXT ? 8 : 16);
1236                 } else {
1237                         size = uw * uh * (bitSize / 8);
1238                 }
1239
1240                 if (data == NULL) {
1241                         data = (byte *)R_StaticAlloc( size );
1242                 }
1243
1244                 if ( FormatIsDXT( altInternalFormat ) ) {
1245                         qglGetCompressedTexImageARB( GL_TEXTURE_2D, level, data );
1246                 } else {
1247                         qglGetTexImage( GL_TEXTURE_2D, level, altInternalFormat, GL_UNSIGNED_BYTE, data );
1248                 }
1249
1250                 f->Write( data, size );
1251
1252                 uw /= 2;
1253                 uh /= 2;
1254                 if (uw < 1) {
1255                         uw = 1;
1256                 }
1257                 if (uh < 1) {
1258                         uh = 1;
1259                 }
1260         }
1261
1262         if (data != NULL) {
1263                 R_StaticFree( data );
1264         }
1265
1266         fileSystem->CloseFile( f );
1267 }
1268
1269 /*
1270 ================
1271 ShouldImageBePartialCached
1272
1273 Returns true if there is a precompressed image, and it is large enough
1274 to be worth caching
1275 ================
1276 */
1277 bool idImage::ShouldImageBePartialCached() {
1278         if ( !glConfig.textureCompressionAvailable ) {
1279                 return false;
1280         }
1281
1282         if ( !globalImages->image_useCache.GetBool() ) {
1283                 return false;
1284         }
1285
1286         // the allowDownSize flag does double-duty as don't-partial-load
1287         if ( !allowDownSize ) {
1288                 return false;
1289         }
1290
1291         if ( globalImages->image_cacheMinK.GetInteger() <= 0 ) {
1292                 return false;
1293         }
1294
1295         // if we are doing a copyFiles, make sure the original images are referenced
1296         if ( fileSystem->PerformingCopyFiles() ) {
1297                 return false;
1298         }
1299
1300         char    filename[MAX_IMAGE_NAME];
1301         ImageProgramStringToCompressedFileName( imgName, filename );
1302
1303         // get the file timestamp
1304         fileSystem->ReadFile( filename, NULL, &timestamp );
1305
1306         if ( timestamp == FILE_NOT_FOUND_TIMESTAMP ) {
1307                 return false;
1308         }
1309
1310         // open it and get the file size
1311         idFile *f;
1312
1313         f = fileSystem->OpenFileRead( filename );
1314         if ( !f ) {
1315                 return false;
1316         }
1317
1318         int     len = f->Length();
1319         fileSystem->CloseFile( f );
1320
1321         if ( len <= globalImages->image_cacheMinK.GetInteger() * 1024 ) {
1322                 return false;
1323         }
1324
1325         // we do want to do a partial load
1326         return true;
1327 }
1328
1329 /*
1330 ================
1331 CheckPrecompressedImage
1332
1333 If fullLoad is false, only the small mip levels of the image will be loaded
1334 ================
1335 */
1336 bool idImage::CheckPrecompressedImage( bool fullLoad ) {
1337         if ( !glConfig.isInitialized || !glConfig.textureCompressionAvailable ) {
1338                 return false;
1339         }
1340
1341 #if 1 // ( _D3XP had disabled ) - Allow grabbing of DDS's from original Doom pak files
1342         // if we are doing a copyFiles, make sure the original images are referenced
1343         if ( fileSystem->PerformingCopyFiles() ) {
1344                 return false;
1345         }
1346 #endif
1347
1348         if ( depth == TD_BUMP && globalImages->image_useNormalCompression.GetInteger() != 2 ) {
1349                 return false;
1350         }
1351
1352         // god i love last minute hacks :-)
1353         if ( com_machineSpec.GetInteger() >= 1 && com_videoRam.GetInteger() >= 128 && imgName.Icmpn( "lights/", 7 ) == 0 ) {
1354                 return false;
1355         }
1356
1357         char filename[MAX_IMAGE_NAME];
1358         ImageProgramStringToCompressedFileName( imgName, filename );
1359
1360         // get the file timestamp
1361         ID_TIME_T precompTimestamp;
1362         fileSystem->ReadFile( filename, NULL, &precompTimestamp );
1363
1364
1365         if ( precompTimestamp == FILE_NOT_FOUND_TIMESTAMP ) {
1366                 return false;
1367         }
1368
1369         if ( !generatorFunction && timestamp != FILE_NOT_FOUND_TIMESTAMP ) {
1370                 if ( precompTimestamp < timestamp ) {
1371                         // The image has changed after being precompressed
1372                         return false;
1373                 }
1374         }
1375
1376         timestamp = precompTimestamp;
1377
1378         // open it and just read the header
1379         idFile *f;
1380
1381         f = fileSystem->OpenFileRead( filename );
1382         if ( !f ) {
1383                 return false;
1384         }
1385
1386         int     len = f->Length();
1387         if ( len < sizeof( ddsFileHeader_t ) ) {
1388                 fileSystem->CloseFile( f );
1389                 return false;
1390         }
1391
1392         if ( !fullLoad && len > globalImages->image_cacheMinK.GetInteger() * 1024 ) {
1393                 len = globalImages->image_cacheMinK.GetInteger() * 1024;
1394         }
1395
1396         byte *data = (byte *)R_StaticAlloc( len );
1397
1398         f->Read( data, len );
1399
1400         fileSystem->CloseFile( f );
1401
1402         unsigned long magic = LittleLong( *(unsigned long *)data );
1403         ddsFileHeader_t *_header = (ddsFileHeader_t *)(data + 4);
1404         int ddspf_dwFlags = LittleLong( _header->ddspf.dwFlags );
1405
1406         if ( magic != DDS_MAKEFOURCC('D', 'D', 'S', ' ')) {
1407                 common->Printf( "CheckPrecompressedImage( %s ): magic != 'DDS '\n", imgName.c_str() );
1408                 R_StaticFree( data );
1409                 return false;
1410         }
1411
1412         // if we don't support color index textures, we must load the full image
1413         // should we just expand the 256 color image to 32 bit for upload?
1414         if ( ddspf_dwFlags & DDSF_ID_INDEXCOLOR && !glConfig.sharedTexturePaletteAvailable ) {
1415                 R_StaticFree( data );
1416                 return false;
1417         }
1418
1419         // upload all the levels
1420         UploadPrecompressedImage( data, len );
1421
1422         R_StaticFree( data );
1423
1424         return true;
1425 }
1426
1427 /*
1428 ===================
1429 UploadPrecompressedImage
1430
1431 This can be called by the front end during nromal loading,
1432 or by the backend after a background read of the file
1433 has completed
1434 ===================
1435 */
1436 void idImage::UploadPrecompressedImage( byte *data, int len ) {
1437         ddsFileHeader_t *header = (ddsFileHeader_t *)(data + 4);
1438
1439         // ( not byte swapping dwReserved1 dwReserved2 )
1440         header->dwSize = LittleLong( header->dwSize );
1441         header->dwFlags = LittleLong( header->dwFlags );
1442         header->dwHeight = LittleLong( header->dwHeight );
1443         header->dwWidth = LittleLong( header->dwWidth );
1444         header->dwPitchOrLinearSize = LittleLong( header->dwPitchOrLinearSize );
1445         header->dwDepth = LittleLong( header->dwDepth );
1446         header->dwMipMapCount = LittleLong( header->dwMipMapCount );
1447         header->dwCaps1 = LittleLong( header->dwCaps1 );
1448         header->dwCaps2 = LittleLong( header->dwCaps2 );
1449
1450         header->ddspf.dwSize = LittleLong( header->ddspf.dwSize );
1451         header->ddspf.dwFlags = LittleLong( header->ddspf.dwFlags );
1452         header->ddspf.dwFourCC = LittleLong( header->ddspf.dwFourCC );
1453         header->ddspf.dwRGBBitCount = LittleLong( header->ddspf.dwRGBBitCount );
1454         header->ddspf.dwRBitMask = LittleLong( header->ddspf.dwRBitMask );
1455         header->ddspf.dwGBitMask = LittleLong( header->ddspf.dwGBitMask );
1456         header->ddspf.dwBBitMask = LittleLong( header->ddspf.dwBBitMask );
1457         header->ddspf.dwABitMask = LittleLong( header->ddspf.dwABitMask );
1458
1459         // generate the texture number
1460         qglGenTextures( 1, &texnum );
1461
1462         int externalFormat = 0;
1463
1464         precompressedFile = true;
1465
1466         uploadWidth = header->dwWidth;
1467         uploadHeight = header->dwHeight;
1468     if ( header->ddspf.dwFlags & DDSF_FOURCC ) {
1469         switch ( header->ddspf.dwFourCC ) {
1470         case DDS_MAKEFOURCC( 'D', 'X', 'T', '1' ):
1471                         if ( header->ddspf.dwFlags & DDSF_ALPHAPIXELS ) {
1472                                 internalFormat = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;
1473                         } else {
1474                                 internalFormat = GL_COMPRESSED_RGB_S3TC_DXT1_EXT;
1475                         }
1476             break;
1477         case DDS_MAKEFOURCC( 'D', 'X', 'T', '3' ):
1478             internalFormat = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
1479             break;
1480         case DDS_MAKEFOURCC( 'D', 'X', 'T', '5' ):
1481             internalFormat = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
1482             break;
1483                 case DDS_MAKEFOURCC( 'R', 'X', 'G', 'B' ):
1484                         internalFormat = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
1485                         break;
1486         default:
1487             common->Warning( "Invalid compressed internal format\n" );
1488             return;
1489         }
1490     } else if ( ( header->ddspf.dwFlags & DDSF_RGBA ) && header->ddspf.dwRGBBitCount == 32 ) {
1491                 externalFormat = GL_BGRA_EXT;
1492                 internalFormat = GL_RGBA8;
1493     } else if ( ( header->ddspf.dwFlags & DDSF_RGB ) && header->ddspf.dwRGBBitCount == 32 ) {
1494         externalFormat = GL_BGRA_EXT;
1495                 internalFormat = GL_RGBA8;
1496     } else if ( ( header->ddspf.dwFlags & DDSF_RGB ) && header->ddspf.dwRGBBitCount == 24 ) {
1497                 if ( header->ddspf.dwFlags & DDSF_ID_INDEXCOLOR ) { 
1498                         externalFormat = GL_COLOR_INDEX;
1499                         internalFormat = GL_COLOR_INDEX8_EXT;
1500                 } else {
1501                         externalFormat = GL_BGR_EXT;
1502                         internalFormat = GL_RGB8;
1503                 }
1504         } else if ( header->ddspf.dwRGBBitCount == 8 ) {
1505                 externalFormat = GL_ALPHA;
1506                 internalFormat = GL_ALPHA8;
1507         } else {
1508                 common->Warning( "Invalid uncompressed internal format\n" );
1509                 return;
1510         }
1511
1512         // we need the monochrome flag for the NV20 optimized path
1513         if ( header->dwFlags & DDSF_ID_MONOCHROME ) {
1514                 isMonochrome = true;
1515         }
1516
1517         type = TT_2D;                   // FIXME: we may want to support pre-compressed cube maps in the future
1518
1519         Bind();
1520
1521         int numMipmaps = 1;
1522         if ( header->dwFlags & DDSF_MIPMAPCOUNT ) {
1523                 numMipmaps = header->dwMipMapCount;
1524         }
1525
1526         int uw = uploadWidth;
1527         int uh = uploadHeight;
1528
1529         // We may skip some mip maps if we are downsizing
1530         int skipMip = 0;
1531         GetDownsize( uploadWidth, uploadHeight );
1532
1533         byte *imagedata = data + sizeof(ddsFileHeader_t) + 4;
1534
1535         for ( int i = 0 ; i < numMipmaps; i++ ) {
1536                 int size = 0;
1537                 if ( FormatIsDXT( internalFormat ) ) {
1538                         size = ( ( uw + 3 ) / 4 ) * ( ( uh + 3 ) / 4 ) *
1539                                 (internalFormat <= GL_COMPRESSED_RGBA_S3TC_DXT1_EXT ? 8 : 16);
1540                 } else {
1541                         size = uw * uh * (header->ddspf.dwRGBBitCount / 8);
1542                 }
1543
1544                 if ( uw > uploadWidth || uh > uploadHeight ) {
1545                         skipMip++;
1546                 } else {
1547                         if ( FormatIsDXT( internalFormat ) ) {
1548                                 qglCompressedTexImage2DARB( GL_TEXTURE_2D, i - skipMip, internalFormat, uw, uh, 0, size, imagedata );
1549                         } else {
1550                                 qglTexImage2D( GL_TEXTURE_2D, i - skipMip, internalFormat, uw, uh, 0, externalFormat, GL_UNSIGNED_BYTE, imagedata );
1551                         }
1552                 }
1553
1554                 imagedata += size;
1555                 uw /= 2;
1556                 uh /= 2;
1557                 if (uw < 1) {
1558                         uw = 1;
1559                 }
1560                 if (uh < 1) {
1561                         uh = 1;
1562                 }
1563         }
1564
1565         SetImageFilterAndRepeat();
1566 }
1567
1568 /*
1569 ===============
1570 ActuallyLoadImage
1571
1572 Absolutely every image goes through this path
1573 On exit, the idImage will have a valid OpenGL texture number that can be bound
1574 ===============
1575 */
1576 void    idImage::ActuallyLoadImage( bool checkForPrecompressed, bool fromBackEnd ) {
1577         int             width, height;
1578         byte    *pic;
1579
1580         // this is the ONLY place generatorFunction will ever be called
1581         if ( generatorFunction ) {
1582                 generatorFunction( this );
1583                 return;
1584         }
1585
1586         // if we are a partial image, we are only going to load from a compressed file
1587         if ( isPartialImage ) {
1588                 if ( CheckPrecompressedImage( false ) ) {
1589                         return;
1590                 }
1591                 // this is an error -- the partial image failed to load
1592                 MakeDefault();
1593                 return;
1594         }
1595
1596         //
1597         // load the image from disk
1598         //
1599         if ( cubeFiles != CF_2D ) {
1600                 byte    *pics[6];
1601
1602                 // we don't check for pre-compressed cube images currently
1603                 R_LoadCubeImages( imgName, cubeFiles, pics, &width, &timestamp );
1604
1605                 if ( pics[0] == NULL ) {
1606                         common->Warning( "Couldn't load cube image: %s", imgName.c_str() );
1607                         MakeDefault();
1608                         return;
1609                 }
1610
1611                 GenerateCubeImage( (const byte **)pics, width, filter, allowDownSize, depth );
1612                 precompressedFile = false;
1613
1614                 for ( int i = 0 ; i < 6 ; i++ ) {
1615                         if ( pics[i] ) {
1616                                 R_StaticFree( pics[i] );
1617                         }
1618                 }
1619         } else {
1620                 // see if we have a pre-generated image file that is
1621                 // already image processed and compressed
1622                 if ( checkForPrecompressed && globalImages->image_usePrecompressedTextures.GetBool() ) {
1623                         if ( CheckPrecompressedImage( true ) ) {
1624                                 // we got the precompressed image
1625                                 return;
1626                         }
1627                         // fall through to load the normal image
1628                 }
1629
1630                 R_LoadImageProgram( imgName, &pic, &width, &height, &timestamp, &depth );
1631
1632                 if ( pic == NULL ) {
1633                         common->Warning( "Couldn't load image: %s", imgName.c_str() );
1634                         MakeDefault();
1635                         return;
1636                 }
1637 /*
1638                 // swap the red and alpha for rxgb support
1639                 // do this even on tga normal maps so we only have to use
1640                 // one fragment program
1641                 // if the image is precompressed ( either in palletized mode or true rxgb mode )
1642                 // then it is loaded above and the swap never happens here
1643                 if ( depth == TD_BUMP && globalImages->image_useNormalCompression.GetInteger() != 1 ) {
1644                         for ( int i = 0; i < width * height * 4; i += 4 ) {
1645                                 pic[ i + 3 ] = pic[ i ];
1646                                 pic[ i ] = 0;
1647                         }
1648                 }
1649 */
1650                 // build a hash for checking duplicate image files
1651                 // NOTE: takes about 10% of image load times (SD)
1652                 // may not be strictly necessary, but some code uses it, so let's leave it in
1653                 imageHash = MD4_BlockChecksum( pic, width * height * 4 );
1654
1655                 GenerateImage( pic, width, height, filter, allowDownSize, repeat, depth );
1656                 timestamp = timestamp;
1657                 precompressedFile = false;
1658
1659                 R_StaticFree( pic );
1660
1661                 // write out the precompressed version of this file if needed
1662                 WritePrecompressedImage();
1663         }
1664 }
1665
1666 //=========================================================================================================
1667
1668 /*
1669 ===============
1670 PurgeImage
1671 ===============
1672 */
1673 void idImage::PurgeImage() {
1674         if ( texnum != TEXTURE_NOT_LOADED ) {
1675                 // sometimes is NULL when exiting with an error
1676                 if ( qglDeleteTextures ) {
1677                         qglDeleteTextures( 1, &texnum );        // this should be the ONLY place it is ever called!
1678                 }
1679                 texnum = TEXTURE_NOT_LOADED;
1680         }
1681
1682         // clear all the current binding caches, so the next bind will do a real one
1683         for ( int i = 0 ; i < MAX_MULTITEXTURE_UNITS ; i++ ) {
1684                 backEnd.glState.tmu[i].current2DMap = -1;
1685                 backEnd.glState.tmu[i].current3DMap = -1;
1686                 backEnd.glState.tmu[i].currentCubeMap = -1;
1687         }
1688 }
1689
1690 /*
1691 ==============
1692 Bind
1693
1694 Automatically enables 2D mapping, cube mapping, or 3D texturing if needed
1695 ==============
1696 */
1697 void idImage::Bind() {
1698         if ( tr.logFile ) {
1699                 RB_LogComment( "idImage::Bind( %s )\n", imgName.c_str() );
1700         }
1701
1702         // if this is an image that we are caching, move it to the front of the LRU chain
1703         if ( partialImage ) {
1704                 if ( cacheUsageNext ) {
1705                         // unlink from old position
1706                         cacheUsageNext->cacheUsagePrev = cacheUsagePrev;
1707                         cacheUsagePrev->cacheUsageNext = cacheUsageNext;
1708                 }
1709                 // link in at the head of the list
1710                 cacheUsageNext = globalImages->cacheLRU.cacheUsageNext;
1711                 cacheUsagePrev = &globalImages->cacheLRU;
1712
1713                 cacheUsageNext->cacheUsagePrev = this;
1714                 cacheUsagePrev->cacheUsageNext = this;
1715         }
1716
1717         // load the image if necessary (FIXME: not SMP safe!)
1718         if ( texnum == TEXTURE_NOT_LOADED ) {
1719                 if ( partialImage ) {
1720                         // if we have a partial image, go ahead and use that
1721                         this->partialImage->Bind();
1722
1723                         // start a background load of the full thing if it isn't already in the queue
1724                         if ( !backgroundLoadInProgress ) {
1725                                 StartBackgroundImageLoad();
1726                         }
1727                         return;
1728                 }
1729
1730                 // load the image on demand here, which isn't our normal game operating mode
1731                 ActuallyLoadImage( true, true );        // check for precompressed, load is from back end
1732         }
1733
1734
1735         // bump our statistic counters
1736         frameUsed = backEnd.frameCount;
1737         bindCount++;
1738
1739         tmu_t                   *tmu = &backEnd.glState.tmu[backEnd.glState.currenttmu];
1740
1741         // enable or disable apropriate texture modes
1742         if ( tmu->textureType != type && ( backEnd.glState.currenttmu < glConfig.maxTextureUnits ) ) {
1743                 if ( tmu->textureType == TT_CUBIC ) {
1744                         qglDisable( GL_TEXTURE_CUBE_MAP_EXT );
1745                 } else if ( tmu->textureType == TT_3D ) {
1746                         qglDisable( GL_TEXTURE_3D );
1747                 } else if ( tmu->textureType == TT_2D ) {
1748                         qglDisable( GL_TEXTURE_2D );
1749                 }
1750
1751                 if ( type == TT_CUBIC ) {
1752                         qglEnable( GL_TEXTURE_CUBE_MAP_EXT );
1753                 } else if ( type == TT_3D ) {
1754                         qglEnable( GL_TEXTURE_3D );
1755                 } else if ( type == TT_2D ) {
1756                         qglEnable( GL_TEXTURE_2D );
1757                 }
1758                 tmu->textureType = type;
1759         }
1760
1761         // bind the texture
1762         if ( type == TT_2D ) {
1763                 if ( tmu->current2DMap != texnum ) {
1764                         tmu->current2DMap = texnum;
1765                         qglBindTexture( GL_TEXTURE_2D, texnum );
1766                 }
1767         } else if ( type == TT_CUBIC ) {
1768                 if ( tmu->currentCubeMap != texnum ) {
1769                         tmu->currentCubeMap = texnum;
1770                         qglBindTexture( GL_TEXTURE_CUBE_MAP_EXT, texnum );
1771                 }
1772         } else if ( type == TT_3D ) {
1773                 if ( tmu->current3DMap != texnum ) {
1774                         tmu->current3DMap = texnum;
1775                         qglBindTexture( GL_TEXTURE_3D, texnum );
1776                 }
1777         }
1778
1779         if ( com_purgeAll.GetBool() ) {
1780                 GLclampf priority = 1.0f;
1781                 qglPrioritizeTextures( 1, &texnum, &priority );
1782         }
1783 }
1784
1785 /*
1786 ==============
1787 BindFragment
1788
1789 Fragment programs explicitly say which type of map they want, so we don't need to
1790 do any enable / disable changes
1791 ==============
1792 */
1793 void idImage::BindFragment() {
1794         if ( tr.logFile ) {
1795                 RB_LogComment( "idImage::BindFragment %s )\n", imgName.c_str() );
1796         }
1797
1798         // if this is an image that we are caching, move it to the front of the LRU chain
1799         if ( partialImage ) {
1800                 if ( cacheUsageNext ) {
1801                         // unlink from old position
1802                         cacheUsageNext->cacheUsagePrev = cacheUsagePrev;
1803                         cacheUsagePrev->cacheUsageNext = cacheUsageNext;
1804                 }
1805                 // link in at the head of the list
1806                 cacheUsageNext = globalImages->cacheLRU.cacheUsageNext;
1807                 cacheUsagePrev = &globalImages->cacheLRU;
1808
1809                 cacheUsageNext->cacheUsagePrev = this;
1810                 cacheUsagePrev->cacheUsageNext = this;
1811         }
1812
1813         // load the image if necessary (FIXME: not SMP safe!)
1814         if ( texnum == TEXTURE_NOT_LOADED ) {
1815                 if ( partialImage ) {
1816                         // if we have a partial image, go ahead and use that
1817                         this->partialImage->BindFragment();
1818
1819                         // start a background load of the full thing if it isn't already in the queue
1820                         if ( !backgroundLoadInProgress ) {
1821                                 StartBackgroundImageLoad();
1822                         }
1823                         return;
1824                 }
1825
1826                 // load the image on demand here, which isn't our normal game operating mode
1827                 ActuallyLoadImage( true, true );        // check for precompressed, load is from back end
1828         }
1829
1830
1831         // bump our statistic counters
1832         frameUsed = backEnd.frameCount;
1833         bindCount++;
1834
1835         // bind the texture
1836         if ( type == TT_2D ) {
1837                 qglBindTexture( GL_TEXTURE_2D, texnum );
1838         } else if ( type == TT_RECT ) {
1839                 qglBindTexture( GL_TEXTURE_RECTANGLE_NV, texnum );
1840         } else if ( type == TT_CUBIC ) {
1841                 qglBindTexture( GL_TEXTURE_CUBE_MAP_EXT, texnum );
1842         } else if ( type == TT_3D ) {
1843                 qglBindTexture( GL_TEXTURE_3D, texnum );
1844         }
1845 }
1846
1847
1848 /*
1849 ====================
1850 CopyFramebuffer
1851 ====================
1852 */
1853 void idImage::CopyFramebuffer( int x, int y, int imageWidth, int imageHeight, bool useOversizedBuffer ) {
1854         Bind();
1855
1856         if ( cvarSystem->GetCVarBool( "g_lowresFullscreenFX" ) ) {
1857                 imageWidth = 512;
1858                 imageHeight = 512;
1859         }
1860
1861         // if the size isn't a power of 2, the image must be increased in size
1862         int     potWidth, potHeight;
1863
1864         potWidth = MakePowerOfTwo( imageWidth );
1865         potHeight = MakePowerOfTwo( imageHeight );
1866
1867         GetDownsize( imageWidth, imageHeight );
1868         GetDownsize( potWidth, potHeight );
1869
1870         qglReadBuffer( GL_BACK );
1871
1872         // only resize if the current dimensions can't hold it at all,
1873         // otherwise subview renderings could thrash this
1874         if ( ( useOversizedBuffer && ( uploadWidth < potWidth || uploadHeight < potHeight ) )
1875                 || ( !useOversizedBuffer && ( uploadWidth != potWidth || uploadHeight != potHeight ) ) ) {
1876                 uploadWidth = potWidth;
1877                 uploadHeight = potHeight;
1878                 if ( potWidth == imageWidth && potHeight == imageHeight ) {
1879                         qglCopyTexImage2D( GL_TEXTURE_2D, 0, GL_RGB8, x, y, imageWidth, imageHeight, 0 );
1880                 } else {
1881                         byte    *junk;
1882                         // we need to create a dummy image with power of two dimensions,
1883                         // then do a qglCopyTexSubImage2D of the data we want
1884                         // this might be a 16+ meg allocation, which could fail on _alloca
1885                         junk = (byte *)Mem_Alloc( potWidth * potHeight * 4 );
1886                         memset( junk, 0, potWidth * potHeight * 4 );            //!@#
1887 #if 0 // Disabling because it's unnecessary and introduces a green strip on edge of _currentRender
1888                         for ( int i = 0 ; i < potWidth * potHeight * 4 ; i+=4 ) {
1889                                 junk[i+1] = 255;
1890                         }
1891 #endif
1892                         qglTexImage2D( GL_TEXTURE_2D, 0, GL_RGB, potWidth, potHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, junk );
1893                         Mem_Free( junk );
1894
1895                         qglCopyTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0, x, y, imageWidth, imageHeight );
1896                 }
1897         } else {
1898                 // otherwise, just subimage upload it so that drivers can tell we are going to be changing
1899                 // it and don't try and do a texture compression or some other silliness
1900                 qglCopyTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0, x, y, imageWidth, imageHeight );
1901         }
1902
1903         // if the image isn't a full power of two, duplicate an extra row and/or column to fix bilerps
1904         if ( imageWidth != potWidth ) {
1905                 qglCopyTexSubImage2D( GL_TEXTURE_2D, 0, imageWidth, 0, x+imageWidth-1, y, 1, imageHeight );
1906         }
1907         if ( imageHeight != potHeight ) {
1908                 qglCopyTexSubImage2D( GL_TEXTURE_2D, 0, 0, imageHeight, x, y+imageHeight-1, imageWidth, 1 );
1909         }
1910
1911         qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
1912         qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
1913
1914         qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
1915         qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
1916
1917         backEnd.c_copyFrameBuffer++;
1918 }
1919
1920 /*
1921 ====================
1922 CopyDepthbuffer
1923
1924 This should just be part of copyFramebuffer once we have a proper image type field
1925 ====================
1926 */
1927 void idImage::CopyDepthbuffer( int x, int y, int imageWidth, int imageHeight ) {
1928         Bind();
1929
1930         // if the size isn't a power of 2, the image must be increased in size
1931         int     potWidth, potHeight;
1932
1933         potWidth = MakePowerOfTwo( imageWidth );
1934         potHeight = MakePowerOfTwo( imageHeight );
1935
1936         if ( uploadWidth != potWidth || uploadHeight != potHeight ) {
1937                 uploadWidth = potWidth;
1938                 uploadHeight = potHeight;
1939                 if ( potWidth == imageWidth && potHeight == imageHeight ) {
1940                         qglCopyTexImage2D( GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, x, y, imageWidth, imageHeight, 0 );
1941                 } else {
1942                         // we need to create a dummy image with power of two dimensions,
1943                         // then do a qglCopyTexSubImage2D of the data we want
1944                         qglTexImage2D( GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, potWidth, potHeight, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, NULL );
1945                         qglCopyTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0, x, y, imageWidth, imageHeight );
1946                 }
1947         } else {
1948                 // otherwise, just subimage upload it so that drivers can tell we are going to be changing
1949                 // it and don't try and do a texture compression or some other silliness
1950                 qglCopyTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0, x, y, imageWidth, imageHeight );
1951         }
1952
1953 //      qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
1954 //      qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
1955
1956         qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
1957         qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
1958 }
1959
1960 /*
1961 =============
1962 RB_UploadScratchImage
1963
1964 if rows = cols * 6, assume it is a cube map animation
1965 =============
1966 */
1967 void idImage::UploadScratch( const byte *data, int cols, int rows ) {
1968         int                     i;
1969
1970         // if rows = cols * 6, assume it is a cube map animation
1971         if ( rows == cols * 6 ) {
1972                 if ( type != TT_CUBIC ) {
1973                         type = TT_CUBIC;
1974                         uploadWidth = -1;       // for a non-sub upload
1975                 }
1976
1977                 Bind();
1978
1979                 rows /= 6;
1980                 // if the scratchImage isn't in the format we want, specify it as a new texture
1981                 if ( cols != uploadWidth || rows != uploadHeight ) {
1982                         uploadWidth = cols;
1983                         uploadHeight = rows;
1984
1985                         // upload the base level
1986                         for ( i = 0 ; i < 6 ; i++ ) {
1987                                 qglTexImage2D( GL_TEXTURE_CUBE_MAP_POSITIVE_X_EXT+i, 0, GL_RGB8, cols, rows, 0, 
1988                                         GL_RGBA, GL_UNSIGNED_BYTE, data + cols*rows*4*i );
1989                         }
1990                 } else {
1991                         // otherwise, just subimage upload it so that drivers can tell we are going to be changing
1992                         // it and don't try and do a texture compression
1993                         for ( i = 0 ; i < 6 ; i++ ) {
1994                                 qglTexSubImage2D( GL_TEXTURE_CUBE_MAP_POSITIVE_X_EXT+i, 0, 0, 0, cols, rows, 
1995                                         GL_RGBA, GL_UNSIGNED_BYTE, data + cols*rows*4*i );
1996                         }
1997                 }
1998                 qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
1999                 qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
2000                 // no other clamp mode makes sense
2001                 qglTexParameteri(GL_TEXTURE_CUBE_MAP_EXT, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
2002                 qglTexParameteri(GL_TEXTURE_CUBE_MAP_EXT, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
2003         } else {
2004                 // otherwise, it is a 2D image
2005                 if ( type != TT_2D ) {
2006                         type = TT_2D;
2007                         uploadWidth = -1;       // for a non-sub upload
2008                 }
2009
2010                 Bind();
2011
2012                 // if the scratchImage isn't in the format we want, specify it as a new texture
2013                 if ( cols != uploadWidth || rows != uploadHeight ) {
2014                         uploadWidth = cols;
2015                         uploadHeight = rows;
2016                         qglTexImage2D( GL_TEXTURE_2D, 0, GL_RGB8, cols, rows, 0, GL_RGBA, GL_UNSIGNED_BYTE, data );
2017                 } else {
2018                         // otherwise, just subimage upload it so that drivers can tell we are going to be changing
2019                         // it and don't try and do a texture compression
2020                         qglTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0, cols, rows, GL_RGBA, GL_UNSIGNED_BYTE, data );
2021                 }
2022                 qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
2023                 qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
2024                 // these probably should be clamp, but we have a lot of issues with editor
2025                 // geometry coming out with texcoords slightly off one side, resulting in
2026                 // a smear across the entire polygon
2027 #if 1
2028                 qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT );
2029                 qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT );        
2030 #else
2031                 qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
2032                 qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE ); 
2033 #endif
2034         }
2035 }
2036
2037
2038 void idImage::SetClassification( int tag ) {
2039         classification = tag;
2040 }
2041
2042 /*
2043 ==================
2044 StorageSize
2045 ==================
2046 */
2047 int idImage::StorageSize() const {
2048         int             baseSize;
2049
2050         if ( texnum == TEXTURE_NOT_LOADED ) {
2051                 return 0;
2052         }
2053
2054         switch ( type ) {
2055         default:
2056         case TT_2D:
2057                 baseSize = uploadWidth*uploadHeight;
2058                 break;
2059         case TT_3D:
2060                 baseSize = uploadWidth*uploadHeight*uploadDepth;
2061                 break;
2062         case TT_CUBIC:
2063                 baseSize = 6 * uploadWidth*uploadHeight;
2064                 break;
2065         }
2066
2067         baseSize *= BitsForInternalFormat( internalFormat );
2068
2069         baseSize /= 8;
2070
2071         // account for mip mapping
2072         baseSize = baseSize * 4 / 3;
2073
2074         return baseSize;
2075 }
2076
2077 /*
2078 ==================
2079 Print
2080 ==================
2081 */
2082 void idImage::Print() const {
2083         if ( precompressedFile ) {
2084                 common->Printf( "P" );
2085         } else if ( generatorFunction ) {
2086                 common->Printf( "F" );
2087         } else {
2088                 common->Printf( " " );
2089         }
2090
2091         switch ( type ) {
2092         case TT_2D:
2093                 common->Printf( " " );
2094                 break;
2095         case TT_3D:
2096                 common->Printf( "3" );
2097                 break;
2098         case TT_CUBIC:
2099                 common->Printf( "C" );
2100                 break;
2101         case TT_RECT:
2102                 common->Printf( "R" );
2103                 break;
2104         default:
2105                 common->Printf( "<BAD TYPE:%i>", type );
2106                 break;
2107         }
2108
2109         common->Printf( "%4i %4i ",     uploadWidth, uploadHeight );
2110
2111         switch( filter ) {
2112         case TF_DEFAULT:
2113                 common->Printf( "dflt " );
2114                 break;
2115         case TF_LINEAR:
2116                 common->Printf( "linr " );
2117                 break;
2118         case TF_NEAREST:
2119                 common->Printf( "nrst " );
2120                 break;
2121         default:
2122                 common->Printf( "<BAD FILTER:%i>", filter );
2123                 break;
2124         }
2125
2126         switch ( internalFormat ) {
2127         case GL_INTENSITY8:
2128         case 1:
2129                 common->Printf( "I     " );
2130                 break;
2131         case 2:
2132         case GL_LUMINANCE8_ALPHA8:
2133                 common->Printf( "LA    " );
2134                 break;
2135         case 3:
2136                 common->Printf( "RGB   " );
2137                 break;
2138         case 4:
2139                 common->Printf( "RGBA  " );
2140                 break;
2141         case GL_LUMINANCE8:
2142                 common->Printf( "L     " );
2143                 break;
2144         case GL_ALPHA8:
2145                 common->Printf( "A     " );
2146                 break;
2147         case GL_RGBA8:
2148                 common->Printf( "RGBA8 " );
2149                 break;
2150         case GL_RGB8:
2151                 common->Printf( "RGB8  " );
2152                 break;
2153         case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
2154                 common->Printf( "DXT1  " );
2155                 break;
2156         case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
2157                 common->Printf( "DXT1A " );
2158                 break;
2159         case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT:
2160                 common->Printf( "DXT3  " );
2161                 break;
2162         case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT:
2163                 common->Printf( "DXT5  " );
2164                 break;
2165         case GL_RGBA4:
2166                 common->Printf( "RGBA4 " );
2167                 break;
2168         case GL_RGB5:
2169                 common->Printf( "RGB5  " );
2170                 break;
2171         case GL_COLOR_INDEX8_EXT:
2172                 common->Printf( "CI8   " );
2173                 break;
2174         case GL_COLOR_INDEX:
2175                 common->Printf( "CI    " );
2176                 break;
2177         case GL_COMPRESSED_RGB_ARB:
2178                 common->Printf( "RGBC  " );
2179                 break;
2180         case GL_COMPRESSED_RGBA_ARB:
2181                 common->Printf( "RGBAC " );
2182                 break;
2183         case 0:
2184                 common->Printf( "      " );
2185                 break;
2186         default:
2187                 common->Printf( "<BAD FORMAT:%i>", internalFormat );
2188                 break;
2189         }
2190
2191         switch ( repeat ) {
2192         case TR_REPEAT:
2193                 common->Printf( "rept " );
2194                 break;
2195         case TR_CLAMP_TO_ZERO:
2196                 common->Printf( "zero " );
2197                 break;
2198         case TR_CLAMP_TO_ZERO_ALPHA:
2199                 common->Printf( "azro " );
2200                 break;
2201         case TR_CLAMP:
2202                 common->Printf( "clmp " );
2203                 break;
2204         default:
2205                 common->Printf( "<BAD REPEAT:%i>", repeat );
2206                 break;
2207         }
2208         
2209         common->Printf( "%4ik ", StorageSize() / 1024 );
2210
2211         common->Printf( " %s\n", imgName.c_str() );
2212 }