#include "image.h"
#include "jpeg.h"
#include "image_png.h"
+#include "intoverflow.h"
cvar_t gl_max_size = {CVAR_SAVE, "gl_max_size", "2048", "maximum allowed texture size, can be used to reduce video memory usage, limited by hardware capabilities (typically 2048, 4096, or 8192)"};
cvar_t gl_max_lightmapsize = {CVAR_SAVE, "gl_max_lightmapsize", "1024", "maximum allowed texture size for lightmap textures, use larger values to improve rendering speed, as long as there is enough video memory available (setting it too high for the hardware will cause very bad performance)"};
cvar_t gl_texturecompression_q3bspdeluxemaps = {CVAR_SAVE, "gl_texturecompression_q3bspdeluxemaps", "0", "whether to compress deluxemaps in q3bsp format levels (only levels compiled with q3map2 -deluxe have these)"};
cvar_t gl_texturecompression_sky = {CVAR_SAVE, "gl_texturecompression_sky", "0", "whether to compress sky textures"};
cvar_t gl_texturecompression_lightcubemaps = {CVAR_SAVE, "gl_texturecompression_lightcubemaps", "1", "whether to compress light cubemaps (spotlights and other light projection images)"};
+cvar_t gl_texturecompression_reflectmask = {CVAR_SAVE, "gl_texturecompression_reflectmask", "1", "whether to compress reflection cubemap masks (mask of which areas of the texture should reflect the generic shiny cubemap)"};
cvar_t gl_nopartialtextureupdates = {CVAR_SAVE, "gl_nopartialtextureupdates", "1", "use alternate path for dynamic lightmap updates that avoids a possibly slow code path in the driver"};
+qboolean gl_filter_force = false;
int gl_filter_min = GL_LINEAR_MIPMAP_LINEAR;
int gl_filter_mag = GL_LINEAR;
static mempool_t *texturemempool;
+static memexpandablearray_t texturearray;
// note: this must not conflict with TEXF_ flags in r_textures.h
// bitmask for mismatch checking
int inputbytesperpixel;
int internalbytesperpixel;
float glinternalbytesperpixel;
- int glformat;
int glinternalformat;
+ int glformat;
int gltype;
}
textypeinfo_t;
-static textypeinfo_t textype_palette = {TEXTYPE_PALETTE, 1, 4, 4.0f, GL_BGRA , 3, GL_UNSIGNED_BYTE};
-static textypeinfo_t textype_palette_alpha = {TEXTYPE_PALETTE, 1, 4, 4.0f, GL_BGRA , 4, GL_UNSIGNED_BYTE};
-static textypeinfo_t textype_rgba = {TEXTYPE_RGBA , 4, 4, 4.0f, GL_RGBA , 3, GL_UNSIGNED_BYTE};
-static textypeinfo_t textype_rgba_alpha = {TEXTYPE_RGBA , 4, 4, 4.0f, GL_RGBA , 4, GL_UNSIGNED_BYTE};
-static textypeinfo_t textype_rgba_compress = {TEXTYPE_RGBA , 4, 4, 0.5f, GL_RGBA , GL_COMPRESSED_RGB_ARB, GL_UNSIGNED_BYTE};
-static textypeinfo_t textype_rgba_alpha_compress = {TEXTYPE_RGBA , 4, 4, 1.0f, GL_RGBA , GL_COMPRESSED_RGBA_ARB, GL_UNSIGNED_BYTE};
-static textypeinfo_t textype_bgra = {TEXTYPE_BGRA , 4, 4, 4.0f, GL_BGRA , 3, GL_UNSIGNED_BYTE};
-static textypeinfo_t textype_bgra_alpha = {TEXTYPE_BGRA , 4, 4, 4.0f, GL_BGRA , 4, GL_UNSIGNED_BYTE};
-static textypeinfo_t textype_bgra_compress = {TEXTYPE_BGRA , 4, 4, 0.5f, GL_BGRA , GL_COMPRESSED_RGB_ARB, GL_UNSIGNED_BYTE};
-static textypeinfo_t textype_bgra_alpha_compress = {TEXTYPE_BGRA , 4, 4, 1.0f, GL_BGRA , GL_COMPRESSED_RGBA_ARB, GL_UNSIGNED_BYTE};
-static textypeinfo_t textype_shadowmap16 = {TEXTYPE_SHADOWMAP,2,2, 2.0f, GL_DEPTH_COMPONENT, GL_DEPTH_COMPONENT16_ARB, GL_UNSIGNED_SHORT};
-static textypeinfo_t textype_shadowmap24 = {TEXTYPE_SHADOWMAP,4,4, 4.0f, GL_DEPTH_COMPONENT, GL_DEPTH_COMPONENT24_ARB, GL_UNSIGNED_INT};
-static textypeinfo_t textype_alpha = {TEXTYPE_ALPHA , 1, 4, 4.0f, GL_ALPHA , 4, GL_UNSIGNED_BYTE};
-static textypeinfo_t textype_dxt1 = {TEXTYPE_DXT1 , 4, 0, 0.5f, 0 , GL_COMPRESSED_RGB_S3TC_DXT1_EXT, 0};
-static textypeinfo_t textype_dxt1a = {TEXTYPE_DXT1A , 4, 0, 0.5f, 0 , GL_COMPRESSED_RGBA_S3TC_DXT1_EXT, 0};
-static textypeinfo_t textype_dxt3 = {TEXTYPE_DXT3 , 4, 0, 1.0f, 0 , GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, 0};
-static textypeinfo_t textype_dxt5 = {TEXTYPE_DXT5 , 4, 0, 1.0f, 0 , GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, 0};
+
+static textypeinfo_t textype_palette = {TEXTYPE_PALETTE , 1, 4, 4.0f, 3 , GL_BGRA , GL_UNSIGNED_BYTE };
+static textypeinfo_t textype_palette_alpha = {TEXTYPE_PALETTE , 1, 4, 4.0f, 4 , GL_BGRA , GL_UNSIGNED_BYTE };
+static textypeinfo_t textype_rgba = {TEXTYPE_RGBA , 4, 4, 4.0f, 3 , GL_RGBA , GL_UNSIGNED_BYTE };
+static textypeinfo_t textype_rgba_alpha = {TEXTYPE_RGBA , 4, 4, 4.0f, 4 , GL_RGBA , GL_UNSIGNED_BYTE };
+static textypeinfo_t textype_rgba_compress = {TEXTYPE_RGBA , 4, 4, 0.5f, GL_COMPRESSED_RGB_S3TC_DXT1_EXT , GL_RGBA , GL_UNSIGNED_BYTE };
+static textypeinfo_t textype_rgba_alpha_compress = {TEXTYPE_RGBA , 4, 4, 1.0f, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, GL_RGBA , GL_UNSIGNED_BYTE };
+static textypeinfo_t textype_bgra = {TEXTYPE_BGRA , 4, 4, 4.0f, 3 , GL_BGRA , GL_UNSIGNED_BYTE };
+static textypeinfo_t textype_bgra_alpha = {TEXTYPE_BGRA , 4, 4, 4.0f, 4 , GL_BGRA , GL_UNSIGNED_BYTE };
+static textypeinfo_t textype_bgra_compress = {TEXTYPE_BGRA , 4, 4, 0.5f, GL_COMPRESSED_RGB_S3TC_DXT1_EXT , GL_BGRA , GL_UNSIGNED_BYTE };
+static textypeinfo_t textype_bgra_alpha_compress = {TEXTYPE_BGRA , 4, 4, 1.0f, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, GL_BGRA , GL_UNSIGNED_BYTE };
+static textypeinfo_t textype_shadowmap16 = {TEXTYPE_SHADOWMAP , 2, 2, 2.0f, GL_DEPTH_COMPONENT16_ARB , GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT};
+static textypeinfo_t textype_shadowmap24 = {TEXTYPE_SHADOWMAP , 4, 4, 4.0f, GL_DEPTH_COMPONENT24_ARB , GL_DEPTH_COMPONENT, GL_UNSIGNED_INT };
+static textypeinfo_t textype_alpha = {TEXTYPE_ALPHA , 1, 4, 4.0f, GL_ALPHA , GL_ALPHA , GL_UNSIGNED_BYTE };
+static textypeinfo_t textype_dxt1 = {TEXTYPE_DXT1 , 4, 0, 0.5f, GL_COMPRESSED_RGB_S3TC_DXT1_EXT , 0 , 0 };
+static textypeinfo_t textype_dxt1a = {TEXTYPE_DXT1A , 4, 0, 0.5f, GL_COMPRESSED_RGBA_S3TC_DXT1_EXT, 0 , 0 };
+static textypeinfo_t textype_dxt3 = {TEXTYPE_DXT3 , 4, 0, 1.0f, GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, 0 , 0 };
+static textypeinfo_t textype_dxt5 = {TEXTYPE_DXT5 , 4, 0, 1.0f, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, 0 , 0 };
+static textypeinfo_t textype_colorbuffer = {TEXTYPE_COLORBUFFER, 4, 4, 4.0f, 4 , GL_BGRA , GL_UNSIGNED_BYTE };
+
typedef enum gltexturetype_e
{
static unsigned char *resizebuffer = NULL, *colorconvertbuffer;
static int resizebuffersize = 0;
static const unsigned char *texturebuffer;
-static int texturebuffersize = 0;
static textypeinfo_t *R_GetTexTypeInfo(textype_t textype, int flags)
{
case TEXTYPE_RGBA:
if ((flags & TEXF_COMPRESS) && gl_texturecompression.integer >= 1 && vid.support.arb_texture_compression)
return (flags & TEXF_ALPHA) ? &textype_rgba_alpha_compress : &textype_rgba_compress;
- else
- return (flags & TEXF_ALPHA) ? &textype_rgba_alpha : &textype_rgba;
- break;
+ return (flags & TEXF_ALPHA) ? &textype_rgba_alpha : &textype_rgba;
case TEXTYPE_BGRA:
if ((flags & TEXF_COMPRESS) && gl_texturecompression.integer >= 1 && vid.support.arb_texture_compression)
return (flags & TEXF_ALPHA) ? &textype_bgra_alpha_compress : &textype_bgra_compress;
- else
- return (flags & TEXF_ALPHA) ? &textype_bgra_alpha : &textype_bgra;
- break;
+ return (flags & TEXF_ALPHA) ? &textype_bgra_alpha : &textype_bgra;
case TEXTYPE_ALPHA:
return &textype_alpha;
case TEXTYPE_SHADOWMAP:
return (flags & TEXF_LOWPRECISION) ? &textype_shadowmap16 : &textype_shadowmap24;
+ case TEXTYPE_COLORBUFFER:
+ return &textype_colorbuffer;
default:
Host_Error("R_GetTexTypeInfo: unknown texture format");
- return NULL;
+ break;
}
- return NULL; // this line only to hush compiler warnings
+ return NULL;
}
// dynamic texture code [11/22/2007 Black]
if (glt->inputtexels)
Mem_Free(glt->inputtexels);
- Mem_Free(glt);
+ Mem_ExpandableArray_FreeRecord(&texturearray, glt);
}
rtexturepool_t *R_AllocTexturePool(void)
if (Cmd_Argc() == 1)
{
+ Con_Printf("Texture mode is %sforced\n", gl_filter_force ? "" : "not ");
for (i = 0;i < 6;i++)
{
if (gl_filter_min == modes[i].minification)
return;
}
- for (i = 0;i < 6;i++)
+ for (i = 0;i < (int)(sizeof(modes)/sizeof(*modes));i++)
if (!strcasecmp (modes[i].name, Cmd_Argv(1) ) )
break;
if (i == 6)
gl_filter_min = modes[i].minification;
gl_filter_mag = modes[i].magnification;
+ gl_filter_force = ((Cmd_Argc() > 2) && !strcasecmp(Cmd_Argv(2), "force"));
// change all the existing mipmap texture objects
// FIXME: force renderer(/client/something?) restart instead?
for (glt = pool->gltchain;glt;glt = glt->chain)
{
// only update already uploaded images
- if (glt->texnum && !(glt->flags & (TEXF_FORCENEAREST | TEXF_FORCELINEAR)))
+ if (glt->texnum && (gl_filter_force || !(glt->flags & (TEXF_FORCENEAREST | TEXF_FORCELINEAR))))
{
oldbindtexnum = R_Mesh_TexBound(0, gltexturetypeenums[glt->texturetype]);
qglBindTexture(gltexturetypeenums[glt->texturetype], glt->texnum);CHECKGLERROR
qglPixelStorei(GL_PACK_ALIGNMENT, 1);CHECKGLERROR
texturemempool = Mem_AllocPool("texture management", 0, NULL);
+ Mem_ExpandableArray_NewArray(&texturearray, texturemempool, sizeof(gltexture_t), 512);
// Disable JPEG screenshots if the DLL isn't loaded
if (! JPEG_OpenLibrary ())
}
resizebuffersize = 0;
- texturebuffersize = 0;
resizebuffer = NULL;
colorconvertbuffer = NULL;
texturebuffer = NULL;
+ Mem_ExpandableArray_FreeArray(&texturearray);
Mem_FreePool(&texturemempool);
}
void R_Textures_Init (void)
{
- Cmd_AddCommand("gl_texturemode", &GL_TextureMode_f, "set texture filtering mode (GL_NEAREST, GL_LINEAR, GL_LINEAR_MIPMAP_LINEAR, etc)");
+ Cmd_AddCommand("gl_texturemode", &GL_TextureMode_f, "set texture filtering mode (GL_NEAREST, GL_LINEAR, GL_LINEAR_MIPMAP_LINEAR, etc); an additional argument 'force' forces the texture mode even in cases where it may not be appropriate");
Cmd_AddCommand("r_texturestats", R_TextureStats_f, "print information about all loaded textures and some statistics");
Cvar_RegisterVariable (&gl_max_size);
Cvar_RegisterVariable (&gl_picmip);
Cvar_RegisterVariable (&gl_texturecompression_q3bspdeluxemaps);
Cvar_RegisterVariable (&gl_texturecompression_sky);
Cvar_RegisterVariable (&gl_texturecompression_lightcubemaps);
+ Cvar_RegisterVariable (&gl_texturecompression_reflectmask);
Cvar_RegisterVariable (&gl_nopartialtextureupdates);
R_RegisterModule("R_Textures", r_textures_start, r_textures_shutdown, r_textures_newmap);
}
CHECKGLERROR
- if (flags & TEXF_FORCENEAREST)
+ if (!gl_filter_force && flags & TEXF_FORCENEAREST)
{
if (flags & TEXF_MIPMAP)
{
}
qglTexParameteri(textureenum, GL_TEXTURE_MAG_FILTER, GL_NEAREST);CHECKGLERROR
}
- else if (flags & TEXF_FORCELINEAR)
+ else if (!gl_filter_force && flags & TEXF_FORCELINEAR)
{
if (flags & TEXF_MIPMAP)
{
int i, size;
gltexture_t *glt;
gltexturepool_t *pool = (gltexturepool_t *)rtexturepool;
- textypeinfo_t *texinfo;
+ textypeinfo_t *texinfo, *texinfo2;
if (cls.state == ca_dedicated)
return NULL;
case TEXTYPE_ALPHA:
flags |= TEXF_ALPHA;
break;
+ case TEXTYPE_COLORBUFFER:
+ flags |= TEXF_ALPHA;
+ break;
default:
Host_Error("R_LoadTexture: unknown texture type");
}
- glt = (gltexture_t *)Mem_Alloc(texturemempool, sizeof(gltexture_t));
+ texinfo2 = R_GetTexTypeInfo(textype, flags);
+ if(size == width * height * depth * sides * texinfo->inputbytesperpixel)
+ texinfo = texinfo2;
+ else
+ Con_Printf ("R_LoadTexture: input size changed after alpha fallback\n");
+
+ glt = (gltexture_t *)Mem_ExpandableArray_AllocRecord(&texturearray);
if (identifier)
strlcpy (glt->identifier, identifier, sizeof(glt->identifier));
glt->pool = pool;
rtexture_t *R_LoadTextureDDSFile(rtexturepool_t *rtexturepool, const char *filename, int flags, qboolean *hasalphaflag, float *avgcolor)
{
- int i, size, dds_flags, dds_format_flags, dds_miplevels, dds_width, dds_height, textype;
+ int i, size, dds_format_flags, dds_miplevels, dds_width, dds_height;
+ //int dds_flags;
+ textype_t textype;
int bytesperblock, bytesperpixel;
int mipcomplete;
gltexture_t *glt;
ddssize = ddsfilesize;
if (!dds)
+ {
+ Log_Printf("ddstexturefailures.log", "%s\n", filename);
return NULL; // not found
+ }
- if (memcmp(dds, "DDS ", 4) || ddssize < (unsigned int)BuffLittleLong(dds+4) || BuffLittleLong(dds+76) != 32)
+ if (ddsfilesize <= 128 || memcmp(dds, "DDS ", 4) || ddssize < (unsigned int)BuffLittleLong(dds+4) || BuffLittleLong(dds+76) != 32)
{
Mem_Free(dds);
Con_Printf("^1%s: not a DDS image\n", filename);
return NULL;
}
- dds_flags = BuffLittleLong(dds+8);
+ //dds_flags = BuffLittleLong(dds+8);
dds_format_flags = BuffLittleLong(dds+80);
dds_miplevels = (BuffLittleLong(dds+108) & 0x400000) ? BuffLittleLong(dds+28) : 1;
dds_width = BuffLittleLong(dds+16);
textype = TEXTYPE_BGRA;
bytesperblock = 0;
bytesperpixel = 4;
- size = dds_width*dds_height*bytesperpixel;
+ size = INTOVERFLOW_MUL(INTOVERFLOW_MUL(dds_width, dds_height), bytesperpixel);
+ if(INTOVERFLOW_ADD(128, size) > INTOVERFLOW_NORMALIZE(ddsfilesize))
+ {
+ Mem_Free(dds);
+ Con_Printf("^1%s: invalid BGRA DDS image\n", filename);
+ return NULL;
+ }
// check alpha
for (i = 3;i < size;i += 4)
if (ddspixels[i] < 255)
break;
- if (i < size)
- flags |= TEXF_ALPHA;
+ if (i >= size)
+ flags &= ~TEXF_ALPHA;
}
else if (!memcmp(dds+84, "DXT1", 4))
{
textype = TEXTYPE_DXT1;
bytesperblock = 8;
bytesperpixel = 0;
- size = ((dds_width+3)/4)*((dds_height+3)/4)*bytesperblock;
+ //size = ((dds_width+3)/4)*((dds_height+3)/4)*bytesperblock;
+ size = INTOVERFLOW_MUL(INTOVERFLOW_MUL(INTOVERFLOW_DIV(INTOVERFLOW_ADD(dds_width, 3), 4), INTOVERFLOW_DIV(INTOVERFLOW_ADD(dds_height, 3), 4)), bytesperblock);
+ if(INTOVERFLOW_ADD(128, size) > INTOVERFLOW_NORMALIZE(ddsfilesize))
+ {
+ Mem_Free(dds);
+ Con_Printf("^1%s: invalid DXT1 DDS image\n", filename);
+ return NULL;
+ }
for (i = 0;i < size;i += bytesperblock)
if (ddspixels[i+0] + ddspixels[i+1] * 256 <= ddspixels[i+2] + ddspixels[i+3] * 256)
break;
if (i < size)
- {
textype = TEXTYPE_DXT1A;
- flags |= TEXF_ALPHA;
- }
+ else
+ flags &= ~TEXF_ALPHA;
}
else if (!memcmp(dds+84, "DXT3", 4))
{
textype = TEXTYPE_DXT3;
bytesperblock = 16;
bytesperpixel = 0;
- size = ((dds_width+3)/4)*((dds_height+3)/4)*bytesperblock;
- flags |= TEXF_ALPHA;
+ size = INTOVERFLOW_MUL(INTOVERFLOW_MUL(INTOVERFLOW_DIV(INTOVERFLOW_ADD(dds_width, 3), 4), INTOVERFLOW_DIV(INTOVERFLOW_ADD(dds_height, 3), 4)), bytesperblock);
+ if(INTOVERFLOW_ADD(128, size) > INTOVERFLOW_NORMALIZE(ddsfilesize))
+ {
+ Mem_Free(dds);
+ Con_Printf("^1%s: invalid DXT3 DDS image\n", filename);
+ return NULL;
+ }
}
else if (!memcmp(dds+84, "DXT5", 4))
{
textype = TEXTYPE_DXT5;
bytesperblock = 16;
bytesperpixel = 0;
- size = ((dds_width+3)/4)*((dds_height+3)/4)*bytesperblock;
- flags |= TEXF_ALPHA;
+ size = INTOVERFLOW_MUL(INTOVERFLOW_MUL(INTOVERFLOW_DIV(INTOVERFLOW_ADD(dds_width, 3), 4), INTOVERFLOW_DIV(INTOVERFLOW_ADD(dds_height, 3), 4)), bytesperblock);
+ if(INTOVERFLOW_ADD(128, size) > INTOVERFLOW_NORMALIZE(ddsfilesize))
+ {
+ Mem_Free(dds);
+ Con_Printf("^1%s: invalid DXT5 DDS image\n", filename);
+ return NULL;
+ }
}
else
{
texinfo = R_GetTexTypeInfo(textype, flags);
- glt = (gltexture_t *)Mem_Alloc(texturemempool, sizeof(gltexture_t));
+ glt = (gltexture_t *)Mem_ExpandableArray_AllocRecord(&texturearray);
strlcpy (glt->identifier, filename, sizeof(glt->identifier));
glt->pool = pool;
glt->chain = pool->gltchain;