📄 gl_image.c
字号:
/*
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 gltextures[MAX_GLTEXTURES];
int numgltextures;
int base_textureid; // gltextures[i] = base_textureid+i
static byte intensitytable[256];
static unsigned char gammatable[256];
cvar_t *intensity;
unsigned d_8to24table[256];
qboolean GL_Upload8 (byte *data, int width, int height, qboolean mipmap, qboolean is_sky );
qboolean GL_Upload32 (unsigned *data, int width, int height, qboolean mipmap);
int gl_solid_format = 3;
int gl_alpha_format = 4;
int gl_tex_solid_format = 3;
int gl_tex_alpha_format = 4;
int gl_filter_min = GL_LINEAR_MIPMAP_NEAREST;
int gl_filter_max = GL_LINEAR;
void GL_SetTexturePalette( unsigned palette[256] )
{
int i;
unsigned char temptable[768];
if ( qglColorTableEXT && gl_ext_palettedtexture->value )
{
for ( i = 0; i < 256; i++ )
{
temptable[i*3+0] = ( palette[i] >> 0 ) & 0xff;
temptable[i*3+1] = ( palette[i] >> 8 ) & 0xff;
temptable[i*3+2] = ( palette[i] >> 16 ) & 0xff;
}
qglColorTableEXT( GL_SHARED_TEXTURE_PALETTE_EXT,
GL_RGB,
256,
GL_RGB,
GL_UNSIGNED_BYTE,
temptable );
}
}
void GL_EnableMultitexture( qboolean enable )
{
if ( !qglSelectTextureSGIS && !qglActiveTextureARB )
return;
if ( enable )
{
GL_SelectTexture( GL_TEXTURE1 );
qglEnable( GL_TEXTURE_2D );
GL_TexEnv( GL_REPLACE );
}
else
{
GL_SelectTexture( GL_TEXTURE1 );
qglDisable( GL_TEXTURE_2D );
GL_TexEnv( GL_REPLACE );
}
GL_SelectTexture( GL_TEXTURE0 );
GL_TexEnv( GL_REPLACE );
}
void GL_SelectTexture( GLenum texture )
{
int tmu;
if ( !qglSelectTextureSGIS && !qglActiveTextureARB )
return;
if ( texture == GL_TEXTURE0 )
{
tmu = 0;
}
else
{
tmu = 1;
}
if ( tmu == gl_state.currenttmu )
{
return;
}
gl_state.currenttmu = tmu;
if ( qglSelectTextureSGIS )
{
qglSelectTextureSGIS( texture );
}
else if ( qglActiveTextureARB )
{
qglActiveTextureARB( texture );
qglClientActiveTextureARB( texture );
}
}
void GL_TexEnv( GLenum mode )
{
static int lastmodes[2] = { -1, -1 };
if ( mode != lastmodes[gl_state.currenttmu] )
{
qglTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, mode );
lastmodes[gl_state.currenttmu] = mode;
}
}
void GL_Bind (int texnum)
{
extern image_t *draw_chars;
if (gl_nobind->value && draw_chars) // performance evaluation option
texnum = draw_chars->texnum;
if ( gl_state.currenttextures[gl_state.currenttmu] == texnum)
return;
gl_state.currenttextures[gl_state.currenttmu] = texnum;
qglBindTexture (GL_TEXTURE_2D, texnum);
}
void GL_MBind( GLenum target, int texnum )
{
GL_SelectTexture( target );
if ( target == GL_TEXTURE0 )
{
if ( gl_state.currenttextures[0] == texnum )
return;
}
else
{
if ( gl_state.currenttextures[1] == texnum )
return;
}
GL_Bind( texnum );
}
typedef struct
{
char *name;
int minimize, maximize;
} glmode_t;
glmode_t modes[] = {
{"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}
};
#define NUM_GL_MODES (sizeof(modes) / sizeof (glmode_t))
typedef struct
{
char *name;
int mode;
} gltmode_t;
gltmode_t gl_alpha_modes[] = {
{"default", 4},
{"GL_RGBA", GL_RGBA},
{"GL_RGBA8", GL_RGBA8},
{"GL_RGB5_A1", GL_RGB5_A1},
{"GL_RGBA4", GL_RGBA4},
{"GL_RGBA2", GL_RGBA2},
};
#define NUM_GL_ALPHA_MODES (sizeof(gl_alpha_modes) / sizeof (gltmode_t))
gltmode_t gl_solid_modes[] = {
{"default", 3},
{"GL_RGB", GL_RGB},
{"GL_RGB8", GL_RGB8},
{"GL_RGB5", GL_RGB5},
{"GL_RGB4", GL_RGB4},
{"GL_R3_G3_B2", GL_R3_G3_B2},
#ifdef GL_RGB2_EXT
{"GL_RGB2", GL_RGB2_EXT},
#endif
};
#define NUM_GL_SOLID_MODES (sizeof(gl_solid_modes) / sizeof (gltmode_t))
/*
===============
GL_TextureMode
===============
*/
void GL_TextureMode( char *string )
{
int i;
image_t *glt;
for (i=0 ; i< NUM_GL_MODES ; i++)
{
if ( !Q_stricmp( modes[i].name, string ) )
break;
}
if (i == NUM_GL_MODES)
{
ri.Con_Printf (PRINT_ALL, "bad filter name\n");
return;
}
gl_filter_min = modes[i].minimize;
gl_filter_max = modes[i].maximize;
// change all the existing mipmap texture objects
for (i=0, glt=gltextures ; i<numgltextures ; i++, glt++)
{
if (glt->type != it_pic && glt->type != it_sky )
{
GL_Bind (glt->texnum);
qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_min);
qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max);
}
}
}
/*
===============
GL_TextureAlphaMode
===============
*/
void GL_TextureAlphaMode( char *string )
{
int i;
for (i=0 ; i< NUM_GL_ALPHA_MODES ; i++)
{
if ( !Q_stricmp( gl_alpha_modes[i].name, string ) )
break;
}
if (i == NUM_GL_ALPHA_MODES)
{
ri.Con_Printf (PRINT_ALL, "bad alpha texture mode name\n");
return;
}
gl_tex_alpha_format = gl_alpha_modes[i].mode;
}
/*
===============
GL_TextureSolidMode
===============
*/
void GL_TextureSolidMode( char *string )
{
int i;
for (i=0 ; i< NUM_GL_SOLID_MODES ; i++)
{
if ( !Q_stricmp( gl_solid_modes[i].name, string ) )
break;
}
if (i == NUM_GL_SOLID_MODES)
{
ri.Con_Printf (PRINT_ALL, "bad solid texture mode name\n");
return;
}
gl_tex_solid_format = gl_solid_modes[i].mode;
}
/*
===============
GL_ImageList_f
===============
*/
void GL_ImageList_f (void)
{
int i;
image_t *image;
int texels;
const char *palstrings[2] =
{
"RGB",
"PAL"
};
ri.Con_Printf (PRINT_ALL, "------------------\n");
texels = 0;
for (i=0, image=gltextures ; i<numgltextures ; i++, image++)
{
if (image->texnum <= 0)
continue;
texels += image->upload_width*image->upload_height;
switch (image->type)
{
case it_skin:
ri.Con_Printf (PRINT_ALL, "M");
break;
case it_sprite:
ri.Con_Printf (PRINT_ALL, "S");
break;
case it_wall:
ri.Con_Printf (PRINT_ALL, "W");
break;
case it_pic:
ri.Con_Printf (PRINT_ALL, "P");
break;
default:
ri.Con_Printf (PRINT_ALL, " ");
break;
}
ri.Con_Printf (PRINT_ALL, " %3i %3i %s: %s\n",
image->upload_width, image->upload_height, palstrings[image->paletted], image->name);
}
ri.Con_Printf (PRINT_ALL, "Total texel count (not counting mipmaps): %i\n", texels);
}
/*
=============================================================================
scrap allocation
Allocate all the little status bar obejcts into a single texture
to crutch up inefficient hardware / drivers
=============================================================================
*/
#define MAX_SCRAPS 1
#define BLOCK_WIDTH 256
#define BLOCK_HEIGHT 256
int scrap_allocated[MAX_SCRAPS][BLOCK_WIDTH];
byte scrap_texels[MAX_SCRAPS][BLOCK_WIDTH*BLOCK_HEIGHT];
qboolean scrap_dirty;
// returns a texture number and the position inside it
int Scrap_AllocBlock (int w, int h, int *x, int *y)
{
int i, j;
int best, best2;
int texnum;
for (texnum=0 ; texnum<MAX_SCRAPS ; texnum++)
{
best = BLOCK_HEIGHT;
for (i=0 ; i<BLOCK_WIDTH-w ; i++)
{
best2 = 0;
for (j=0 ; j<w ; j++)
{
if (scrap_allocated[texnum][i+j] >= best)
break;
if (scrap_allocated[texnum][i+j] > best2)
best2 = scrap_allocated[texnum][i+j];
}
if (j == w)
{ // this is a valid spot
*x = i;
*y = best = best2;
}
}
if (best + h > BLOCK_HEIGHT)
continue;
for (i=0 ; i<w ; i++)
scrap_allocated[texnum][*x + i] = best + h;
return texnum;
}
return -1;
// Sys_Error ("Scrap_AllocBlock: full");
}
int scrap_uploads;
void Scrap_Upload (void)
{
scrap_uploads++;
GL_Bind(TEXNUM_SCRAPS);
GL_Upload8 (scrap_texels[0], BLOCK_WIDTH, BLOCK_HEIGHT, false, false );
scrap_dirty = false;
}
/*
=================================================================
PCX LOADING
=================================================================
*/
/*
==============
LoadPCX
==============
*/
void LoadPCX (char *filename, byte **pic, byte **palette, int *width, int *height)
{
byte *raw;
pcx_t *pcx;
int x, y;
int len;
int dataByte, runLength;
byte *out, *pix;
*pic = NULL;
*palette = NULL;
//
// load the file
//
len = ri.FS_LoadFile (filename, (void **)&raw);
if (!raw)
{
ri.Con_Printf (PRINT_DEVELOPER, "Bad pcx file %s\n", filename);
return;
}
//
// parse the PCX file
//
pcx = (pcx_t *)raw;
pcx->xmin = LittleShort(pcx->xmin);
pcx->ymin = LittleShort(pcx->ymin);
pcx->xmax = LittleShort(pcx->xmax);
pcx->ymax = LittleShort(pcx->ymax);
pcx->hres = LittleShort(pcx->hres);
pcx->vres = LittleShort(pcx->vres);
pcx->bytes_per_line = LittleShort(pcx->bytes_per_line);
pcx->palette_type = LittleShort(pcx->palette_type);
raw = &pcx->data;
if (pcx->manufacturer != 0x0a
|| pcx->version != 5
|| pcx->encoding != 1
|| pcx->bits_per_pixel != 8
|| pcx->xmax >= 640
|| pcx->ymax >= 480)
{
ri.Con_Printf (PRINT_ALL, "Bad pcx file %s\n", filename);
return;
}
out = malloc ( (pcx->ymax+1) * (pcx->xmax+1) );
*pic = out;
pix = out;
if (palette)
{
*palette = malloc(768);
memcpy (*palette, (byte *)pcx + len - 768, 768);
}
if (width)
*width = pcx->xmax+1;
if (height)
*height = pcx->ymax+1;
for (y=0 ; y<=pcx->ymax ; y++, pix += pcx->xmax+1)
{
for (x=0 ; x<=pcx->xmax ; )
{
dataByte = *raw++;
if((dataByte & 0xC0) == 0xC0)
{
runLength = dataByte & 0x3F;
dataByte = *raw++;
}
else
runLength = 1;
while(runLength-- > 0)
pix[x++] = dataByte;
}
}
if ( raw - (byte *)pcx > len)
{
ri.Con_Printf (PRINT_DEVELOPER, "PCX file %s was malformed", filename);
free (*pic);
*pic = NULL;
}
ri.FS_FreeFile (pcx);
}
/*
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -