mirror of
https://github.com/blendogames/thirtyflightsofloving.git
synced 2025-01-31 04:30:39 +00:00
3ce18b138c
Simplified loading and saving of cvars in slider menu control. Enlarged text in Game, Multiplayer, and Options menus. Fixed repeat of failed file causing HTTP downloads to restart. Added cl_zoommode cvar to simplify Lazarus zoom command. Changed zoom command to use new cl_zoommode cvar in default Lazarus and missionpack DLLs. Removed unused "crossh" cvar in default Lazarus and missionpack DLLs. Fixed Makron not having a classname when spawned from Jorg in default Lazarus and missionpack DLLs. Made Tactician Gunner ignore small amounts of damage in missionpack DLL.
2783 lines
67 KiB
C
2783 lines
67 KiB
C
/*
|
|
===========================================================================
|
|
Copyright (C) 1997-2001 Id Software, Inc.
|
|
|
|
This file is part of Quake 2 source code.
|
|
|
|
Quake 2 source code 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.
|
|
|
|
Quake 2 source code 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 Quake 2 source code; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
===========================================================================
|
|
*/
|
|
|
|
// r_image.c
|
|
|
|
#include "r_local.h"
|
|
//#include "r_cin.h"
|
|
#include "../include/jpeg/jpeglib.h"
|
|
#ifdef PNG_SUPPORT
|
|
#if defined (_MSC_VER) && (_MSC_VER <= 1200) // use older version of libpng for MSVC6
|
|
#include "../include/zlibpng_vc6/png.h"
|
|
#else
|
|
#include "../include/zlibpng/png.h"
|
|
#endif
|
|
#endif // PNG_SUPPORT
|
|
|
|
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 *r_intensity;
|
|
|
|
unsigned d_8to24table[256];
|
|
float d_8to24tablef[256][3]; //Knightmare- MrG's Vertex array stuff
|
|
|
|
qboolean GL_Upload8 (byte *data, int width, int height, imagetype_t type);
|
|
qboolean GL_Upload32 (unsigned *data, int width, int height, imagetype_t type);
|
|
|
|
/*
|
|
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 );
|
|
}
|
|
}
|
|
|
|
|
|
typedef struct
|
|
{
|
|
char *name;
|
|
int minimize, maximize;
|
|
} glmode_t;
|
|
|
|
glmode_t gl_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(gl_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_UpdateAnisoMode
|
|
===============
|
|
*/
|
|
void GL_UpdateAnisoMode (void)
|
|
{
|
|
int i;
|
|
image_t *glt;
|
|
|
|
// clamp selected anisotropy
|
|
if (glConfig.anisotropic)
|
|
{
|
|
if (r_anisotropic->value > glConfig.max_anisotropy)
|
|
Cvar_SetValue("r_anisotropic", glConfig.max_anisotropy);
|
|
else if (r_anisotropic->value < 1.0)
|
|
Cvar_SetValue("r_anisotropic", 1.0);
|
|
}
|
|
|
|
// change all the existing mipmap texture objects
|
|
for (i=0, glt=gltextures; i<numgltextures; i++, glt++)
|
|
{
|
|
if ( (glt->type != it_pic) && (glt->type != it_font) && (glt->type != it_scrap) && (glt->type != it_sky) )
|
|
{
|
|
GL_Bind (glt->texnum);
|
|
// Set anisotropic filter if supported and enabled
|
|
if (glConfig.anisotropic && r_anisotropic->value)
|
|
qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, r_anisotropic->value);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
===============
|
|
GL_TextureMode
|
|
===============
|
|
*/
|
|
void GL_TextureMode (char *string)
|
|
{
|
|
int i;
|
|
image_t *glt;
|
|
|
|
for (i=0; i< NUM_GL_MODES; i++)
|
|
{
|
|
if ( !Q_stricmp(gl_modes[i].name, string) )
|
|
break;
|
|
}
|
|
|
|
if (i == NUM_GL_MODES)
|
|
{
|
|
VID_Printf (PRINT_ALL, "bad filter name\n");
|
|
return;
|
|
}
|
|
|
|
gl_filter_min = gl_modes[i].minimize;
|
|
gl_filter_max = gl_modes[i].maximize;
|
|
|
|
// clamp selected anisotropy
|
|
if (glConfig.anisotropic)
|
|
{
|
|
if (r_anisotropic->value > glConfig.max_anisotropy)
|
|
Cvar_SetValue("r_anisotropic", glConfig.max_anisotropy);
|
|
else if (r_anisotropic->value < 1.0)
|
|
Cvar_SetValue("r_anisotropic", 1.0);
|
|
}
|
|
|
|
// change all the existing mipmap texture objects
|
|
for (i=0, glt=gltextures; i<numgltextures; i++, glt++)
|
|
{
|
|
if ( (glt->type != it_pic) && (glt->type != it_font) && (glt->type != it_scrap) && (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);
|
|
|
|
// Set anisotropic filter if supported and enabled
|
|
if (glConfig.anisotropic && r_anisotropic->value) {
|
|
qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, r_anisotropic->value);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#if 0 // removed
|
|
/*
|
|
===============
|
|
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)
|
|
{
|
|
VID_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)
|
|
{
|
|
VID_Printf (PRINT_ALL, "bad solid texture mode name\n");
|
|
return;
|
|
}
|
|
|
|
gl_tex_solid_format = gl_solid_modes[i].mode;
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
===============
|
|
R_ImageList_f
|
|
===============
|
|
*/
|
|
void R_ImageList_f (void)
|
|
{
|
|
int i;
|
|
image_t *image;
|
|
int texels;
|
|
const char *palstrings[2] =
|
|
{
|
|
"RGB",
|
|
"PAL"
|
|
};
|
|
|
|
VID_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:
|
|
VID_Printf (PRINT_ALL, "M");
|
|
break;
|
|
case it_sprite:
|
|
VID_Printf (PRINT_ALL, "S");
|
|
break;
|
|
case it_wall:
|
|
VID_Printf (PRINT_ALL, "W");
|
|
break;
|
|
case it_pic:
|
|
VID_Printf (PRINT_ALL, "P");
|
|
break;
|
|
case it_font:
|
|
VID_Printf (PRINT_ALL, "F");
|
|
break;
|
|
case it_scrap:
|
|
VID_Printf (PRINT_ALL, "C");
|
|
break;
|
|
case it_part:
|
|
VID_Printf (PRINT_ALL, "P");
|
|
break;
|
|
default:
|
|
VID_Printf (PRINT_ALL, " ");
|
|
break;
|
|
}
|
|
|
|
VID_Printf (PRINT_ALL, " %3i %3i %s: %s\n",
|
|
image->upload_width, image->upload_height, palstrings[image->paletted], image->name);
|
|
}
|
|
VID_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 SCRAP_BLOCK_WIDTH 256
|
|
#define SCRAP_BLOCK_HEIGHT 256
|
|
|
|
int scrap_allocated[MAX_SCRAPS][SCRAP_BLOCK_WIDTH];
|
|
byte scrap_texels[MAX_SCRAPS][SCRAP_BLOCK_WIDTH*SCRAP_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 = SCRAP_BLOCK_HEIGHT;
|
|
|
|
for (i=0; i<SCRAP_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 > SCRAP_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], SCRAP_BLOCK_WIDTH, SCRAP_BLOCK_HEIGHT, it_scrap); // was it_pic
|
|
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 = FS_LoadFile (filename, (void **)&raw);
|
|
if (!raw)
|
|
{
|
|
// Knightmare- skip this unless developer >= 2 because it spams the console
|
|
if (developer->integer > 1)
|
|
VID_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)
|
|
{
|
|
VID_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)
|
|
{
|
|
VID_Printf (PRINT_DEVELOPER, "PCX file %s was malformed", filename);
|
|
free (*pic);
|
|
*pic = NULL;
|
|
}
|
|
|
|
FS_FreeFile (pcx);
|
|
}
|
|
|
|
/*
|
|
=========================================================
|
|
|
|
TARGA LOADING
|
|
|
|
=========================================================
|
|
*/
|
|
|
|
// Definitions for image types
|
|
#define TGA_Null 0 // no image data
|
|
#define TGA_Map 1 // Uncompressed, color-mapped images
|
|
#define TGA_RGB 2 // Uncompressed, RGB images
|
|
#define TGA_Mono 3 // Uncompressed, black and white images
|
|
#define TGA_RLEMap 9 // Runlength encoded color-mapped images
|
|
#define TGA_RLERGB 10 // Runlength encoded RGB images
|
|
#define TGA_RLEMono 11 // Compressed, black and white images
|
|
#define TGA_CompMap 32 // Compressed color-mapped data, using Huffman, Delta, and runlength encoding
|
|
#define TGA_CompMap4 33 // Compressed color-mapped data, using Huffman, Delta, and runlength encoding. 4-pass quadtree-type process
|
|
// Definitions for interleave flag
|
|
#define TGA_IL_None 0 // non-interleaved
|
|
#define TGA_IL_Two 1 // two-way (even/odd) interleaving
|
|
#define TGA_IL_Four 2 // four way interleaving
|
|
#define TGA_IL_Reserved 3 // reserved
|
|
// Definitions for origin flag
|
|
#define TGA_O_UPPER 0 // Origin in lower left-hand corner
|
|
#define TGA_O_LOWER 1 // Origin in upper left-hand corner
|
|
#define MAXCOLORS 16384
|
|
|
|
typedef struct _TargaHeader {
|
|
unsigned char id_length, colormap_type, image_type;
|
|
unsigned short colormap_index, colormap_length;
|
|
unsigned char colormap_size;
|
|
unsigned short x_origin, y_origin, width, height;
|
|
unsigned char pixel_size, attributes;
|
|
} TargaHeader;
|
|
|
|
#if 1
|
|
|
|
/*
|
|
=============
|
|
R_LoadTGA
|
|
NiceAss: LoadTGA() from Q2Ice, it supports more formats
|
|
=============
|
|
*/
|
|
void R_LoadTGA( char *filename, byte **pic, int *width, int *height )
|
|
{
|
|
int w, h, x, y, i, temp1, temp2;
|
|
int realrow, truerow, baserow, size, interleave, origin;
|
|
int pixel_size, map_idx, mapped, rlencoded, RLE_count, RLE_flag;
|
|
TargaHeader header;
|
|
byte tmp[2], r, g, b, a, j, k, l;
|
|
byte *dst, *ColorMap, *data, *pdata;
|
|
|
|
// load file
|
|
FS_LoadFile( filename, &data );
|
|
|
|
if ( !data )
|
|
return;
|
|
|
|
pdata = data;
|
|
|
|
header.id_length = *pdata++;
|
|
header.colormap_type = *pdata++;
|
|
header.image_type = *pdata++;
|
|
|
|
tmp[0] = pdata[0];
|
|
tmp[1] = pdata[1];
|
|
header.colormap_index = LittleShort( *((short *)tmp) );
|
|
pdata+=2;
|
|
tmp[0] = pdata[0];
|
|
tmp[1] = pdata[1];
|
|
header.colormap_length = LittleShort( *((short *)tmp) );
|
|
pdata+=2;
|
|
header.colormap_size = *pdata++;
|
|
header.x_origin = LittleShort( *((short *)pdata) );
|
|
pdata+=2;
|
|
header.y_origin = LittleShort( *((short *)pdata) );
|
|
pdata+=2;
|
|
header.width = LittleShort( *((short *)pdata) );
|
|
pdata+=2;
|
|
header.height = LittleShort( *((short *)pdata) );
|
|
pdata+=2;
|
|
header.pixel_size = *pdata++;
|
|
header.attributes = *pdata++;
|
|
|
|
if( header.id_length )
|
|
pdata += header.id_length;
|
|
|
|
// validate TGA type
|
|
switch( header.image_type ) {
|
|
case TGA_Map:
|
|
case TGA_RGB:
|
|
case TGA_Mono:
|
|
case TGA_RLEMap:
|
|
case TGA_RLERGB:
|
|
case TGA_RLEMono:
|
|
break;
|
|
default:
|
|
VID_Error ( ERR_DROP, "R_LoadTGA: Only type 1 (map), 2 (RGB), 3 (mono), 9 (RLEmap), 10 (RLERGB), 11 (RLEmono) TGA images supported\n" );
|
|
return;
|
|
}
|
|
|
|
// validate color depth
|
|
switch( header.pixel_size ) {
|
|
case 8:
|
|
case 15:
|
|
case 16:
|
|
case 24:
|
|
case 32:
|
|
break;
|
|
default:
|
|
VID_Error ( ERR_DROP, "R_LoadTGA: Only 8, 15, 16, 24 and 32 bit images (with colormaps) supported\n" );
|
|
return;
|
|
}
|
|
|
|
r = g = b = a = l = 0;
|
|
|
|
// if required, read the color map information
|
|
ColorMap = NULL;
|
|
mapped = ( header.image_type == TGA_Map || header.image_type == TGA_RLEMap || header.image_type == TGA_CompMap || header.image_type == TGA_CompMap4 ) && header.colormap_type == 1;
|
|
if( mapped ) {
|
|
// validate colormap size
|
|
switch( header.colormap_size ) {
|
|
case 8:
|
|
case 16:
|
|
case 32:
|
|
case 24:
|
|
break;
|
|
default:
|
|
VID_Error ( ERR_DROP, "R_LoadTGA: Only 8, 16, 24 and 32 bit colormaps supported\n" );
|
|
return;
|
|
}
|
|
|
|
temp1 = header.colormap_index;
|
|
temp2 = header.colormap_length;
|
|
if( (temp1 + temp2 + 1) >= MAXCOLORS ) {
|
|
FS_FreeFile( data );
|
|
return;
|
|
}
|
|
ColorMap = (byte *)malloc( MAXCOLORS * 4 );
|
|
map_idx = 0;
|
|
for( i = temp1; i < temp1 + temp2; ++i, map_idx += 4 ) {
|
|
// read appropriate number of bytes, break into rgb & put in map
|
|
switch( header.colormap_size ) {
|
|
case 8:
|
|
r = g = b = *pdata++;
|
|
a = 255;
|
|
break;
|
|
case 15:
|
|
j = *pdata++;
|
|
k = *pdata++;
|
|
l = ((unsigned int) k << 8) + j;
|
|
r = (byte) ( ((k & 0x7C) >> 2) << 3 );
|
|
g = (byte) ( (((k & 0x03) << 3) + ((j & 0xE0) >> 5)) << 3 );
|
|
b = (byte) ( (j & 0x1F) << 3 );
|
|
a = 255;
|
|
break;
|
|
case 16:
|
|
j = *pdata++;
|
|
k = *pdata++;
|
|
l = ((unsigned int) k << 8) + j;
|
|
r = (byte) ( ((k & 0x7C) >> 2) << 3 );
|
|
g = (byte) ( (((k & 0x03) << 3) + ((j & 0xE0) >> 5)) << 3 );
|
|
b = (byte) ( (j & 0x1F) << 3 );
|
|
a = (k & 0x80) ? 255 : 0;
|
|
break;
|
|
case 24:
|
|
b = *pdata++;
|
|
g = *pdata++;
|
|
r = *pdata++;
|
|
a = 255;
|
|
l = 0;
|
|
break;
|
|
case 32:
|
|
b = *pdata++;
|
|
g = *pdata++;
|
|
r = *pdata++;
|
|
a = *pdata++;
|
|
l = 0;
|
|
break;
|
|
}
|
|
ColorMap[map_idx + 0] = r;
|
|
ColorMap[map_idx + 1] = g;
|
|
ColorMap[map_idx + 2] = b;
|
|
ColorMap[map_idx + 3] = a;
|
|
}
|
|
}
|
|
|
|
// check run-length encoding
|
|
rlencoded = header.image_type == TGA_RLEMap || header.image_type == TGA_RLERGB || header.image_type == TGA_RLEMono;
|
|
RLE_count = 0;
|
|
RLE_flag = 0;
|
|
|
|
w = header.width;
|
|
h = header.height;
|
|
|
|
if( width )
|
|
*width = w;
|
|
if( height )
|
|
*height = h;
|
|
|
|
size = w * h * 4;
|
|
*pic = (byte *)malloc( size );
|
|
|
|
memset( *pic, 0, size );
|
|
|
|
// read the Targa file body and convert to portable format
|
|
pixel_size = header.pixel_size;
|
|
origin = (header.attributes & 0x20) >> 5;
|
|
interleave = (header.attributes & 0xC0) >> 6;
|
|
truerow = 0;
|
|
baserow = 0;
|
|
for( y = 0; y < h; y++ ) {
|
|
realrow = truerow;
|
|
if( origin == TGA_O_UPPER )
|
|
realrow = h - realrow - 1;
|
|
|
|
dst = *pic + realrow * w * 4;
|
|
|
|
for( x = 0; x < w; x++ ) {
|
|
// check if run length encoded
|
|
if( rlencoded ) {
|
|
if( !RLE_count ) {
|
|
// have to restart run
|
|
i = *pdata++;
|
|
RLE_flag = (i & 0x80);
|
|
if( !RLE_flag ) {
|
|
// stream of unencoded pixels
|
|
RLE_count = i + 1;
|
|
} else {
|
|
// single pixel replicated
|
|
RLE_count = i - 127;
|
|
}
|
|
// decrement count & get pixel
|
|
--RLE_count;
|
|
} else {
|
|
// have already read count & (at least) first pixel
|
|
--RLE_count;
|
|
if( RLE_flag )
|
|
// replicated pixels
|
|
goto PixEncode;
|
|
}
|
|
}
|
|
|
|
// read appropriate number of bytes, break into RGB
|
|
switch( pixel_size ) {
|
|
case 8:
|
|
r = g = b = l = *pdata++;
|
|
a = 255;
|
|
break;
|
|
case 15:
|
|
j = *pdata++;
|
|
k = *pdata++;
|
|
l = ((unsigned int) k << 8) + j;
|
|
r = (byte) ( ((k & 0x7C) >> 2) << 3 );
|
|
g = (byte) ( (((k & 0x03) << 3) + ((j & 0xE0) >> 5)) << 3 );
|
|
b = (byte) ( (j & 0x1F) << 3 );
|
|
a = 255;
|
|
break;
|
|
case 16:
|
|
j = *pdata++;
|
|
k = *pdata++;
|
|
l = ((unsigned int) k << 8) + j;
|
|
r = (byte) ( ((k & 0x7C) >> 2) << 3 );
|
|
g = (byte) ( (((k & 0x03) << 3) + ((j & 0xE0) >> 5)) << 3 );
|
|
b = (byte) ( (j & 0x1F) << 3 );
|
|
a = 255;
|
|
break;
|
|
case 24:
|
|
b = *pdata++;
|
|
g = *pdata++;
|
|
r = *pdata++;
|
|
a = 255;
|
|
l = 0;
|
|
break;
|
|
case 32:
|
|
b = *pdata++;
|
|
g = *pdata++;
|
|
r = *pdata++;
|
|
a = *pdata++;
|
|
l = 0;
|
|
break;
|
|
default:
|
|
VID_Error( ERR_DROP, "Illegal pixel_size '%d' in file '%s'\n", filename );
|
|
return;
|
|
}
|
|
|
|
PixEncode:
|
|
if ( mapped )
|
|
{
|
|
map_idx = l * 4;
|
|
*dst++ = ColorMap[map_idx + 0];
|
|
*dst++ = ColorMap[map_idx + 1];
|
|
*dst++ = ColorMap[map_idx + 2];
|
|
*dst++ = ColorMap[map_idx + 3];
|
|
}
|
|
else
|
|
{
|
|
*dst++ = r;
|
|
*dst++ = g;
|
|
*dst++ = b;
|
|
*dst++ = a;
|
|
}
|
|
}
|
|
|
|
if (interleave == TGA_IL_Four)
|
|
truerow += 4;
|
|
else if (interleave == TGA_IL_Two)
|
|
truerow += 2;
|
|
else
|
|
truerow++;
|
|
|
|
if (truerow >= h)
|
|
truerow = ++baserow;
|
|
}
|
|
|
|
if (mapped)
|
|
free( ColorMap );
|
|
|
|
FS_FreeFile( data );
|
|
}
|
|
|
|
#else
|
|
|
|
/*
|
|
=============
|
|
R_LoadTGA
|
|
=============
|
|
*/
|
|
void R_LoadTGA (char *name, byte **pic, int *width, int *height)
|
|
{
|
|
int columns, rows, numPixels;
|
|
byte *pixbuf;
|
|
int row, column;
|
|
byte *buf_p;
|
|
byte *buffer;
|
|
int length;
|
|
TargaHeader targa_header;
|
|
byte *targa_rgba;
|
|
byte tmp[2];
|
|
|
|
*pic = NULL;
|
|
|
|
//
|
|
// load the file
|
|
//
|
|
length = FS_LoadFile (name, (void **)&buffer);
|
|
if (!buffer)
|
|
{ // Knightmare- removed this because it spams the console
|
|
//VID_Printf (PRINT_DEVELOPER, "Bad tga file %s\n", name);
|
|
return;
|
|
}
|
|
|
|
buf_p = buffer;
|
|
|
|
targa_header.id_length = *buf_p++;
|
|
targa_header.colormap_type = *buf_p++;
|
|
targa_header.image_type = *buf_p++;
|
|
|
|
tmp[0] = buf_p[0];
|
|
tmp[1] = buf_p[1];
|
|
targa_header.colormap_index = LittleShort ( *((short *)tmp) );
|
|
buf_p+=2;
|
|
tmp[0] = buf_p[0];
|
|
tmp[1] = buf_p[1];
|
|
targa_header.colormap_length = LittleShort ( *((short *)tmp) );
|
|
buf_p+=2;
|
|
targa_header.colormap_size = *buf_p++;
|
|
targa_header.x_origin = LittleShort ( *((short *)buf_p) );
|
|
buf_p+=2;
|
|
targa_header.y_origin = LittleShort ( *((short *)buf_p) );
|
|
buf_p+=2;
|
|
targa_header.width = LittleShort ( *((short *)buf_p) );
|
|
buf_p+=2;
|
|
targa_header.height = LittleShort ( *((short *)buf_p) );
|
|
buf_p+=2;
|
|
targa_header.pixel_size = *buf_p++;
|
|
targa_header.attributes = *buf_p++;
|
|
|
|
// Knightmare- check for bad data
|
|
if (!targa_header.width || !targa_header.height) {
|
|
VID_Printf (PRINT_ALL, "Bad tga file %s\n", name);
|
|
FS_FreeFile (buffer);
|
|
return;
|
|
}
|
|
|
|
if (targa_header.image_type != 2
|
|
&& targa_header.image_type != 10) {
|
|
// VID_Error (ERR_DROP, "R_LoadTGA: Only type 2 and 10 targa RGB images supported\n");
|
|
VID_Printf (PRINT_ALL, "R_LoadTGA: %s has wrong image format; only type 2 and 10 targa RGB images supported.\n", name);
|
|
FS_FreeFile (buffer);
|
|
return;
|
|
}
|
|
|
|
if (targa_header.colormap_type !=0
|
|
|| (targa_header.pixel_size!=32 && targa_header.pixel_size!=24)) {
|
|
// VID_Error (ERR_DROP, "R_LoadTGA: Only 32 or 24 bit images supported (no colormaps)\n");
|
|
VID_Printf (PRINT_ALL, "R_LoadTGA: %s has wrong image format; only 32 or 24 bit images supported (no colormaps).\n", name);
|
|
FS_FreeFile (buffer);
|
|
return;
|
|
}
|
|
|
|
columns = targa_header.width;
|
|
rows = targa_header.height;
|
|
numPixels = columns * rows;
|
|
|
|
if (width)
|
|
*width = columns;
|
|
if (height)
|
|
*height = rows;
|
|
|
|
targa_rgba = malloc (numPixels*4);
|
|
*pic = targa_rgba;
|
|
|
|
if (targa_header.id_length != 0)
|
|
buf_p += targa_header.id_length; // skip TARGA image comment
|
|
|
|
if (targa_header.image_type==2) { // Uncompressed, RGB images
|
|
for(row=rows-1; row>=0; row--) {
|
|
pixbuf = targa_rgba + row*columns*4;
|
|
for(column=0; column<columns; column++) {
|
|
unsigned char red,green,blue,alphabyte;
|
|
switch (targa_header.pixel_size) {
|
|
case 24:
|
|
|
|
blue = *buf_p++;
|
|
green = *buf_p++;
|
|
red = *buf_p++;
|
|
*pixbuf++ = red;
|
|
*pixbuf++ = green;
|
|
*pixbuf++ = blue;
|
|
*pixbuf++ = 255;
|
|
break;
|
|
case 32:
|
|
blue = *buf_p++;
|
|
green = *buf_p++;
|
|
red = *buf_p++;
|
|
alphabyte = *buf_p++;
|
|
*pixbuf++ = red;
|
|
*pixbuf++ = green;
|
|
*pixbuf++ = blue;
|
|
*pixbuf++ = alphabyte;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (targa_header.image_type==10) { // Runlength encoded RGB images
|
|
unsigned char red,green,blue,alphabyte,packetHeader,packetSize,j;
|
|
for(row=rows-1; row>=0; row--) {
|
|
pixbuf = targa_rgba + row*columns*4;
|
|
for(column=0; column<columns; ) {
|
|
packetHeader= *buf_p++;
|
|
packetSize = 1 + (packetHeader & 0x7f);
|
|
if (packetHeader & 0x80) { // run-length packet
|
|
switch (targa_header.pixel_size) {
|
|
case 24:
|
|
blue = *buf_p++;
|
|
green = *buf_p++;
|
|
red = *buf_p++;
|
|
alphabyte = 255;
|
|
break;
|
|
case 32:
|
|
blue = *buf_p++;
|
|
green = *buf_p++;
|
|
red = *buf_p++;
|
|
alphabyte = *buf_p++;
|
|
break;
|
|
}
|
|
|
|
for(j=0;j<packetSize;j++) {
|
|
*pixbuf++=red;
|
|
*pixbuf++=green;
|
|
*pixbuf++=blue;
|
|
*pixbuf++=alphabyte;
|
|
column++;
|
|
if (column==columns) { // run spans across rows
|
|
column=0;
|
|
if (row>0)
|
|
row--;
|
|
else
|
|
goto breakOut;
|
|
pixbuf = targa_rgba + row*columns*4;
|
|
}
|
|
}
|
|
}
|
|
else { // non run-length packet
|
|
for(j=0;j<packetSize;j++) {
|
|
switch (targa_header.pixel_size) {
|
|
case 24:
|
|
blue = *buf_p++;
|
|
green = *buf_p++;
|
|
red = *buf_p++;
|
|
*pixbuf++ = red;
|
|
*pixbuf++ = green;
|
|
*pixbuf++ = blue;
|
|
*pixbuf++ = 255;
|
|
break;
|
|
case 32:
|
|
blue = *buf_p++;
|
|
green = *buf_p++;
|
|
red = *buf_p++;
|
|
alphabyte = *buf_p++;
|
|
*pixbuf++ = red;
|
|
*pixbuf++ = green;
|
|
*pixbuf++ = blue;
|
|
*pixbuf++ = alphabyte;
|
|
break;
|
|
}
|
|
column++;
|
|
if (column==columns) { // pixel packet run spans across rows
|
|
column=0;
|
|
if (row>0)
|
|
row--;
|
|
else
|
|
goto breakOut;
|
|
pixbuf = targa_rgba + row*columns*4;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
breakOut:;
|
|
}
|
|
}
|
|
|
|
FS_FreeFile (buffer);
|
|
}
|
|
|
|
#endif
|
|
|
|
/*
|
|
=================================================================
|
|
|
|
PNG LOADING
|
|
|
|
From Quake2Max
|
|
|
|
=================================================================
|
|
*/
|
|
#ifdef PNG_SUPPORT
|
|
|
|
// Knightmare- abstracted function call for png_set_gray_1_2_4_to_8()
|
|
#if defined (PNG_LIBPNG_VER) && (PNG_LIBPNG_VER < 10209)
|
|
#define PNG_GRAYFUNC_1_2_4_to_8(a) png_set_gray_1_2_4_to_8(a)
|
|
#else
|
|
#define PNG_GRAYFUNC_1_2_4_to_8(a) png_set_expand_gray_1_2_4_to_8(a)
|
|
#endif
|
|
|
|
typedef struct png_handle_s
|
|
{
|
|
char *tmpBuf;
|
|
int tmpi;
|
|
int /*long*/ fBgColor; // DL Background color Added 30/05/2000
|
|
int fTransparent; // DL Is this Image Transparent? Added 30/05/2000
|
|
int /*long*/ fRowBytes; // DL Added 30/05/2000
|
|
double fGamma; // DL Added 07/06/2000
|
|
double fScreenGamma; // DL Added 07/06/2000
|
|
char *fRowPtrs; // DL Changed for consistancy 30/05/2000
|
|
char *data; // property data: pByte read fData;
|
|
char *title;
|
|
char *author;
|
|
char *description;
|
|
int bitDepth;
|
|
int bytesPerPixel;
|
|
int colorType;
|
|
int height;
|
|
int width;
|
|
int interlace;
|
|
int compression;
|
|
int filter;
|
|
double lastModified;
|
|
int transparent;
|
|
} png_handle_t;
|
|
png_handle_t *r_png_handle = 0;
|
|
|
|
void R_InitializePNGData (void)
|
|
{
|
|
size_t /*long*/ *cvaluep; //ub
|
|
int /*long*/ y;
|
|
|
|
// Initialize Data and RowPtrs
|
|
if (r_png_handle->data)
|
|
{
|
|
free(r_png_handle->data);
|
|
r_png_handle->data = 0;
|
|
}
|
|
if (r_png_handle->fRowPtrs)
|
|
{
|
|
free(r_png_handle->fRowPtrs);
|
|
r_png_handle->fRowPtrs = 0;
|
|
}
|
|
r_png_handle->data = malloc(r_png_handle->height * r_png_handle->fRowBytes ); // DL Added 30/5/2000
|
|
r_png_handle->fRowPtrs = malloc(sizeof(void*) * r_png_handle->height);
|
|
|
|
if ((r_png_handle->data)&&(r_png_handle->fRowPtrs))
|
|
{
|
|
cvaluep = (size_t /*long*/*)r_png_handle->fRowPtrs;
|
|
for (y=0; y<r_png_handle->height; y++)
|
|
{
|
|
cvaluep[y] = (size_t /*long*/)r_png_handle->data + ( y * (size_t /*long*/)r_png_handle->fRowBytes ); //DL Added 08/07/2000
|
|
}
|
|
}
|
|
}
|
|
|
|
void R_CreatePNG (void)
|
|
{
|
|
if (r_png_handle)
|
|
return;
|
|
r_png_handle = malloc(sizeof(png_handle_t));
|
|
r_png_handle->data = 0;
|
|
r_png_handle->fRowPtrs = 0;
|
|
r_png_handle->height = 0;
|
|
r_png_handle->width = 0;
|
|
r_png_handle->colorType = PNG_COLOR_TYPE_RGBA; // was PNG_COLOR_TYPE_RGB
|
|
r_png_handle->interlace = PNG_INTERLACE_NONE;
|
|
r_png_handle->compression = PNG_COMPRESSION_TYPE_DEFAULT;
|
|
r_png_handle->filter = PNG_FILTER_TYPE_DEFAULT;
|
|
}
|
|
|
|
void R_DestroyPNG (qboolean keepData)
|
|
{
|
|
if (!r_png_handle)
|
|
return;
|
|
if (r_png_handle->data && !keepData)
|
|
free(r_png_handle->data);
|
|
if (r_png_handle->fRowPtrs)
|
|
free(r_png_handle->fRowPtrs);
|
|
free(r_png_handle);
|
|
r_png_handle = NULL;
|
|
}
|
|
|
|
void PNGAPI R_ReadPNGData (png_structp png, png_bytep data, png_size_t length)
|
|
{ // called by pnglib
|
|
int i;
|
|
|
|
for (i=0; i<length; i++)
|
|
data[i] = r_png_handle->tmpBuf[r_png_handle->tmpi++]; // give pnglib a some more bytes
|
|
}
|
|
|
|
|
|
/*
|
|
==============
|
|
R_LoadPNG
|
|
==============
|
|
*/
|
|
void R_LoadPNG (char *filename, byte **pic, int *width, int *height)
|
|
{
|
|
png_structp png;
|
|
png_infop pnginfo;
|
|
byte ioBuffer[8192];
|
|
int len;
|
|
byte *raw;
|
|
|
|
*pic = NULL;
|
|
|
|
len = FS_LoadFile (filename, (void **)&raw);
|
|
|
|
if (!raw)
|
|
{ // Knightmare- skip this unless developer >= 2 because it spams the console
|
|
if (developer->integer > 1)
|
|
VID_Printf (PRINT_DEVELOPER, "Bad png file %s\n", filename);
|
|
return;
|
|
}
|
|
|
|
if ( png_sig_cmp(raw,0,4) )
|
|
return;
|
|
|
|
png = png_create_read_struct (PNG_LIBPNG_VER_STRING,0,0,0);
|
|
if (!png)
|
|
return;
|
|
|
|
pnginfo = png_create_info_struct (png);
|
|
|
|
if (!pnginfo) {
|
|
png_destroy_read_struct (&png,&pnginfo,0);
|
|
return;
|
|
}
|
|
|
|
png_set_sig_bytes (png, 0/*sizeof( sig )*/);
|
|
|
|
R_CreatePNG (); // creates the r_png_handle struct
|
|
|
|
r_png_handle->tmpBuf = raw; //buf = whole file content
|
|
r_png_handle->tmpi=0;
|
|
png_set_read_fn (png, ioBuffer, R_ReadPNGData);
|
|
png_read_info (png, pnginfo);
|
|
|
|
png_get_IHDR (png, pnginfo, &r_png_handle->width, &r_png_handle->height, &r_png_handle->bitDepth,
|
|
&r_png_handle->colorType, &r_png_handle->interlace, &r_png_handle->compression, &r_png_handle->filter);
|
|
// ...removed bgColor code here...
|
|
|
|
if (r_png_handle->colorType == PNG_COLOR_TYPE_PALETTE)
|
|
png_set_palette_to_rgb(png);
|
|
if (r_png_handle->colorType == PNG_COLOR_TYPE_GRAY && r_png_handle->bitDepth < 8)
|
|
// png_set_gray_1_2_4_to_8 (png);
|
|
PNG_GRAYFUNC_1_2_4_to_8 (png);
|
|
|
|
// Add alpha channel if present
|
|
if ( png_get_valid(png, pnginfo, PNG_INFO_tRNS) )
|
|
png_set_tRNS_to_alpha (png);
|
|
|
|
// hax: expand 24bit to 32bit
|
|
// if (r_png_handle->bitDepth == 8 && r_png_handle->colorType == PNG_COLOR_TYPE_RGB)
|
|
// png_set_filler (png,255, PNG_FILLER_AFTER);
|
|
|
|
// Expand grayscale to RGB
|
|
if ( (r_png_handle->colorType == PNG_COLOR_TYPE_GRAY) || (r_png_handle->colorType == PNG_COLOR_TYPE_GRAY_ALPHA) )
|
|
png_set_gray_to_rgb (png);
|
|
|
|
if (r_png_handle->bitDepth < 8)
|
|
png_set_expand (png);
|
|
|
|
// hax: expand 24bit to 32bit
|
|
if (r_png_handle->bitDepth == 8 && r_png_handle->colorType == PNG_COLOR_TYPE_RGB)
|
|
png_set_filler (png,255, PNG_FILLER_AFTER);
|
|
|
|
// update the info structure
|
|
png_read_update_info( png, pnginfo );
|
|
|
|
r_png_handle->fRowBytes = (int)png_get_rowbytes (png, pnginfo);
|
|
r_png_handle->bytesPerPixel = png_get_channels (png, pnginfo); // DL Added 30/08/2000
|
|
|
|
R_InitializePNGData ();
|
|
if ((r_png_handle->data)&&(r_png_handle->fRowPtrs))
|
|
png_read_image (png, (png_bytepp)r_png_handle->fRowPtrs);
|
|
|
|
png_read_end (png, pnginfo); // read last information chunks
|
|
|
|
png_destroy_read_struct (&png, &pnginfo, 0);
|
|
|
|
// only load 32 bit by now...
|
|
if (r_png_handle->bitDepth == 8)
|
|
{
|
|
*pic= r_png_handle->data;
|
|
*width = r_png_handle->width;
|
|
*height = r_png_handle->height;
|
|
}
|
|
else
|
|
{
|
|
VID_Printf (PRINT_DEVELOPER, "Bad png color depth: %s\n", filename);
|
|
*pic = NULL;
|
|
free (r_png_handle->data);
|
|
}
|
|
|
|
R_DestroyPNG (true);
|
|
FS_FreeFile ((void *)raw);
|
|
}
|
|
#endif // PNG_SUPPORT
|
|
|
|
/*
|
|
=================================================================
|
|
|
|
JPEG LOADING
|
|
|
|
By Robert 'Heffo' Heffernan
|
|
|
|
=================================================================
|
|
*/
|
|
|
|
void jpg_null (j_decompress_ptr cinfo)
|
|
{
|
|
}
|
|
|
|
unsigned char jpg_fill_input_buffer (j_decompress_ptr cinfo)
|
|
{
|
|
VID_Printf(PRINT_ALL, "Premature end of JPEG data\n");
|
|
return 1;
|
|
}
|
|
|
|
void jpg_skip_input_data (j_decompress_ptr cinfo, long num_bytes)
|
|
{
|
|
|
|
cinfo->src->next_input_byte += (size_t) num_bytes;
|
|
cinfo->src->bytes_in_buffer -= (size_t) num_bytes;
|
|
|
|
if (cinfo->src->bytes_in_buffer < 0)
|
|
VID_Printf(PRINT_ALL, "Premature end of JPEG data\n");
|
|
}
|
|
|
|
//void jpeg_mem_src (j_decompress_ptr cinfo, const byte *mem, unsigned long len)
|
|
void jpeg_mem_src (j_decompress_ptr cinfo, const byte *mem, size_t len)
|
|
{
|
|
cinfo->src = (struct jpeg_source_mgr *)(*cinfo->mem->alloc_small)((j_common_ptr) cinfo, JPOOL_PERMANENT, sizeof(struct jpeg_source_mgr));
|
|
cinfo->src->init_source = jpg_null;
|
|
cinfo->src->fill_input_buffer = jpg_fill_input_buffer;
|
|
cinfo->src->skip_input_data = jpg_skip_input_data;
|
|
cinfo->src->resync_to_restart = jpeg_resync_to_restart;
|
|
cinfo->src->term_source = jpg_null;
|
|
cinfo->src->bytes_in_buffer = len;
|
|
cinfo->src->next_input_byte = mem;
|
|
}
|
|
|
|
#define DSTATE_START 200 /* after create_decompress */
|
|
#define DSTATE_INHEADER 201 /* reading header markers, no SOS yet */
|
|
|
|
/*
|
|
==============
|
|
R_LoadJPG
|
|
==============
|
|
*/
|
|
void R_LoadJPG (char *filename, byte **pic, int *width, int *height)
|
|
{
|
|
struct jpeg_decompress_struct cinfo;
|
|
struct jpeg_error_mgr jerr;
|
|
byte *rawdata, *rgbadata, *scanline, *p, *q;
|
|
int rawsize, i;
|
|
|
|
// Load JPEG file into memory
|
|
rawsize = FS_LoadFile(filename, (void **)&rawdata);
|
|
if (!rawdata)
|
|
{
|
|
// Knightmare- skip this unless developer >= 2 because it spams the console
|
|
if (developer->integer > 1)
|
|
VID_Printf (PRINT_DEVELOPER, "Bad jpg file %s\n", filename);
|
|
return;
|
|
}
|
|
|
|
// Knightmare- check for bad data
|
|
if ( rawdata[6] != 'J'
|
|
|| rawdata[7] != 'F'
|
|
|| rawdata[8] != 'I'
|
|
|| rawdata[9] != 'F') {
|
|
VID_Printf (PRINT_ALL, "Bad jpg file %s\n", filename);
|
|
FS_FreeFile(rawdata);
|
|
return;
|
|
}
|
|
|
|
// Initialise libJpeg Object
|
|
cinfo.err = jpeg_std_error(&jerr);
|
|
jpeg_create_decompress(&cinfo);
|
|
|
|
// Feed JPEG memory into the libJpeg Object
|
|
jpeg_mem_src(&cinfo, rawdata, rawsize);
|
|
|
|
// Process JPEG header
|
|
jpeg_read_header(&cinfo, true); // bombs out here
|
|
|
|
// Start Decompression
|
|
jpeg_start_decompress(&cinfo);
|
|
|
|
// Check Color Components
|
|
if (cinfo.output_components != 3)
|
|
{
|
|
VID_Printf(PRINT_ALL, "Invalid JPEG color components in %s\n", filename);
|
|
jpeg_destroy_decompress(&cinfo);
|
|
FS_FreeFile(rawdata);
|
|
return;
|
|
}
|
|
|
|
// Allocate Memory for decompressed image
|
|
rgbadata = malloc(cinfo.output_width * cinfo.output_height * 4);
|
|
if (!rgbadata)
|
|
{
|
|
VID_Printf(PRINT_ALL, "Insufficient RAM for JPEG buffer\n");
|
|
jpeg_destroy_decompress(&cinfo);
|
|
FS_FreeFile(rawdata);
|
|
return;
|
|
}
|
|
|
|
// Pass sizes to output
|
|
*width = cinfo.output_width; *height = cinfo.output_height;
|
|
|
|
// Allocate Scanline buffer
|
|
scanline = malloc(cinfo.output_width * 3);
|
|
if (!scanline)
|
|
{
|
|
VID_Printf(PRINT_ALL, "Insufficient RAM for JPEG scanline buffer\n");
|
|
free(rgbadata);
|
|
jpeg_destroy_decompress(&cinfo);
|
|
FS_FreeFile(rawdata);
|
|
return;
|
|
}
|
|
|
|
// Read Scanlines, and expand from RGB to RGBA
|
|
q = rgbadata;
|
|
while (cinfo.output_scanline < cinfo.output_height)
|
|
{
|
|
p = scanline;
|
|
jpeg_read_scanlines(&cinfo, &scanline, 1);
|
|
|
|
for (i=0; i<cinfo.output_width; i++)
|
|
{
|
|
q[0] = p[0];
|
|
q[1] = p[1];
|
|
q[2] = p[2];
|
|
q[3] = 255;
|
|
|
|
p+=3; q+=4;
|
|
}
|
|
}
|
|
|
|
// Free the scanline buffer
|
|
free(scanline);
|
|
|
|
// Finish Decompression
|
|
jpeg_finish_decompress(&cinfo);
|
|
|
|
// Destroy JPEG object
|
|
jpeg_destroy_decompress(&cinfo);
|
|
|
|
// Free raw data buffer
|
|
FS_FreeFile(rawdata);
|
|
|
|
// Return the 'rgbadata'
|
|
*pic = rgbadata;
|
|
}
|
|
|
|
|
|
/*
|
|
====================================================================
|
|
|
|
IMAGE FLOOD FILLING
|
|
|
|
====================================================================
|
|
*/
|
|
|
|
|
|
/*
|
|
=================
|
|
Mod_FloodFillSkin
|
|
|
|
Fill background pixels so mipmapping doesn't have haloes
|
|
=================
|
|
*/
|
|
|
|
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 ) \
|
|
{ \
|
|
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]; \
|
|
}
|
|
|
|
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 << 0)) // 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))
|
|
{
|
|
//printf( "not filling skin from %d to %d\n", fillcolor, filledcolor );
|
|
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;
|
|
}
|
|
}
|
|
|
|
//=======================================================
|
|
|
|
|
|
|
|
/*
|
|
================
|
|
GL_ResampleTextureLerpLine
|
|
from DarkPlaces
|
|
================
|
|
*/
|
|
|
|
void GL_ResampleTextureLerpLine (byte *in, byte *out, int inwidth, int outwidth)
|
|
{
|
|
int j, xi, oldx = 0, f, fstep, l1, l2, endx;
|
|
|
|
fstep = (int) (inwidth*65536.0f/outwidth);
|
|
endx = (inwidth-1);
|
|
for (j = 0,f = 0;j < outwidth;j++, f += fstep)
|
|
{
|
|
xi = (int) f >> 16;
|
|
if (xi != oldx)
|
|
{
|
|
in += (xi - oldx) * 4;
|
|
oldx = xi;
|
|
}
|
|
if (xi < endx)
|
|
{
|
|
l2 = f & 0xFFFF;
|
|
l1 = 0x10000 - l2;
|
|
*out++ = (byte) ((in[0] * l1 + in[4] * l2) >> 16);
|
|
*out++ = (byte) ((in[1] * l1 + in[5] * l2) >> 16);
|
|
*out++ = (byte) ((in[2] * l1 + in[6] * l2) >> 16);
|
|
*out++ = (byte) ((in[3] * l1 + in[7] * l2) >> 16);
|
|
}
|
|
else // last pixel of the line has no pixel to lerp to
|
|
{
|
|
*out++ = in[0];
|
|
*out++ = in[1];
|
|
*out++ = in[2];
|
|
*out++ = in[3];
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
GL_ResampleTexture
|
|
================
|
|
*/
|
|
void GL_ResampleTexture (void *indata, int inwidth, int inheight, void *outdata, int outwidth, int outheight)
|
|
{
|
|
int i, j, yi, oldy, f, fstep, l1, l2, endy = (inheight-1);
|
|
|
|
byte *inrow, *out, *row1, *row2;
|
|
out = outdata;
|
|
fstep = (int) (inheight*65536.0f/outheight);
|
|
|
|
row1 = malloc(outwidth*4);
|
|
row2 = malloc(outwidth*4);
|
|
inrow = indata;
|
|
oldy = 0;
|
|
GL_ResampleTextureLerpLine (inrow, row1, inwidth, outwidth);
|
|
GL_ResampleTextureLerpLine (inrow + inwidth*4, row2, inwidth, outwidth);
|
|
for (i = 0, f = 0;i < outheight;i++,f += fstep)
|
|
{
|
|
yi = f >> 16;
|
|
if (yi != oldy)
|
|
{
|
|
inrow = (byte *)indata + inwidth*4*yi;
|
|
if (yi == oldy+1)
|
|
memcpy(row1, row2, outwidth*4);
|
|
else
|
|
GL_ResampleTextureLerpLine (inrow, row1, inwidth, outwidth);
|
|
|
|
if (yi < endy)
|
|
GL_ResampleTextureLerpLine (inrow + inwidth*4, row2, inwidth, outwidth);
|
|
else
|
|
memcpy(row2, row1, outwidth*4);
|
|
oldy = yi;
|
|
}
|
|
if (yi < endy)
|
|
{
|
|
l2 = f & 0xFFFF;
|
|
l1 = 0x10000 - l2;
|
|
for (j = 0;j < outwidth;j++)
|
|
{
|
|
*out++ = (byte) ((*row1++ * l1 + *row2++ * l2) >> 16);
|
|
*out++ = (byte) ((*row1++ * l1 + *row2++ * l2) >> 16);
|
|
*out++ = (byte) ((*row1++ * l1 + *row2++ * l2) >> 16);
|
|
*out++ = (byte) ((*row1++ * l1 + *row2++ * l2) >> 16);
|
|
}
|
|
row1 -= outwidth*4;
|
|
row2 -= outwidth*4;
|
|
}
|
|
else // last line has no pixels to lerp to
|
|
{
|
|
for (j = 0;j < outwidth;j++)
|
|
{
|
|
*out++ = *row1++;
|
|
*out++ = *row1++;
|
|
*out++ = *row1++;
|
|
*out++ = *row1++;
|
|
}
|
|
row1 -= outwidth*4;
|
|
}
|
|
}
|
|
free(row1);
|
|
free(row2);
|
|
}
|
|
/*
|
|
void GL_ResampleTexture (unsigned *in, int inwidth, int inheight, unsigned *out, int outwidth, int outheight)
|
|
{
|
|
int i, j;
|
|
unsigned *inrow, *inrow2;
|
|
unsigned frac, fracstep;
|
|
unsigned p1[1024], p2[1024];
|
|
byte *pix1, *pix2, *pix3, *pix4;
|
|
|
|
fracstep = inwidth*0x10000/outwidth;
|
|
|
|
frac = fracstep>>2;
|
|
for (i=0 ; i<outwidth ; i++)
|
|
{
|
|
p1[i] = 4*(frac>>16);
|
|
frac += fracstep;
|
|
}
|
|
frac = 3*(fracstep>>2);
|
|
for (i=0 ; i<outwidth ; i++)
|
|
{
|
|
p2[i] = 4*(frac>>16);
|
|
frac += fracstep;
|
|
}
|
|
|
|
for (i=0 ; i<outheight ; i++, out += outwidth)
|
|
{
|
|
inrow = in + inwidth*(int)((i+0.25)*inheight/outheight);
|
|
inrow2 = in + inwidth*(int)((i+0.75)*inheight/outheight);
|
|
frac = fracstep >> 1;
|
|
for (j=0 ; j<outwidth ; j++)
|
|
{
|
|
pix1 = (byte *)inrow + p1[j];
|
|
pix2 = (byte *)inrow + p2[j];
|
|
pix3 = (byte *)inrow2 + p1[j];
|
|
pix4 = (byte *)inrow2 + p2[j];
|
|
((byte *)(out+j))[0] = (pix1[0] + pix2[0] + pix3[0] + pix4[0])>>2;
|
|
((byte *)(out+j))[1] = (pix1[1] + pix2[1] + pix3[1] + pix4[1])>>2;
|
|
((byte *)(out+j))[2] = (pix1[2] + pix2[2] + pix3[2] + pix4[2])>>2;
|
|
((byte *)(out+j))[3] = (pix1[3] + pix2[3] + pix3[3] + pix4[3])>>2;
|
|
}
|
|
}
|
|
}*/
|
|
|
|
|
|
#define MAX_UPSAMPLE_FACTOR 5 // limit to 5 (32x scaling)
|
|
/*
|
|
================
|
|
GL_UpscaleTexture
|
|
================
|
|
*/
|
|
void GL_UpscaleTexture (void *indata, int inwidth, int inheight, void *outdata, int scaleFactor, qboolean blend)
|
|
{
|
|
if ( !indata || !outdata || (scaleFactor < 1) || (scaleFactor > MAX_UPSAMPLE_FACTOR) )
|
|
return;
|
|
|
|
// if ( (r_font_upscale->integer >= 2) && (scaleFactor == 1) ) {
|
|
if ( blend && (scaleFactor == 1) ) {
|
|
R_Upscale2x_Render (outdata, indata, inwidth, inheight);
|
|
}
|
|
// else if ( (r_font_upscale->integer >= 2) && (scaleFactor == 2) ) {
|
|
else if ( blend && (scaleFactor == 2) ) {
|
|
R_Upscale4x_Render (outdata, indata, inwidth, inheight);
|
|
}
|
|
else // if (scaleFactor >= 3) // just block-copy each pixel
|
|
{
|
|
int sizeMult = (1 << scaleFactor);
|
|
int x, y, i, j;
|
|
unsigned *inL, *inP, *outL, *outP;
|
|
|
|
for (y = 0; y < inheight; y++)
|
|
{
|
|
inL = (unsigned *)indata + (y * inwidth);
|
|
outL = (unsigned *)outdata + (y * inwidth * sizeMult * sizeMult);
|
|
|
|
for (x = 0; x < inwidth; x++)
|
|
{
|
|
inP = inL + x;
|
|
outP = outL + (x * sizeMult);
|
|
// copy each input pixel to sizeMult^2 pixels
|
|
for (i = 0; i < sizeMult; i++)
|
|
{
|
|
for (j = 0; j < sizeMult; j++)
|
|
{
|
|
*(outP + (i * inwidth * sizeMult) + j) = *inP;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
================
|
|
GL_LightScaleTexture
|
|
|
|
Scale up the pixel values in a texture to increase the
|
|
lighting range
|
|
================
|
|
*/
|
|
void GL_LightScaleTexture (unsigned *in, int inwidth, int inheight, qboolean only_gamma )
|
|
{
|
|
if ( only_gamma )
|
|
{
|
|
int i, c;
|
|
byte *p;
|
|
|
|
p = (byte *)in;
|
|
|
|
c = inwidth*inheight;
|
|
for (i=0 ; i<c ; i++, p+=4)
|
|
{
|
|
p[0] = gammatable[p[0]];
|
|
p[1] = gammatable[p[1]];
|
|
p[2] = gammatable[p[2]];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
int i, c;
|
|
byte *p;
|
|
|
|
p = (byte *)in;
|
|
|
|
c = inwidth*inheight;
|
|
for (i=0 ; i<c ; i++, p+=4)
|
|
{
|
|
p[0] = gammatable[intensitytable[p[0]]];
|
|
p[1] = gammatable[intensitytable[p[1]]];
|
|
p[2] = gammatable[intensitytable[p[2]]];
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
GL_MipMap
|
|
|
|
Operates in place, quartering the size of the texture
|
|
================
|
|
*/
|
|
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_BuildPalettedTexture
|
|
================
|
|
*/
|
|
void GL_BuildPalettedTexture (unsigned char *paletted_texture, unsigned char *scaled, int scaled_width, int scaled_height)
|
|
{
|
|
int i;
|
|
|
|
for ( i = 0; i < scaled_width * scaled_height; i++ )
|
|
{
|
|
unsigned int r, g, b, c;
|
|
|
|
r = ( scaled[0] >> 3 ) & 31;
|
|
g = ( scaled[1] >> 2 ) & 63;
|
|
b = ( scaled[2] >> 3 ) & 31;
|
|
|
|
c = r | ( g << 5 ) | ( b << 11 );
|
|
|
|
paletted_texture[i] = glState.d_16to8table[c];
|
|
|
|
scaled += 4;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
===============
|
|
here starts modified code
|
|
by Heffo/changes by Nexus
|
|
===============
|
|
*/
|
|
|
|
static int upload_width, upload_height; //** DMP made local to module
|
|
static qboolean uploaded_paletted; //** DMP ditto
|
|
|
|
/*
|
|
static qboolean IsPowerOf2 (int value)
|
|
{
|
|
int i = 1;
|
|
while (1) {
|
|
if (value == i)
|
|
return true;
|
|
if (i > value)
|
|
return false;
|
|
i <<= 1;
|
|
}
|
|
}
|
|
*/
|
|
|
|
int NearestPowerOf2 (int size)
|
|
{
|
|
int i = 2;
|
|
|
|
// NeVo - infinite loop bug-fix
|
|
// if (size == 1)
|
|
if (size < 2) // FS: Better fix
|
|
return size;
|
|
|
|
while (1)
|
|
{
|
|
i <<= 1;
|
|
if (size == i)
|
|
return i;
|
|
if (size > i && size < (i << 1))
|
|
{
|
|
if (size >= ((i + (i << 1))/2))
|
|
return i << 1;
|
|
else
|
|
return i;
|
|
}
|
|
};
|
|
}
|
|
|
|
|
|
/*
|
|
===============
|
|
GL_Upload32
|
|
|
|
Returns has_alpha
|
|
===============
|
|
*/
|
|
//#define USE_GLMIPMAP
|
|
qboolean GL_Upload32 (unsigned *data, int width, int height, imagetype_t type)
|
|
{
|
|
unsigned *scaled = NULL;
|
|
int scaled_width, scaled_height;
|
|
int i, c, comp, idealImgRes = 256, upscaleFactor = 0;
|
|
qboolean mipmap, hasAlpha, isNPOT, resampled = false;
|
|
byte *scan;
|
|
|
|
mipmap = ( (type != it_pic) && (type != it_font) && (type != it_scrap) && (type != it_sky) );
|
|
uploaded_paletted = false;
|
|
|
|
// get best upscale size for old fonts
|
|
if ( (type == it_font) && (width == 128) && (height == 128) && r_font_upscale->integer ) {
|
|
idealImgRes = NearestPowerOf2(min(vid.width, vid.height)) / 2;
|
|
idealImgRes = min(idealImgRes, glConfig.max_texsize);
|
|
idealImgRes = max(idealImgRes, 256);
|
|
// VID_Printf (PRINT_DEVELOPER, "GL_Upload32: ideal font res is %d.\n", idealImgRes);
|
|
}
|
|
// get best upscale size for scrap
|
|
if ( (type == it_scrap) && r_scrap_upscale->integer ) {
|
|
idealImgRes = NearestPowerOf2(min(vid.width, vid.height));
|
|
idealImgRes = min(idealImgRes, glConfig.max_texsize);
|
|
idealImgRes = max(idealImgRes, 256);
|
|
// VID_Printf (PRINT_DEVELOPER, "GL_Upload32: ideal scrap res is %d.\n", idealImgRes);
|
|
}
|
|
|
|
//
|
|
// scan the texture for any non-255 alpha
|
|
//
|
|
c = width*height;
|
|
scan = ((byte *)data) + 3;
|
|
hasAlpha = false;
|
|
for (i = 0; i < c; i++, scan += 4)
|
|
{
|
|
if ( *scan != 255 ) {
|
|
hasAlpha = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Heffo - ARB Texture Compression
|
|
qglHint(GL_TEXTURE_COMPRESSION_HINT_ARB, GL_NICEST);
|
|
comp = (glState.texture_compression && (type != it_pic) && (type != it_font) && (type != it_scrap)) ? GL_COMPRESSED_RGBA_ARB : GL_RGBA;
|
|
|
|
//
|
|
// find sizes to scale to
|
|
//
|
|
if ( glConfig.arbTextureNonPowerOfTwo && (!mipmap || r_nonpoweroftwo_mipmaps->integer) ) {
|
|
scaled_width = width;
|
|
scaled_height = height;
|
|
isNPOT = ( (NearestPowerOf2(width) != width) || (NearestPowerOf2(height) != height) );
|
|
}
|
|
else {
|
|
scaled_width = NearestPowerOf2 (width);
|
|
scaled_height = NearestPowerOf2 (height);
|
|
isNPOT = false;
|
|
}
|
|
|
|
if (scaled_width > glConfig.max_texsize)
|
|
scaled_width = glConfig.max_texsize;
|
|
if (scaled_height > glConfig.max_texsize)
|
|
scaled_height = glConfig.max_texsize;
|
|
|
|
//
|
|
// allow sampling down of the world textures for speed
|
|
//
|
|
if (mipmap && r_picmip->integer > 0)
|
|
{
|
|
int maxsize;
|
|
|
|
if (r_picmip->integer == 1) // clamp to 1024x1024
|
|
maxsize = 1024;
|
|
else if (r_picmip->integer == 2) // clamp to 512x512
|
|
maxsize = 512;
|
|
else if (r_picmip->integer == 3) // clamp to 256x256
|
|
maxsize = 256;
|
|
else // clamp to 128x128
|
|
maxsize = 128;
|
|
|
|
while (1) {
|
|
if (scaled_width <= maxsize && scaled_height <= maxsize)
|
|
break;
|
|
scaled_width >>= 1;
|
|
scaled_height >>= 1;
|
|
}
|
|
// scaled_width >>= r_picmip->integer;
|
|
// scaled_height >>= r_picmip->integer;
|
|
}
|
|
|
|
//
|
|
// resample texture if needed
|
|
//
|
|
if ( ( (type == it_font) && r_font_upscale->integer ) || ( (type == it_scrap) && r_scrap_upscale->integer )
|
|
&& (scaled_width == scaled_height) && ((scaled_width * 2) <= idealImgRes) ) // scale up fonts
|
|
{
|
|
while (1) {
|
|
if ( ((scaled_width * 2) > idealImgRes) && ((scaled_height * 2) > idealImgRes) )
|
|
break;
|
|
if (upscaleFactor >= MAX_UPSAMPLE_FACTOR) // don't go past 32x scaling
|
|
break;
|
|
scaled_width <<= 1;
|
|
scaled_height <<= 1;
|
|
upscaleFactor++;
|
|
}
|
|
if (upscaleFactor > 0)
|
|
{
|
|
upscaleFactor = min(upscaleFactor, MAX_UPSAMPLE_FACTOR); // clamp to max upscale factor
|
|
VID_Printf (PRINT_DEVELOPER, "GL_Upload32: scaling font image from %dx%d to %dx%d.\n", width, height, scaled_width, scaled_height);
|
|
scaled = malloc((scaled_width * scaled_height) * 4);
|
|
if (type == it_font)
|
|
GL_UpscaleTexture (data, width, height, scaled, upscaleFactor, (r_font_upscale->integer >= 2));
|
|
else
|
|
GL_UpscaleTexture (data, width, height, scaled, upscaleFactor, (r_scrap_upscale->integer >= 2));
|
|
resampled = true;
|
|
}
|
|
else {
|
|
scaled_width = width;
|
|
scaled_height = height;
|
|
scaled = data;
|
|
resampled = false;
|
|
}
|
|
}
|
|
else if (scaled_width != width || scaled_height != height) {
|
|
scaled = malloc((scaled_width * scaled_height) * 4);
|
|
GL_ResampleTexture (data, width, height, scaled, scaled_width, scaled_height);
|
|
resampled = true;
|
|
}
|
|
else {
|
|
scaled_width = width;
|
|
scaled_height = height;
|
|
scaled = data;
|
|
resampled = false;
|
|
}
|
|
|
|
if (!glState.gammaRamp)
|
|
GL_LightScaleTexture (scaled, scaled_width, scaled_height, !mipmap );
|
|
|
|
//
|
|
// generate mipmaps and upload
|
|
//
|
|
#ifdef USE_GLMIPMAP
|
|
qglTexImage2D (GL_TEXTURE_2D, 0, comp, scaled_width, scaled_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, scaled);
|
|
if (mipmap)
|
|
{
|
|
int mip_width, mip_height, miplevel = 0;
|
|
|
|
mip_width = scaled_width; mip_height = scaled_height;
|
|
while (mip_width > 1 || mip_height > 1)
|
|
{
|
|
GL_MipMap ((byte *)scaled, mip_width, mip_height);
|
|
mip_width = max(mip_width>>1, 1);
|
|
mip_height = max(mip_height>>1, 1);
|
|
miplevel++;
|
|
qglTexImage2D (GL_TEXTURE_2D, miplevel, comp, mip_width, mip_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, scaled);
|
|
}
|
|
}
|
|
#else
|
|
if (mipmap)
|
|
{
|
|
if (glState.sgis_mipmap) {
|
|
qglTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP_SGIS, true);
|
|
qglTexImage2D (GL_TEXTURE_2D, 0, comp, scaled_width, scaled_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, scaled);
|
|
}
|
|
else
|
|
gluBuild2DMipmaps (GL_TEXTURE_2D, comp, scaled_width, scaled_height, GL_RGBA, GL_UNSIGNED_BYTE, scaled);
|
|
}
|
|
else
|
|
qglTexImage2D (GL_TEXTURE_2D, 0, comp, scaled_width, scaled_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, scaled);
|
|
#endif
|
|
|
|
// if (scaled_width != width || scaled_height != height)
|
|
if (resampled)
|
|
free(scaled);
|
|
|
|
upload_width = scaled_width; upload_height = scaled_height;
|
|
|
|
qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, (mipmap) ? gl_filter_min : gl_filter_max);
|
|
qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max);
|
|
|
|
// Set anisotropic filter if supported and enabled
|
|
if (mipmap && glConfig.anisotropic && r_anisotropic->value)
|
|
qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, r_anisotropic->value);
|
|
|
|
return hasAlpha;
|
|
}
|
|
|
|
|
|
/*
|
|
===============
|
|
GL_Upload8
|
|
|
|
Returns has_alpha
|
|
===============
|
|
*/
|
|
qboolean GL_Upload8 (byte *data, int width, int height, imagetype_t type)
|
|
{
|
|
// unsigned trans[512*256];
|
|
unsigned *trans = NULL; // Knightmare- changed to dynamic allocation
|
|
int i, s;
|
|
int p;
|
|
qboolean has_alpha; // Knightmare added
|
|
|
|
s = width*height;
|
|
|
|
// if (s > sizeof(trans)/4)
|
|
// VID_Error (ERR_DROP, "GL_Upload8: too large");
|
|
|
|
trans = (unsigned *)malloc(s*4); // Knightmare- changed to dynamic allocation
|
|
|
|
for (i=0 ; i<s ; i++)
|
|
{
|
|
p = data[i];
|
|
trans[i] = 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
|
|
((byte *)&trans[i])[0] = ((byte *)&d_8to24table[p])[0];
|
|
((byte *)&trans[i])[1] = ((byte *)&d_8to24table[p])[1];
|
|
((byte *)&trans[i])[2] = ((byte *)&d_8to24table[p])[2];
|
|
}
|
|
}
|
|
|
|
// return GL_Upload32 (trans, width, height, type);
|
|
|
|
// Knightmare- changed to dynamic allocation
|
|
has_alpha = GL_Upload32 (trans, width, height, type);
|
|
|
|
free (trans);
|
|
trans = NULL;
|
|
|
|
return has_alpha;
|
|
// end Knightmare
|
|
}
|
|
|
|
|
|
/*
|
|
================
|
|
R_LoadPic
|
|
|
|
This is also used as an entry point for the generated notexture
|
|
Nexus - changes for hires-textures
|
|
================
|
|
*/
|
|
image_t *R_LoadPic (char *name, byte *pic, int width, int height, imagetype_t type, int bits)
|
|
{
|
|
image_t *image;
|
|
int i;
|
|
// Nexus'added vars
|
|
int nameLen;
|
|
char refName[128];
|
|
|
|
// find a free image_t
|
|
for (i=0, image=gltextures ; i<numgltextures ; i++,image++)
|
|
{
|
|
if (!image->texnum)
|
|
break;
|
|
}
|
|
if (i == numgltextures)
|
|
{
|
|
if (numgltextures == MAX_GLTEXTURES)
|
|
VID_Error (ERR_DROP, "MAX_GLTEXTURES");
|
|
numgltextures++;
|
|
}
|
|
image = &gltextures[i];
|
|
|
|
if (strlen(name) >= sizeof(image->name))
|
|
VID_Error (ERR_DROP, "Draw_LoadPic: \"%s\" is too long", name);
|
|
// strncpy (image->name, name);
|
|
Q_strncpyz (image->name, sizeof(image->name), name);
|
|
image->hash = Com_HashFileName(name, 0, false); // Knightmare added
|
|
image->registration_sequence = registration_sequence;
|
|
|
|
image->width = width;
|
|
image->height = height;
|
|
image->type = type;
|
|
image->replace_scale_w = image->replace_scale_h = 1.0f; // Knightmare added
|
|
|
|
if (type == it_skin && bits == 8)
|
|
R_FloodFillSkin(pic, width, height);
|
|
|
|
// replacement scaling hack for TGA/JPEG HUD images and skins
|
|
// TODO: replace this with shaders as soon as they are supported
|
|
nameLen = (int)strlen(name);
|
|
// strncpy(s,name);
|
|
Q_strncpyz(refName, sizeof(refName), name);
|
|
|
|
// Load .pcx for size refereence, check if we have a tga/jpg/png pic
|
|
#ifdef PNG_SUPPORT
|
|
if ( (nameLen >= 5) && ((type == it_pic) || (type == it_font)) && (!strcmp(refName+nameLen-4, ".tga") || !strcmp(refName+nameLen-4, ".png") || !strcmp(refName+nameLen-4, ".jpg")) )
|
|
#else // PNG_SUPPORT
|
|
if ( (nameLen >= 5) && ((type == it_pic) || (type == it_font)) && (!strcmp(refName+nameLen-4, ".tga") || !strcmp(refName+nameLen-4, ".jpg")) )
|
|
#endif // PNG_SUPPORT
|
|
{
|
|
byte *pic, *palette;
|
|
int pcxwidth, pcxheight;
|
|
refName[nameLen-3]='p'; refName[nameLen-2]='c'; refName[nameLen-1]='x'; // replace extension
|
|
LoadPCX (refName, &pic, &palette, &pcxwidth, &pcxheight); // load .pcx file
|
|
|
|
if (pic && (pcxwidth > 0) && (pcxheight > 0)) {
|
|
image->replace_scale_w = (float)pcxwidth/image->width;
|
|
image->replace_scale_h = (float)pcxheight/image->height;
|
|
}
|
|
if (pic) free(pic);
|
|
if (palette) free(palette);
|
|
}
|
|
|
|
// load little pics into the scrap
|
|
if ( (image->type == it_pic) && (bits == 8)
|
|
&& (image->width < 64) && (image->height < 64) )
|
|
{
|
|
int x, y;
|
|
int i, j, k;
|
|
int texnum;
|
|
|
|
texnum = Scrap_AllocBlock (image->width, image->height, &x, &y);
|
|
if (texnum == -1)
|
|
goto nonscrap;
|
|
scrap_dirty = true;
|
|
|
|
// copy the texels into the scrap block
|
|
k = 0;
|
|
for (i=0 ; i<image->height ; i++)
|
|
for (j=0 ; j<image->width ; j++, k++)
|
|
scrap_texels[texnum][(y+i)*SCRAP_BLOCK_WIDTH + x + j] = pic[k];
|
|
image->texnum = TEXNUM_SCRAPS + texnum;
|
|
image->scrap = true;
|
|
image->has_alpha = true;
|
|
image->sl = (x+0.01)/(float)SCRAP_BLOCK_WIDTH;
|
|
image->sh = (x+image->width-0.01)/(float)SCRAP_BLOCK_WIDTH;
|
|
image->tl = (y+0.01)/(float)SCRAP_BLOCK_WIDTH;
|
|
image->th = (y+image->height-0.01)/(float)SCRAP_BLOCK_WIDTH;
|
|
}
|
|
else
|
|
{
|
|
nonscrap:
|
|
image->scrap = false;
|
|
image->texnum = TEXNUM_IMAGES + (image - gltextures);
|
|
GL_Bind(image->texnum);
|
|
if (bits == 8)
|
|
image->has_alpha = GL_Upload8 (pic, width, height, image->type);
|
|
else
|
|
image->has_alpha = GL_Upload32 ((unsigned *)pic, width, height, image->type);
|
|
image->upload_width = upload_width; // after power of 2 and scales
|
|
image->upload_height = upload_height;
|
|
image->paletted = uploaded_paletted;
|
|
image->sl = 0;
|
|
image->sh = 1;
|
|
image->tl = 0;
|
|
image->th = 1;
|
|
}
|
|
|
|
return image;
|
|
}
|
|
|
|
|
|
// store the names of last images that failed to load
|
|
#define NUM_FAIL_IMAGES 1024
|
|
char lastFailedImage[NUM_FAIL_IMAGES][MAX_OSPATH];
|
|
unsigned int lastFailedImageHash[NUM_FAIL_IMAGES];
|
|
static unsigned failedImgListIndex;
|
|
|
|
/*
|
|
===============
|
|
R_InitFailedImgList
|
|
===============
|
|
*/
|
|
void R_InitFailedImgList (void)
|
|
{
|
|
int i;
|
|
|
|
for (i=0; i<NUM_FAIL_IMAGES; i++) {
|
|
Com_sprintf(lastFailedImage[i], sizeof(lastFailedImage[i]), "\0");
|
|
lastFailedImageHash[i] = 0;
|
|
}
|
|
|
|
failedImgListIndex = 0;
|
|
}
|
|
|
|
/*
|
|
===============
|
|
R_CheckImgFailed
|
|
===============
|
|
*/
|
|
qboolean R_CheckImgFailed (char *name)
|
|
{
|
|
int i;
|
|
unsigned int hash;
|
|
|
|
hash = Com_HashFileName(name, 0, false);
|
|
for (i=0; i<NUM_FAIL_IMAGES; i++)
|
|
{
|
|
if (hash == lastFailedImageHash[i]) { // compare hash first
|
|
if (lastFailedImage[i] && strlen(lastFailedImage[i])
|
|
&& !strcmp(name, lastFailedImage[i]))
|
|
{ // we already tried to load this image, didn't find it
|
|
//VID_Printf (PRINT_ALL, "R_CheckImgFailed: found %s on failed to load list\n", name);
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
===============
|
|
R_AddToFailedImgList
|
|
===============
|
|
*/
|
|
void R_AddToFailedImgList (char *name)
|
|
{
|
|
if (!strncmp(name, "save/", 5)) // don't add saveshots
|
|
return;
|
|
|
|
//VID_Printf (PRINT_ALL, "R_AddToFailedImgList: adding %s to failed to load list\n", name);
|
|
|
|
Com_sprintf(lastFailedImage[failedImgListIndex], sizeof(lastFailedImage[failedImgListIndex]), "%s", name);
|
|
lastFailedImageHash[failedImgListIndex] = Com_HashFileName(name, 0, false);
|
|
failedImgListIndex++;
|
|
|
|
// wrap around to start of list
|
|
if (failedImgListIndex >= NUM_FAIL_IMAGES)
|
|
failedImgListIndex = 0;
|
|
}
|
|
|
|
/*
|
|
================
|
|
R_LoadWal
|
|
================
|
|
*/
|
|
image_t *R_LoadWal (char *name, imagetype_t type)
|
|
{
|
|
miptex_t *mt;
|
|
int width, height, ofs;
|
|
image_t *image;
|
|
|
|
FS_LoadFile (name, (void **)&mt);
|
|
if (!mt)
|
|
{
|
|
if (type == it_wall)
|
|
VID_Printf (PRINT_ALL, "R_FindImage: can't load %s\n", name);
|
|
//return glMedia.notexture;
|
|
return NULL;
|
|
}
|
|
|
|
width = LittleLong (mt->width);
|
|
height = LittleLong (mt->height);
|
|
ofs = LittleLong (mt->offsets[0]);
|
|
|
|
image = R_LoadPic (name, (byte *)mt + ofs, width, height, it_wall, 8);
|
|
|
|
FS_FreeFile ((void *)mt);
|
|
|
|
return image;
|
|
}
|
|
|
|
|
|
/*
|
|
===============
|
|
R_FindImage
|
|
|
|
Finds or loads the given image
|
|
===============
|
|
*/
|
|
image_t *R_FindImage (char *name, imagetype_t type)
|
|
{
|
|
image_t *image;
|
|
int i, len;
|
|
byte *pic, *palette;
|
|
unsigned int hash;
|
|
int width, height;
|
|
char s[128];
|
|
char *tmp;
|
|
|
|
if (!name)
|
|
return NULL;
|
|
len = (int)strlen(name);
|
|
if (len<5)
|
|
return NULL;
|
|
|
|
// fix up bad image paths
|
|
tmp = name;
|
|
while ( *tmp != 0 )
|
|
{
|
|
if ( *tmp == '\\' )
|
|
*tmp = '/';
|
|
tmp++;
|
|
}
|
|
|
|
// look for it
|
|
hash = Com_HashFileName(name, 0, false);
|
|
for (i=0, image=gltextures; i<numgltextures; i++,image++)
|
|
{
|
|
if (hash == image->hash) { // compare hash first
|
|
if (!strcmp(name, image->name))
|
|
{
|
|
image->registration_sequence = registration_sequence;
|
|
return image;
|
|
}
|
|
}
|
|
}
|
|
|
|
// don't try again to load an image that just failed
|
|
if (R_CheckImgFailed (name))
|
|
{
|
|
if (!strcmp(name+len-4, ".tga"))
|
|
{
|
|
#ifdef PNG_SUPPORT
|
|
// fall back to png
|
|
Q_strncpyz (s, sizeof(s), name);
|
|
s[len-3]='p'; s[len-2]='n'; s[len-1]='g';
|
|
return R_FindImage(s,type);
|
|
#else // PNG_SUPPORT
|
|
// fall back to jpg
|
|
Q_strncpyz(s, sizeof(s), name);
|
|
s[len-3]='j'; s[len-2]='p'; s[len-1]='g';
|
|
return R_FindImage(s,type);
|
|
#endif // PNG_SUPPORT
|
|
}
|
|
#ifdef PNG_SUPPORT
|
|
else if (!strcmp(name+len-4, ".png"))
|
|
{ // fall back to jpg
|
|
Q_strncpyz (s, sizeof(s), name);
|
|
s[len-3]='j'; s[len-2]='p'; s[len-1]='g';
|
|
return R_FindImage(s,type);
|
|
}
|
|
#endif // PNG_SUPPORT
|
|
else
|
|
return NULL;
|
|
}
|
|
|
|
// MrG's automatic JPG & TGA loading
|
|
// search for TGAs or JPGs to replace .pcx and .wal images
|
|
if (!strcmp(name+len-4, ".pcx") || !strcmp(name+len-4, ".wal"))
|
|
{
|
|
// strncpy(s, name);
|
|
Q_strncpyz (s, sizeof(s), name);
|
|
s[len-3]='t'; s[len-2]='g'; s[len-1]='a';
|
|
image = R_FindImage(s,type);
|
|
if (image)
|
|
return image;
|
|
}
|
|
|
|
//
|
|
// load the pic from disk
|
|
//
|
|
pic = NULL;
|
|
palette = NULL;
|
|
|
|
if (!strcmp(name+len-4, ".pcx"))
|
|
{
|
|
LoadPCX (name, &pic, &palette, &width, &height);
|
|
if (pic)
|
|
image = R_LoadPic (name, pic, width, height, type, 8);
|
|
else
|
|
image = NULL;
|
|
}
|
|
else if (!strcmp(name+len-4, ".wal"))
|
|
{
|
|
image = R_LoadWal (name, type);
|
|
}
|
|
else if (!strcmp(name+len-4, ".tga"))
|
|
{
|
|
R_LoadTGA (name, &pic, &width, &height);
|
|
if (pic)
|
|
image = R_LoadPic (name, pic, width, height, type, 32);
|
|
#ifdef PNG_SUPPORT
|
|
else { // fall back to png
|
|
R_AddToFailedImgList(name);
|
|
Q_strncpyz (s, sizeof(s), name);
|
|
s[len-3]='p'; s[len-2]='n'; s[len-1]='g';
|
|
return R_FindImage(s,type);
|
|
}
|
|
#else // PNG_SUPPORT
|
|
else { // fall back to jpg
|
|
R_AddToFailedImgList(name);
|
|
Q_strncpyz(s, sizeof(s), name);
|
|
s[len-3]='j'; s[len-2]='p'; s[len-1]='g';
|
|
return R_FindImage(s,type);
|
|
}
|
|
#endif // PNG_SUPPORT
|
|
}
|
|
#ifdef PNG_SUPPORT
|
|
else if (!strcmp(name+len-4, ".png"))
|
|
{
|
|
R_LoadPNG (name, &pic, &width, &height);
|
|
if (pic)
|
|
image = R_LoadPic(name, pic, width, height, type, 32);
|
|
else { // fall back to jpg
|
|
R_AddToFailedImgList(name);
|
|
Q_strncpyz (s, sizeof(s), name);
|
|
s[len-3]='j'; s[len-2]='p'; s[len-1]='g';
|
|
return R_FindImage(s,type);
|
|
}
|
|
}
|
|
#endif // PNG_SUPPORT
|
|
else if (!strcmp(name+len-4, ".jpg")) // Heffo - JPEG support
|
|
{
|
|
R_LoadJPG(name, &pic, &width, &height);
|
|
if (pic)
|
|
image = R_LoadPic(name, pic, width, height, type, 32);
|
|
else
|
|
image = NULL;
|
|
}
|
|
/*else if (!strcmp(name+len-4, ".cin")) // Heffo
|
|
{ // WHY .cin files? because we can!
|
|
cinematics_t *newcin;
|
|
|
|
newcin = CIN_OpenCin(name);
|
|
if(!newcin)
|
|
return NULL;
|
|
|
|
pic = malloc(256*256*4);
|
|
memset(pic, 192, (256*256*4));
|
|
|
|
image = R_LoadPic (name, pic, 256, 256, type, 32);
|
|
|
|
newcin->texnum = image->texnum;
|
|
image->is_cin = true;
|
|
}*/
|
|
else
|
|
image = NULL;
|
|
|
|
if (!image)
|
|
R_AddToFailedImgList(name);
|
|
|
|
if (pic)
|
|
free(pic);
|
|
if (palette)
|
|
free(palette);
|
|
|
|
return image;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
===============
|
|
R_RegisterSkin
|
|
===============
|
|
*/
|
|
struct image_s *R_RegisterSkin (char *name)
|
|
{
|
|
return R_FindImage (name, it_skin);
|
|
}
|
|
|
|
|
|
/*
|
|
================
|
|
R_FreeUnusedImages
|
|
|
|
Any image that was not touched on this registration sequence
|
|
will be freed.
|
|
================
|
|
*/
|
|
void R_FreeUnusedImages (void)
|
|
{
|
|
int i;
|
|
image_t *image;
|
|
|
|
// never free notexture or particle textures
|
|
glMedia.notexture->registration_sequence = registration_sequence;
|
|
glMedia.whitetexture->registration_sequence = registration_sequence;
|
|
glMedia.distTextureARB->registration_sequence = registration_sequence;
|
|
#ifdef ROQ_SUPPORT
|
|
glMedia.rawtexture->registration_sequence = registration_sequence;
|
|
#endif // ROQ_SUPPORT
|
|
glMedia.envmappic->registration_sequence = registration_sequence;
|
|
glMedia.spheremappic->registration_sequence = registration_sequence;
|
|
glMedia.shelltexture->registration_sequence = registration_sequence;
|
|
glMedia.celshadetexture->registration_sequence = registration_sequence;
|
|
glMedia.causticwaterpic->registration_sequence = registration_sequence;
|
|
glMedia.causticslimepic->registration_sequence = registration_sequence;
|
|
glMedia.causticlavapic->registration_sequence = registration_sequence;
|
|
glMedia.particlebeam->registration_sequence = registration_sequence;
|
|
|
|
for (i=0; i<PARTICLE_TYPES; i++)
|
|
if (glMedia.particletextures[i]) // dont mess with null ones silly :p
|
|
glMedia.particletextures[i]->registration_sequence = registration_sequence;
|
|
|
|
for (i=0, image=gltextures; i<numgltextures; i++, image++)
|
|
{
|
|
if (image->registration_sequence == registration_sequence)
|
|
continue; // used this sequence
|
|
if (!image->registration_sequence)
|
|
continue; // free image_t slot
|
|
// if (image->type == it_pic)
|
|
if ( (image->type == it_pic) || (image->type == it_font) || (image->type == it_scrap) )
|
|
continue; // don't free pics or fonts
|
|
|
|
//Heffo - Free Cinematic
|
|
//if(image->is_cin)
|
|
// CIN_FreeCin(image->texnum);
|
|
|
|
// free it
|
|
qglDeleteTextures (1, &image->texnum);
|
|
memset (image, 0, sizeof(*image));
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
===============
|
|
Draw_GetPalette
|
|
===============
|
|
*/
|
|
int Draw_GetPalette (void)
|
|
{
|
|
int i;
|
|
int r, g, b;
|
|
unsigned v;
|
|
byte *pic, *pal;
|
|
int width, height;
|
|
//int avg, dr, dg, db, d1, d2, d3; //** DMP
|
|
//float sat; //** DMP
|
|
|
|
// get the palette
|
|
|
|
LoadPCX ("pics/colormap.pcx", &pic, &pal, &width, &height);
|
|
if (!pal)
|
|
VID_Error (ERR_FATAL, "Couldn't load pics/colormap.pcx");
|
|
|
|
for (i=0 ; i<256 ; i++)
|
|
{
|
|
r = pal[i*3+0];
|
|
g = pal[i*3+1];
|
|
b = pal[i*3+2];
|
|
|
|
//** DMP adjust saturation
|
|
/*avg = (r + g + b + 2) / 3; // first calc grey value we'll desaturate to
|
|
dr = avg - r; // calc distance from grey value to each gun
|
|
dg = avg - g;
|
|
db = avg - b;
|
|
d1 = abs(r - g); // find greatest distance between all the guns
|
|
d2 = abs(g - b);
|
|
d3 = abs(b - r);
|
|
if (d1 > d2) // we will use this for our existing saturation
|
|
if (d1 > d3)
|
|
sat = d1;
|
|
else
|
|
if (d2 > d3)
|
|
sat = d2;
|
|
else
|
|
sat = d3;
|
|
else
|
|
if (d2 > d3)
|
|
sat = d2;
|
|
else
|
|
sat = d3;
|
|
sat /= 255.0; // convert existing saturationn to ratio
|
|
sat = 1.0 - sat; // invert so the most saturation causes the desaturation to lessen
|
|
sat *= (1.0 - r_saturation->value); // scale the desaturation value so our desaturation is non-linear (keeps lava looking good)
|
|
dr *= sat; // scale the differences by the amount we want to desaturate
|
|
dg *= sat;
|
|
db *= sat;
|
|
r += dr; // now move the gun values towards total grey by the amount we desaturated
|
|
g += dg;
|
|
b += db;*/
|
|
//** DMP end saturation mod
|
|
|
|
v = (255<<24) + (r<<0) + (g<<8) + (b<<16);
|
|
d_8to24table[i] = LittleLong(v);
|
|
}
|
|
|
|
d_8to24table[255] &= LittleLong(0xffffff); // 255 is transparent
|
|
|
|
free (pic);
|
|
free (pal);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
===============
|
|
R_InitImages
|
|
===============
|
|
*/
|
|
void R_InitImages (void)
|
|
{
|
|
int i, j;
|
|
float g = vid_gamma->value;
|
|
|
|
// Knightmare- reinitialize these after a vid_restart
|
|
// this is needed because the renderer is no longer a DLL
|
|
/*
|
|
gl_solid_format = 3;
|
|
gl_alpha_format = 4;
|
|
gl_tex_solid_format = 3;
|
|
gl_tex_alpha_format = 4;
|
|
*/
|
|
gl_filter_min = GL_LINEAR_MIPMAP_NEAREST;
|
|
gl_filter_max = GL_LINEAR;
|
|
|
|
registration_sequence = 1;
|
|
|
|
// init intensity conversions
|
|
//r_intensity = Cvar_Get ("r_intensity", "2", CVAR_ARCHIVE);
|
|
|
|
// Knightmare- added Vic's RGB brightening
|
|
if (glConfig.mtexcombine)
|
|
r_intensity = Cvar_Get ("r_intensity", "1", 0);
|
|
else
|
|
r_intensity = Cvar_Get ("r_intensity", "2", 0);
|
|
Cvar_SetDescription ("r_intensity", "Sets intensity value for gamma table. Value range is >= 1.");
|
|
// end Knightmare
|
|
|
|
if ( r_intensity->value <= 1 )
|
|
Cvar_Set( "r_intensity", "1" );
|
|
|
|
glState.inverse_intensity = 1 / r_intensity->value;
|
|
|
|
R_InitFailedImgList (); // Knightmare added
|
|
|
|
Draw_GetPalette ();
|
|
|
|
if (qglColorTableEXT)
|
|
{
|
|
FS_LoadFile( "pics/16to8.dat", &glState.d_16to8table );
|
|
if ( !glState.d_16to8table )
|
|
VID_Error( ERR_FATAL, "Couldn't load pics/16to8.pcx");
|
|
}
|
|
|
|
if (glConfig.rendType == GLREND_VOODOO)
|
|
//if ( glConfig.renderer & ( GL_RENDERER_VOODOO | GL_RENDERER_VOODOO2 ) )
|
|
{
|
|
g = 1.0F;
|
|
}
|
|
|
|
for ( i = 0; i < 256; i++ )
|
|
{
|
|
if ( g == 1 )
|
|
{
|
|
gammatable[i] = i;
|
|
}
|
|
else
|
|
{
|
|
float inf;
|
|
|
|
inf = 255 * pow ( (i+0.5)/255.5 , g ) + 0.5;
|
|
if (inf < 0)
|
|
inf = 0;
|
|
if (inf > 255)
|
|
inf = 255;
|
|
gammatable[i] = inf;
|
|
}
|
|
}
|
|
|
|
for (i=0 ; i<256 ; i++)
|
|
{
|
|
j = i*r_intensity->value;
|
|
if (j > 255)
|
|
j = 255;
|
|
intensitytable[i] = j;
|
|
}
|
|
|
|
R_Upscale_Init ();
|
|
|
|
R_InitBloomTextures (); // BLOOMS
|
|
}
|
|
|
|
/*
|
|
===============
|
|
R_FreePic
|
|
by Knightmare
|
|
Frees a single pic
|
|
===============
|
|
*/
|
|
void R_FreePic (char *name)
|
|
{
|
|
int i;
|
|
image_t *image;
|
|
|
|
for (i=0, image=gltextures; i<numgltextures; i++, image++)
|
|
{
|
|
if (!image->registration_sequence)
|
|
continue; // free image_t slot
|
|
if (image->type != it_pic)
|
|
continue; // only free pics
|
|
if (!strcmp(name, image->name))
|
|
{
|
|
//Heffo - Free Cinematic
|
|
//if (image->is_cin)
|
|
// CIN_FreeCin(image->texnum);
|
|
|
|
// free it
|
|
qglDeleteTextures (1, &image->texnum);
|
|
memset (image, 0, sizeof(*image));
|
|
return; //we're done here
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
===============
|
|
R_ShutdownImages
|
|
===============
|
|
*/
|
|
void R_ShutdownImages (void)
|
|
{
|
|
int i;
|
|
image_t *image;
|
|
|
|
for (i=0, image=gltextures; i<numgltextures; i++, image++)
|
|
{
|
|
if (!image->registration_sequence)
|
|
continue; // free image_t slot
|
|
|
|
//Heffo - Free Cinematic
|
|
//if (image->is_cin)
|
|
// CIN_FreeCin(image->texnum);
|
|
|
|
// free it
|
|
qglDeleteTextures (1, &image->texnum);
|
|
memset (image, 0, sizeof(*image));
|
|
}
|
|
}
|
|
|