summaryrefslogtreecommitdiff
path: root/source/gl_images.c
diff options
context:
space:
mode:
Diffstat (limited to 'source/gl_images.c')
-rw-r--r--source/gl_images.c1150
1 files changed, 1150 insertions, 0 deletions
diff --git a/source/gl_images.c b/source/gl_images.c
new file mode 100644
index 0000000..c3c33e5
--- /dev/null
+++ b/source/gl_images.c
@@ -0,0 +1,1150 @@
+/*
+Copyright (C) 2003-2006 Andrey Nazarov
+Copyright (C) 1997-2001 Id Software, Inc.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "gl_local.h"
+
+image_t *r_notexture;
+image_t *r_particletexture;
+image_t *r_beamtexture;
+image_t *r_dlightTex;
+image_t *r_whiteimage;
+
+int gl_filter_min;
+int gl_filter_max;
+float gl_filter_anisotropy;
+int gl_tex_alpha_format;
+int gl_tex_solid_format;
+
+static int upload_width;
+static int upload_height;
+static image_t *upload_image;
+bspTexinfo_t *upload_texinfo;
+
+static cvar_t *gl_noscrap;
+static cvar_t *gl_round_down;
+static cvar_t *gl_picmip;
+static cvar_t *gl_gamma_scale_pics;
+static cvar_t *gl_bilerp_chars;
+static cvar_t *gl_texturemode;
+static cvar_t *gl_texturesolidmode;
+static cvar_t *gl_texturealphamode;
+static cvar_t *gl_anisotropy;
+static cvar_t *gl_saturation;
+static cvar_t *gl_intensity;
+static cvar_t *gl_gamma;
+
+qboolean GL_Upload8( byte *data, int width, int height, qboolean mipmap );
+
+typedef struct {
+ char *name;
+ int minimize, maximize;
+} glmode_t;
+
+static glmode_t filterModes[] = {
+ { "GL_NEAREST", GL_NEAREST, GL_NEAREST },
+ { "GL_LINEAR", GL_LINEAR, GL_LINEAR },
+ { "GL_NEAREST_MIPMAP_NEAREST", GL_NEAREST_MIPMAP_NEAREST, GL_NEAREST },
+ { "GL_LINEAR_MIPMAP_NEAREST", GL_LINEAR_MIPMAP_NEAREST, GL_LINEAR },
+ { "GL_NEAREST_MIPMAP_LINEAR", GL_NEAREST_MIPMAP_LINEAR, GL_NEAREST },
+ { "GL_LINEAR_MIPMAP_LINEAR", GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR }
+};
+
+static int numFilterModes = sizeof( filterModes ) / sizeof( filterModes[0] );
+
+typedef struct {
+ char *name;
+ int mode;
+} gltmode_t;
+
+static gltmode_t alphaModes[] = {
+ { "default", 4 },
+ { "GL_RGBA", GL_RGBA },
+ { "GL_RGBA8", GL_RGBA8 },
+ { "GL_RGB5_A1", GL_RGB5_A1 },
+ { "GL_RGBA4", GL_RGBA4 },
+ { "GL_RGBA2", GL_RGBA2 }
+};
+
+static int numAlphaModes = sizeof( alphaModes ) / sizeof( alphaModes[0] );
+
+static gltmode_t solidModes[] = {
+ { "default", 4 },
+ { "GL_RGB", GL_RGB },
+ { "GL_RGB8", GL_RGB8 },
+ { "GL_RGB5", GL_RGB5 },
+ { "GL_RGB4", GL_RGB4 },
+ { "GL_R3_G3_B2", GL_R3_G3_B2 },
+ { "GL_LUMINANCE", GL_LUMINANCE },
+#ifdef GL_RGB2_EXT
+ { "GL_RGB2", GL_RGB2_EXT }
+#endif
+};
+
+static int numSolidModes = sizeof( solidModes ) / sizeof( solidModes[0] );
+
+static void gl_texturemode_changed( cvar_t *self ) {
+ int i;
+ image_t *image;
+
+ for( i = 0; i < numFilterModes ; i++ ) {
+ if( !Q_stricmp( filterModes[i].name, self->string ) )
+ break;
+ }
+
+ if( i == numFilterModes ) {
+ Com_WPrintf( "Bad texture filter name\n" );
+ cvar.Set( "gl_texturemode", "GL_LINEAR_MIPMAP_NEAREST" );
+ gl_filter_min = GL_LINEAR_MIPMAP_NEAREST;
+ gl_filter_max = GL_LINEAR;
+ } else {
+ gl_filter_min = filterModes[i].minimize;
+ gl_filter_max = filterModes[i].maximize;
+ }
+
+ // change all the existing mipmap texture objects
+ for( i = 0, image = r_images; i < r_numImages; i++, image++ ) {
+ if( image->type == it_wall || image->type == it_skin ) {
+ GL_BindTexture( image->texnum );
+ qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
+ gl_filter_min );
+ qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
+ gl_filter_max );
+ }
+ }
+}
+
+static void gl_anisotropy_changed( cvar_t *self ) {
+ int i;
+ image_t *image;
+
+ if( gl_config.maxAnisotropy < 2 ) {
+ return;
+ }
+
+ gl_filter_anisotropy = self->value;
+ clamp( gl_filter_anisotropy, 1, gl_config.maxAnisotropy );
+
+ // change all the existing mipmap texture objects
+ for( i = 0, image = r_images; i < r_numImages; i++, image++ ) {
+ if( image->type == it_wall || image->type == it_skin ) {
+ GL_BindTexture( image->texnum );
+ qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT,
+ gl_filter_anisotropy );
+ }
+ }
+}
+
+static void gl_bilerp_chars_changed( cvar_t *self ) {
+ int i;
+ image_t *image;
+ GLfloat param = self->integer ? GL_LINEAR : GL_NEAREST;
+
+ // change all the existing charset texture objects
+ for( i = 0, image = r_images; i < r_numImages; i++, image++ ) {
+ if( image->type == it_charset ) {
+ GL_BindTexture( image->texnum );
+ qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, param );
+ qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, param );
+ }
+ }
+}
+
+
+/*
+===============
+GL_TextureAlphaMode
+===============
+*/
+static void GL_TextureAlphaMode( void ) {
+ int i;
+
+ for( i = 0; i < numAlphaModes; i++ ) {
+ if( !Q_stricmp( alphaModes[i].name, gl_texturealphamode->string ) )
+ break;
+ }
+
+ if( i == numAlphaModes ) {
+ Com_Printf( "Bad texture alpha mode name %s\n",
+ gl_texturealphamode->string );
+ cvar.Set( "gl_texturealphamode", "default" );
+ gl_tex_alpha_format = alphaModes[0].mode;
+ return;
+ }
+
+ gl_tex_alpha_format = alphaModes[i].mode;
+}
+
+/*
+===============
+GL_TextureSolidMode
+===============
+*/
+static void GL_TextureSolidMode( void ) {
+ int i;
+
+ for( i = 0; i < numSolidModes; i++ ) {
+ if( !Q_stricmp( solidModes[i].name, gl_texturesolidmode->string ) )
+ break;
+ }
+
+ if( i == numSolidModes ) {
+ Com_Printf( "Bad texture texture mode name %s\n",
+ gl_texturesolidmode->string );
+ cvar.Set( "gl_texturesolidmode", "default" );
+ gl_tex_solid_format = solidModes[0].mode;
+ return;
+ }
+
+ gl_tex_solid_format = solidModes[i].mode;
+}
+
+/*
+=============================================================================
+
+ SCRAP ALLOCATION
+
+ Allocate all the little status bar objects into a single texture
+ to crutch up inefficient hardware / drivers
+
+=============================================================================
+*/
+
+#define SCRAP_BLOCK_WIDTH 256
+#define SCRAP_BLOCK_HEIGHT 256
+
+#define SCRAP_TEXNUM ( MAX_RIMAGES + 1 )
+
+static int scrap_inuse[SCRAP_BLOCK_WIDTH];
+static byte scrap_data[SCRAP_BLOCK_WIDTH * SCRAP_BLOCK_HEIGHT];
+qboolean scrap_dirty;
+
+static qboolean Scrap_AllocBlock( int w, int h, int *s, int *t ) {
+ int i, j;
+ int x, y, maxInuse, minInuse;
+
+ x = 0; y = SCRAP_BLOCK_HEIGHT;
+ minInuse = SCRAP_BLOCK_HEIGHT;
+ for( i = 0; i < SCRAP_BLOCK_WIDTH - w; i++ ) {
+ maxInuse = 0;
+ for( j = 0; j < w; j++ ) {
+ if( scrap_inuse[ i + j ] >= minInuse ) {
+ break;
+ }
+ if( maxInuse < scrap_inuse[ i + j ] ) {
+ maxInuse = scrap_inuse[ i + j ];
+ }
+ }
+ if( j == w ) {
+ x = i;
+ y = minInuse = maxInuse;
+ }
+ }
+
+ if( y + h > SCRAP_BLOCK_HEIGHT ) {
+ return qfalse;
+ }
+
+ for( i = 0; i < w; i++ ) {
+ scrap_inuse[ x + i ] = y + h;
+ }
+
+ *s = x;
+ *t = y;
+ return qtrue;
+}
+
+void Scrap_Init( void ) {
+ int i;
+
+ for( i = 0; i < SCRAP_BLOCK_WIDTH; i++ ) {
+ scrap_inuse[i] = 0;
+ }
+}
+
+void Scrap_Upload( void ) {
+ //Com_Printf( "Scrap_Upload()\n" );
+ GL_BindTexture( SCRAP_TEXNUM );
+ GL_Upload8( scrap_data, SCRAP_BLOCK_WIDTH, SCRAP_BLOCK_HEIGHT, qfalse );
+ scrap_dirty = qfalse;
+}
+
+
+/*
+====================================================================
+
+IMAGE FLOOD FILLING
+
+====================================================================
+*/
+
+
+typedef struct {
+ short x, y;
+} floodfill_t;
+
+// must be a power of 2
+#define FLOODFILL_FIFO_SIZE 0x1000
+#define FLOODFILL_FIFO_MASK ( FLOODFILL_FIFO_SIZE - 1 )
+
+#define FLOODFILL_STEP( off, dx, dy ) \
+ do { \
+ if (pos[off] == fillcolor) { \
+ pos[off] = 255; \
+ fifo[inpt].x = x + (dx); \
+ fifo[inpt].y = y + (dy); \
+ inpt = (inpt + 1) & FLOODFILL_FIFO_MASK; \
+ } else if (pos[off] != 255) { \
+ fdc = pos[off]; \
+ } \
+ } while( 0 )
+
+/*
+=================
+Mod_FloodFillSkin
+
+Fill background pixels so mipmapping doesn't have haloes
+=================
+*/
+static void R_FloodFillSkin( byte *skin, int skinwidth, int skinheight ) {
+ byte fillcolor = *skin; // assume this is the pixel to fill
+ floodfill_t fifo[FLOODFILL_FIFO_SIZE];
+ int inpt = 0, outpt = 0;
+ int filledcolor = -1;
+ int i;
+
+ if (filledcolor == -1)
+ {
+ filledcolor = 0;
+ // attempt to find opaque black
+ for (i = 0; i < 256; ++i)
+ if (d_8to24table[i] == 255) {
+ // alpha 1.0
+ filledcolor = i;
+ break;
+ }
+ }
+
+ // can't fill to filled color or to transparent color
+ // (used as visited marker)
+ if ((fillcolor == filledcolor) || (fillcolor == 255)) {
+ return;
+ }
+
+ fifo[inpt].x = 0, fifo[inpt].y = 0;
+ inpt = (inpt + 1) & FLOODFILL_FIFO_MASK;
+
+ while (outpt != inpt) {
+ int x = fifo[outpt].x, y = fifo[outpt].y;
+ int fdc = filledcolor;
+ byte *pos = &skin[x + skinwidth * y];
+
+ outpt = (outpt + 1) & FLOODFILL_FIFO_MASK;
+
+ if (x > 0) FLOODFILL_STEP( -1, -1, 0 );
+ if (x < skinwidth - 1) FLOODFILL_STEP( 1, 1, 0 );
+ if (y > 0) FLOODFILL_STEP( -skinwidth, 0, -1 );
+ if (y < skinheight - 1) FLOODFILL_STEP( skinwidth, 0, 1 );
+
+ skin[x + skinwidth * y] = fdc;
+ }
+}
+
+//=======================================================
+
+static byte gammatable[256];
+static byte intensitytable[256];
+static byte gammaintensitytable[256];
+
+/*
+================
+GL_Saturation
+
+atu 20061129
+================
+*/
+static void GL_Saturation( byte *in, int inwidth, int inheight ) {
+ int i, c;
+ byte *p;
+ int r, g, b, min, max, mid;
+ float sat;
+
+ p = in;
+ c = inwidth * inheight;
+
+ sat = gl_saturation->value;
+ if( sat >= 1 ) {
+ return;
+ }
+ if( sat < 0 ) {
+ sat = 0;
+ }
+
+ for( i = 0; i < c; i++, p += 4 ) {
+ r = p[0];
+ g = p[1];
+ b = p[2];
+ min = max = r;
+ if ( g < min ) min = g;
+ if ( b < min ) min = b;
+ if ( g > max ) max = g;
+ if ( b > max ) max = b;
+ mid = ( min + max ) >> 1;
+ p[0] = mid + ( r - mid ) * sat;
+ p[1] = mid + ( g - mid ) * sat;
+ p[2] = mid + ( b - mid ) * sat;
+ }
+}
+
+/*
+================
+GL_LightScaleTexture
+
+Scale up the pixel values in a texture to increase the
+lighting range
+================
+*/
+static void GL_LightScaleTexture( byte *in, int inwidth, int inheight, qboolean mipmap ) {
+ int i, c;
+ byte *p;
+
+ p = in;
+ c = inwidth * inheight;
+
+
+ if( mipmap ) {
+ for( i = 0; i < c; i++, p += 4 ) {
+ p[0] = gammaintensitytable[p[0]];
+ p[1] = gammaintensitytable[p[1]];
+ p[2] = gammaintensitytable[p[2]];
+ }
+ } else {
+ for( i = 0; i < c; i++, p += 4 ) {
+ p[0] = gammatable[p[0]];
+ p[1] = gammatable[p[1]];
+ p[2] = gammatable[p[2]];
+ }
+ }
+
+
+}
+
+/*
+================
+GL_MipMap
+
+Operates in place, quartering the size of the texture
+================
+*/
+static void GL_MipMap( byte *in, int width, int height ) {
+ int i, j;
+ byte *out;
+
+ width <<= 2;
+ height >>= 1;
+ out = in;
+ for( i = 0; i < height; i++, in += width ) {
+ for( j = 0; j < width; j += 8, out += 4, in += 8 ) {
+ out[0] = ( in[0] + in[4] + in[width+0] + in[width+4] ) >> 2;
+ out[1] = ( in[1] + in[5] + in[width+1] + in[width+5] ) >> 2;
+ out[2] = ( in[2] + in[6] + in[width+2] + in[width+6] ) >> 2;
+ out[3] = ( in[3] + in[7] + in[width+3] + in[width+7] ) >> 2;
+ }
+ }
+}
+
+/*
+===============
+GL_Upload32
+===============
+*/
+qboolean GL_Upload32( byte *data, int width, int height, qboolean mipmap ) {
+ byte *scaled;
+ int scaled_width, scaled_height;
+ int i, c;
+ byte *scan;
+ int comp;
+ qboolean isalpha;
+
+ scaled_width = Q_CeilPowerOfTwo( width );
+ scaled_height = Q_CeilPowerOfTwo( height );
+
+ if( mipmap ) {
+ if( gl_round_down->integer && scaled_width > width )
+ scaled_width >>= 1;
+
+ if( gl_round_down->integer && scaled_height > height )
+ scaled_height >>= 1;
+
+ // let people sample down the world textures for speed
+ scaled_width >>= gl_picmip->integer;
+ scaled_height >>= gl_picmip->integer;
+ }
+
+ // don't ever bother with >256 textures
+ while( scaled_width > gl_static.maxTextureSize ||
+ scaled_height > gl_static.maxTextureSize )
+ {
+ scaled_width >>= 1;
+ scaled_height >>= 1;
+ }
+
+ if( scaled_width < 1 ) {
+ scaled_width = 1;
+ }
+ if( scaled_height < 1 ) {
+ scaled_height = 1;
+ }
+
+ upload_width = scaled_width;
+ upload_height = scaled_height;
+
+ // set saturation and lightscale before mipmap
+ comp = gl_tex_solid_format;
+ if( upload_image->type == it_wall &&
+ gl_saturation->value != 1 &&
+ ( !upload_texinfo ||
+ !( upload_texinfo->flags & (SURF_SKY|SURF_WARP) ) ) )
+ {
+ GL_Saturation( data, width, height );
+ if( gl_saturation->value == 0 ) {
+ comp = GL_LUMINANCE;
+ }
+ }
+
+ if( !gl_hwgamma->integer &&
+ ( mipmap || gl_gamma_scale_pics->integer ) )
+ {
+ GL_LightScaleTexture( data, width, height, mipmap );
+ }
+
+ // scan the texture for any non-255 alpha
+ c = width * height;
+ scan = data + 3;
+ isalpha = qfalse;
+ for( i = 0; i < c; i++, scan += 4 ) {
+ if( *scan != 255 ) {
+ isalpha = qtrue;
+ comp = gl_tex_alpha_format;
+ break;
+ }
+ }
+
+ if( scaled_width == width && scaled_height == height ) {
+ /* optimized case, do not reallocate */
+ scaled = data;
+ } else {
+ scaled = fs.AllocTempMem( scaled_width * scaled_height * 4 );
+ R_ResampleTexture( data, width, height, scaled,
+ scaled_width, scaled_height );
+ }
+
+
+ qglTexImage2D( GL_TEXTURE_2D, 0, comp, scaled_width, scaled_height, 0,
+ GL_RGBA, GL_UNSIGNED_BYTE, scaled );
+
+ if( mipmap ) {
+ int miplevel = 0;
+
+ while( scaled_width > 1 || scaled_height > 1 ) {
+ GL_MipMap( scaled, scaled_width, scaled_height );
+ scaled_width >>= 1;
+ scaled_height >>= 1;
+ if( scaled_width < 1 )
+ scaled_width = 1;
+ if( scaled_height < 1 )
+ scaled_height = 1;
+ miplevel++;
+ qglTexImage2D( GL_TEXTURE_2D, miplevel, comp, scaled_width,
+ scaled_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, scaled );
+ }
+ }
+
+ if( mipmap ) {
+ qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_min );
+ qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max );
+
+ if( gl_config.maxAnisotropy >= 2 ) {
+ qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT,
+ gl_filter_anisotropy );
+ }
+ } else if( upload_image->type == it_charset && !gl_bilerp_chars->integer ) {
+ qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
+ qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
+ } else {
+ qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
+ qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
+ }
+
+
+ if( scaled != data ) {
+ fs.FreeFile( scaled );
+ }
+
+ return isalpha;
+}
+
+/*
+===============
+GL_Upload8
+
+Returns has_alpha
+===============
+*/
+qboolean GL_Upload8( byte *data, int width, int height, qboolean mipmap ) {
+ byte buffer[512*256*4];
+ byte *dest;
+ int i, s;
+ int p;
+
+ s = width * height;
+ if( s > 512*256 ) {
+ Com_Error( ERR_FATAL, "GL_Upload8: %s is too large: width=%d height=%d",
+ upload_image->name, width, height );
+ }
+
+ dest = buffer;
+ for( i = 0; i < s; i++ ) {
+ p = data[i];
+ *( uint32 * )dest = d_8to24table[p];
+
+ if (p == 255) {
+ // transparent, so scan around for another color
+ // to avoid alpha fringes
+ // FIXME: do a full flood fill so mips work...
+ if (i > width && data[i-width] != 255)
+ p = data[i-width];
+ else if (i < s-width && data[i+width] != 255)
+ p = data[i+width];
+ else if (i > 0 && data[i-1] != 255)
+ p = data[i-1];
+ else if (i < s-1 && data[i+1] != 255)
+ p = data[i+1];
+ else
+ p = 0;
+ // copy rgb components
+ dest[0] = ((byte *)&d_8to24table[p])[0];
+ dest[1] = ((byte *)&d_8to24table[p])[1];
+ dest[2] = ((byte *)&d_8to24table[p])[2];
+ }
+
+ dest += 4;
+ }
+
+ return GL_Upload32( buffer, width, height, mipmap );
+
+}
+
+/*
+===============
+R_ImageForHandle
+===============
+*/
+image_t *R_ImageForHandle( qhandle_t hPic ) {
+ if( hPic < 0 || hPic >= r_numImages ) {
+ *( int * )0 = 1;
+ Com_Error( ERR_FATAL, "R_ImageForHandle: %d out of range", hPic );
+ }
+
+ return &r_images[hPic];
+}
+
+/*
+===============
+R_RegisterSkin
+===============
+*/
+qhandle_t R_RegisterSkin( const char *name ) {
+ image_t *image;
+
+ image = R_FindImage( name, it_skin );
+ if( !image ) {
+ return 0;
+ }
+
+ return ( image - r_images );
+}
+
+/*
+================
+R_RegisterPic
+================
+*/
+qhandle_t R_RegisterPic( const char *name ) {
+ image_t *image;
+ char fullname[MAX_QPATH];
+
+ if( name[0] == '*' ) {
+ image = R_FindImage( name, it_pic );
+ } else if( name[0] != '/' && name[0] != '\\' ) {
+ Com_sprintf( fullname, sizeof( fullname ), "pics/%s", name );
+ COM_DefaultExtension( fullname, ".pcx", sizeof( fullname ) );
+ image = R_FindImage( fullname, it_pic );
+ } else {
+ image = R_FindImage( name + 1, it_pic );
+ }
+
+ if( !image ) {
+ return 0;
+ }
+
+ return ( image - r_images );
+}
+
+
+
+/*
+================
+R_LoadImage
+================
+*/
+void R_LoadImage( image_t *image, byte *pic, int width, int height,
+ imagetype_t type, imageflags_t flags )
+{
+ qboolean mipmap, transparent;
+ byte *src, *dst, *ptr;
+ int i, j, s, t;
+
+ image->width = width;
+ image->height = height;
+ image->type = type;
+ upload_image = image;
+
+ // HACK: get dimensions from 8-bit texture
+ if( flags & (if_replace_wal|if_replace_pcx) ) {
+ char buffer[MAX_QPATH];
+ int length;
+ miptex_t mt;
+ pcx_t pcx;
+ fileHandle_t f;
+
+ length = strlen( image->name );
+ if( length > 4 && image->name[ length - 4 ] == '.' ) {
+ strncpy( buffer, image->name, length - 4 );
+ if( flags & if_replace_wal ) {
+ strcpy( buffer + length - 4, ".wal" );
+ fs.FOpenFile( buffer, &f, FS_MODE_READ );
+ if( f ) {
+ length = fs.Read( &mt, sizeof( mt ), f );
+ if( length == sizeof( mt ) ) {
+ image->width = LittleLong( mt.width );
+ image->height = LittleLong( mt.height );
+ }
+ fs.FCloseFile( f );
+ }
+ } else {
+ strcpy( buffer + length - 4, ".pcx" );
+ fs.FOpenFile( buffer, &f, FS_MODE_READ );
+ if( f ) {
+ length = fs.Read( &pcx, sizeof( pcx ), f );
+ if( length == sizeof( pcx ) ) {
+ image->width = LittleShort( pcx.xmax );
+ image->height = LittleShort( pcx.ymax );
+ }
+ fs.FCloseFile( f );
+ }
+ }
+ }
+ }
+
+ if( type == it_pic && ( flags & if_paletted ) &&
+ width < 64 && height < 64 && !gl_noscrap->integer )
+ {
+ if( Scrap_AllocBlock( width, height, &s, &t ) ) {
+ src = pic;
+ dst = &scrap_data[t * SCRAP_BLOCK_WIDTH + s];
+ for( i = 0; i < height; i++ ) {
+ ptr = dst;
+ for( j = 0; j < width; j++ ) {
+ *ptr++ = *src++;
+ }
+ dst += SCRAP_BLOCK_WIDTH;
+ }
+
+ flags |= if_scrap | if_transparent;
+
+ image->texnum = SCRAP_TEXNUM;
+ image->upload_width = width;
+ image->upload_height = height;
+ image->flags = flags;
+ image->sl = ( s + 0.01f ) / ( float )SCRAP_BLOCK_WIDTH;
+ image->sh = ( s + width - 0.01f ) / ( float )SCRAP_BLOCK_WIDTH;
+ image->tl = ( t + 0.01f ) / ( float )SCRAP_BLOCK_HEIGHT;
+ image->th = ( t + height - 0.01f ) / ( float )SCRAP_BLOCK_HEIGHT;
+
+ scrap_dirty = qtrue;
+ if( !gl_static.registering ) {
+ Scrap_Upload();
+ }
+
+ fs.FreeFile( pic );
+
+ return;
+ }
+ }
+
+ if( type == it_skin && ( flags & if_paletted ) )
+ R_FloodFillSkin( pic, width, height );
+
+ mipmap = qfalse;
+ if( type == it_wall || type == it_skin ) {
+ mipmap = qtrue;
+ }
+ image->texnum = ( image - r_images ) + 1;
+ GL_BindTexture( image->texnum );
+ if( flags & if_paletted ) {
+ transparent = GL_Upload8( pic, width, height, mipmap );
+ } else {
+ transparent = GL_Upload32( pic, width, height, mipmap );
+ }
+ if( transparent ) {
+ flags |= if_transparent;
+ }
+ image->upload_width = upload_width; // after power of 2 and scales
+ image->upload_height = upload_height;
+ image->flags = flags;
+ image->sl = 0;
+ image->sh = 1;
+ image->tl = 0;
+ image->th = 1;
+
+#if 0
+ if( width != upload_width || height != upload_height ) {
+ Com_Printf( "Resampled '%s' from %dx%d to %dx%d\n",
+ image->name, width, height, upload_width, upload_height );
+ }
+#endif
+
+ /* don't free autogenerated images */
+ if( flags & if_auto ) {
+ return;
+ }
+
+ /* don't free *.wal textures */
+ if( type == it_wall && ( flags & if_paletted ) ) {
+ return;
+ }
+
+ fs.FreeFile( pic );
+}
+
+
+/*
+================
+R_LoadWal
+================
+*/
+image_t *R_LoadWal( const char *name ) {
+ miptex_t *mt;
+ uint32 width, height, ofs;
+ uint32 length;
+ image_t *image;
+
+ length = fs.LoadFile( name, ( void ** )&mt );
+ if( !mt ) {
+ Com_DPrintf( "GL_LoadWal: can't load %s\n", name);
+ return r_notexture;
+ }
+
+ width = LittleLong( mt->width );
+ height = LittleLong( mt->height );
+ ofs = LittleLong( mt->offsets[0] );
+
+ if( ofs + width * height > length ) {
+ Com_DPrintf( "GL_LoadWal: '%s' is malformed\n", name );
+ fs.FreeFile( ( void * )mt );
+ return NULL;
+ }
+
+ image = R_CreateImage( name, ( byte * )mt + ofs, width, height, it_wall, if_paletted );
+
+ fs.FreeFile( ( void * )mt );
+
+ return image;
+}
+
+void R_FreeImage( image_t *image ) {
+ if( !( image->flags & if_scrap ) ) {
+ qglDeleteTextures( 1, &image->texnum );
+ }
+}
+
+static void GL_BuildGammaTables( void ) {
+ int i;
+ float inf, g = gl_gamma->value;
+
+ if( gl_config.renderer == GL_RENDERER_VOODOO || g == 1.0f ) {
+ for( i = 0; i < 256; i++ ) {
+ gammatable[i] = i;
+ gammaintensitytable[i] = intensitytable[i];
+ }
+ } else {
+ for( i = 0; i < 256; i++ ) {
+ inf = 255 * pow( ( i + 0.5 ) / 255.5, g ) + 0.5;
+ if( inf > 255 ) {
+ inf = 255;
+ }
+ gammatable[i] = inf;
+ gammaintensitytable[i] = intensitytable[gammatable[i]];
+ }
+ }
+}
+
+static void gl_gamma_changed( cvar_t *self ) {
+ GL_BuildGammaTables();
+ video.UpdateGamma( gammatable );
+}
+
+#define DLIGHT_TEXTURE_SIZE 16
+
+static void GL_InitDlightTexture( void ) {
+ byte pixels[DLIGHT_TEXTURE_SIZE*DLIGHT_TEXTURE_SIZE*4];
+ byte *dst;
+ float x, y, f;
+ int i, j;
+
+ dst = pixels;
+ for( i = 0; i < DLIGHT_TEXTURE_SIZE; i++ ) {
+ for( j = 0; j < DLIGHT_TEXTURE_SIZE; j++ ) {
+ x = j - DLIGHT_TEXTURE_SIZE/2 + 0.5f;
+ y = i - DLIGHT_TEXTURE_SIZE/2 + 0.5f;
+ f = sqrt( x * x + y * y );
+ f = 1.0f - f / ( DLIGHT_TEXTURE_SIZE/2 - 1.5f );
+ if( f < 0 ) f = 0;
+ else if( f > 1 ) f = 1;
+ dst[0] = 255 * f;
+ dst[1] = 255 * f;
+ dst[2] = 255 * f;
+ dst[3] = 255;
+ dst += 4;
+ }
+ }
+
+ r_dlightTex = R_CreateImage( "*dlightTexture", pixels, DLIGHT_TEXTURE_SIZE,
+ DLIGHT_TEXTURE_SIZE, it_pic, if_auto );
+ qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP );
+ qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP );
+}
+
+static byte dottexture[8][8] = {
+ {0,0,0,0,0,0,0,0},
+ {0,0,1,1,0,0,0,0},
+ {0,1,1,1,1,0,0,0},
+ {0,1,1,1,1,0,0,0},
+ {0,0,1,1,0,0,0,0},
+ {0,0,0,0,0,0,0,0},
+ {0,0,0,0,0,0,0,0},
+ {0,0,0,0,0,0,0,0},
+};
+
+static void GL_InitDefaultTexture( void ) {
+ int i, j;
+ byte pixels[8*8*4];
+ byte *dst;
+
+ dst = pixels;
+ for( i = 0; i < 8; i++ ) {
+ for( j = 0; j < 8; j++ ) {
+ dst[0] = dottexture[ i & 3 ][ j & 3 ] * 255;
+ dst[1] = 0;
+ dst[2] = 0;
+ dst[3] = 255;
+ dst += 4;
+ }
+ }
+
+ r_notexture = R_CreateImage( "*notexture", pixels, 8, 8, it_wall, if_auto );
+}
+
+static void GL_InitParticleTexture( void ) {
+ int i, j;
+ byte pixels[8*8*4];
+ byte *dst;
+
+ dst = pixels;
+ for( i = 0; i < 8; i++ ) {
+ for( j = 0; j < 8; j++ ) {
+ dst[0] = 255;
+ dst[1] = 255;
+ dst[2] = 255;
+ dst[3] = dottexture[ i ][ j ] * 255;
+ dst += 4;
+ }
+ }
+
+ r_particletexture = R_CreateImage( "*particletexture", pixels, 8, 8,
+ it_sprite, if_auto );
+ qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP );
+ qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP );
+}
+
+static void GL_InitWhiteImage( void ) {
+ int i, j;
+ byte pixels[8*8*4];
+ byte *dst;
+
+ dst = pixels;
+ for( i = 0; i < 8; i++ ) {
+ for( j = 0; j < 8; j++ ) {
+ *( uint32 * )dst = ( uint32 )-1;
+ dst += 4;
+ }
+ }
+
+ r_whiteimage = R_CreateImage( "*whiteimage", pixels, 8, 8, it_pic, if_auto );
+}
+
+#define BEAM_TEXTURE_SIZE 16
+
+static void GL_InitBeamTexture( void ) {
+ byte pixels[BEAM_TEXTURE_SIZE*BEAM_TEXTURE_SIZE*4];
+ byte *dst;
+ float f;
+ int i, j;
+
+ dst = pixels;
+ for( i = 0; i < BEAM_TEXTURE_SIZE; i++ ) {
+ for( j = 0; j < BEAM_TEXTURE_SIZE; j++ ) {
+ f = fabs( j - BEAM_TEXTURE_SIZE/2 ) - 0.5f;
+ f = 1.0f - f / ( BEAM_TEXTURE_SIZE/2 - 2.5f );
+ if( f < 0 ) f = 0;
+ else if( f > 1 ) f = 1;
+ dst[0] = 255 * f;
+ dst[1] = 255 * f;
+ dst[2] = 255 * f;
+ dst[3] = 255;
+ dst += 4;
+ }
+ }
+
+ r_beamtexture = R_CreateImage( "*beamTexture", pixels, BEAM_TEXTURE_SIZE,
+ BEAM_TEXTURE_SIZE, it_pic, if_auto );
+ qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
+ qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
+ // qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP );
+ // qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP );
+ /*
+ {
+ float borderColor[4];
+
+ borderColor[0] = 1;
+ borderColor[1] = 1;
+ borderColor[2] = 1;
+ borderColor[3] = 0;
+
+ qglTexParameterfv( GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor );
+ }
+ */
+}
+
+/*
+===============
+GL_InitImages
+===============
+*/
+void GL_InitImages( void ) {
+ int i, j;
+ float f;
+
+ registration_sequence = 1;
+
+ if( r_numImages ) {
+ Com_Error( ERR_FATAL, "GL_InitImages: %d images still not freed",
+ r_numImages );
+ }
+
+ gl_bilerp_chars = cvar.Get( "gl_bilerp_chars", "0", 0 );
+ gl_bilerp_chars->changed = gl_bilerp_chars_changed;
+ gl_texturemode = cvar.Get( "gl_texturemode", "GL_LINEAR_MIPMAP_NEAREST",
+ CVAR_ARCHIVE );
+ gl_texturemode->changed = gl_texturemode_changed;
+ gl_anisotropy = cvar.Get( "gl_anisotropy", "1", CVAR_ARCHIVE );
+ gl_anisotropy->changed = gl_anisotropy_changed;
+ gl_noscrap = cvar.Get( "gl_noscrap", "0", CVAR_LATCHED );
+ gl_round_down = cvar.Get( "gl_round_down", "0", CVAR_LATCHED );
+ gl_picmip = cvar.Get( "gl_picmip", "0", CVAR_LATCHED );
+ gl_gamma_scale_pics = cvar.Get( "gl_gamma_scale_pics", "0", CVAR_LATCHED );
+ gl_texturealphamode = cvar.Get( "gl_texturealphamode", "default",
+ CVAR_ARCHIVE|CVAR_LATCHED );
+ gl_texturesolidmode = cvar.Get( "gl_texturesolidmode", "default",
+ CVAR_ARCHIVE|CVAR_LATCHED );
+ gl_saturation = cvar.Get( "gl_saturation", "1", CVAR_ARCHIVE|CVAR_LATCHED );
+ gl_intensity = cvar.Get( "intensity", "1", CVAR_ARCHIVE|CVAR_LATCHED );
+ if( gl_hwgamma->integer ) {
+ gl_gamma = cvar.Get( "vid_gamma", "1", 0 );
+ gl_gamma->changed = gl_gamma_changed;
+ } else {
+ gl_gamma = cvar.Get( "vid_gamma", "1", CVAR_LATCHED );
+ }
+
+ R_InitImageManager();
+
+ Scrap_Init();
+
+ R_GetPalette( NULL );
+
+ if( gl_intensity->value < 1 ) {
+ cvar.SetValue( "intensity", 1 );
+ }
+ f = gl_intensity->value;
+ for( i = 0; i < 256; i++ ) {
+ j = i * f;
+ if( j > 255 ) {
+ j = 255;
+ }
+ intensitytable[i] = j;
+ }
+
+ if( gl_hwgamma->integer ) {
+ gl_gamma_changed( gl_gamma );
+ } else {
+ GL_BuildGammaTables();
+ }
+
+ GL_TextureAlphaMode();
+ GL_TextureSolidMode();
+
+ gl_texturemode_changed( gl_texturemode );
+ gl_anisotropy_changed( gl_anisotropy );
+ gl_bilerp_chars_changed( gl_bilerp_chars );
+
+ /* make sure r_notexture == &r_images[0] */
+ GL_InitDefaultTexture();
+ GL_InitDlightTexture();
+ GL_InitParticleTexture();
+ GL_InitWhiteImage();
+ GL_InitBeamTexture();
+}
+
+/*
+===============
+GL_ShutdownImages
+===============
+*/
+void GL_ShutdownImages( void ) {
+ gl_bilerp_chars->changed = NULL;
+ gl_texturemode->changed = NULL;
+ gl_anisotropy->changed = NULL;
+ gl_gamma->changed = NULL;
+
+ R_FreeAllImages();
+ R_ShutdownImageManager();
+}
+