+#include "image.h"
+#include "jpeg.h"
+#include "image_png.h"
+
+cvar_t gl_max_size = {CVAR_SAVE, "gl_max_size", "2048", "maximum allowed texture size, can be used to reduce video memory usage, note: this is automatically reduced to match video card capabilities (such as 256 on 3Dfx cards before Voodoo4/5)"};
+cvar_t gl_picmip = {CVAR_SAVE, "gl_picmip", "0", "reduces resolution of textures by powers of 2, for example 1 will halve width/height, reducing texture memory usage by 75%"};
+cvar_t r_lerpimages = {CVAR_SAVE, "r_lerpimages", "1", "bilinear filters images when scaling them up to power of 2 size (mode 1), looks better than glquake (mode 0)"};
+cvar_t r_precachetextures = {CVAR_SAVE, "r_precachetextures", "1", "0 = never upload textures until used, 1 = upload most textures before use (exceptions: rarely used skin colormap layers), 2 = upload all textures before use (can increase texture memory usage significantly)"};
+cvar_t gl_texture_anisotropy = {CVAR_SAVE, "gl_texture_anisotropy", "1", "anisotropic filtering quality (if supported by hardware), 1 sample (no anisotropy) and 8 sample (8 tap anisotropy) are recommended values"};
+cvar_t gl_texturecompression = {CVAR_SAVE, "gl_texturecompression", "0", "whether to compress textures, a value of 0 disables compression (even if the individual cvars are 1), 1 enables fast (low quality) compression at startup, 2 enables slow (high quality) compression at startup"};
+cvar_t gl_texturecompression_color = {CVAR_SAVE, "gl_texturecompression_color", "1", "whether to compress colormap (diffuse) textures"};
+cvar_t gl_texturecompression_normal = {CVAR_SAVE, "gl_texturecompression_normal", "1", "whether to compress normalmap (normalmap) textures"};
+cvar_t gl_texturecompression_gloss = {CVAR_SAVE, "gl_texturecompression_gloss", "1", "whether to compress glossmap (specular) textures"};
+cvar_t gl_texturecompression_glow = {CVAR_SAVE, "gl_texturecompression_glow", "1", "whether to compress glowmap (luma) textures"};
+cvar_t gl_texturecompression_2d = {CVAR_SAVE, "gl_texturecompression_2d", "1", "whether to compress 2d (hud/menu) textures other than the font"};
+cvar_t gl_texturecompression_q3bsplightmaps = {CVAR_SAVE, "gl_texturecompression_q3bsplightmaps", "1", "whether to compress lightmaps in q3bsp format levels"};
+cvar_t gl_texturecompression_q3bspdeluxemaps = {CVAR_SAVE, "gl_texturecompression_q3bspdeluxemaps", "1", "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)"};
+
+int gl_filter_min = GL_LINEAR_MIPMAP_LINEAR;
+int gl_filter_mag = GL_LINEAR;
+
+
+static mempool_t *texturemempool;
+
+// note: this must not conflict with TEXF_ flags in r_textures.h
+// cleared when a texture is uploaded
+#define GLTEXF_UPLOAD 0x00010000
+// bitmask for mismatch checking
+#define GLTEXF_IMPORTANTBITS (0)
+// set when image is uploaded and freed
+#define GLTEXF_DESTROYED 0x00040000
+
+typedef struct textypeinfo_s
+{
+ int textype;
+ int inputbytesperpixel;
+ int internalbytesperpixel;
+ float glinternalbytesperpixel;
+ int glformat;
+ int glinternalformat;
+}
+textypeinfo_t;
+
+static textypeinfo_t textype_palette = {TEXTYPE_PALETTE, 1, 4, 4.0f, GL_RGBA , 3};
+static textypeinfo_t textype_rgb = {TEXTYPE_RGB , 3, 3, 4.0f, GL_RGB , 3};
+static textypeinfo_t textype_rgba = {TEXTYPE_RGBA , 4, 4, 4.0f, GL_RGBA , 3};
+static textypeinfo_t textype_palette_alpha = {TEXTYPE_PALETTE, 1, 4, 4.0f, GL_RGBA , 4};
+static textypeinfo_t textype_rgba_alpha = {TEXTYPE_RGBA , 4, 4, 4.0f, GL_RGBA , 4};
+static textypeinfo_t textype_palette_compress = {TEXTYPE_PALETTE, 1, 4, 0.5f, GL_RGBA , GL_COMPRESSED_RGB_ARB};
+static textypeinfo_t textype_rgb_compress = {TEXTYPE_RGB , 3, 3, 0.5f, GL_RGB , GL_COMPRESSED_RGB_ARB};
+static textypeinfo_t textype_rgba_compress = {TEXTYPE_RGBA , 4, 4, 0.5f, GL_RGBA , GL_COMPRESSED_RGB_ARB};
+static textypeinfo_t textype_palette_alpha_compress = {TEXTYPE_PALETTE, 1, 4, 1.0f, GL_RGBA , GL_COMPRESSED_RGBA_ARB};
+static textypeinfo_t textype_rgba_alpha_compress = {TEXTYPE_RGBA , 4, 4, 1.0f, GL_RGBA , GL_COMPRESSED_RGBA_ARB};
+
+#define GLTEXTURETYPE_1D 0
+#define GLTEXTURETYPE_2D 1
+#define GLTEXTURETYPE_3D 2
+#define GLTEXTURETYPE_CUBEMAP 3
+
+static int gltexturetypeenums[4] = {GL_TEXTURE_1D, GL_TEXTURE_2D, GL_TEXTURE_3D, GL_TEXTURE_CUBE_MAP_ARB};
+static int gltexturetypebindingenums[4] = {GL_TEXTURE_BINDING_1D, GL_TEXTURE_BINDING_2D, GL_TEXTURE_BINDING_3D, GL_TEXTURE_BINDING_CUBE_MAP_ARB};
+static int gltexturetypedimensions[4] = {1, 2, 3, 2};
+static int cubemapside[6] =
+{
+ GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB,
+ GL_TEXTURE_CUBE_MAP_NEGATIVE_X_ARB,
+ GL_TEXTURE_CUBE_MAP_POSITIVE_Y_ARB,
+ GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_ARB,
+ GL_TEXTURE_CUBE_MAP_POSITIVE_Z_ARB,
+ GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB
+};
+
+typedef struct gltexture_s
+{
+ // this field is exposed to the R_GetTexture macro, for speed reasons
+ // (must be identical in rtexture_t)
+ int texnum; // GL texture slot number
+
+ // pointer to texturepool (check this to see if the texture is allocated)
+ struct gltexturepool_s *pool;
+ // pointer to next texture in texturepool chain
+ struct gltexture_s *chain;
+ // name of the texture (this might be removed someday), no duplicates
+ char identifier[MAX_QPATH + 32];
+ // original data size in *inputtexels
+ int inputwidth, inputheight, inputdepth;
+ // copy of the original texture(s) supplied to the upload function, for
+ // delayed uploads (non-precached)
+ unsigned char *inputtexels;
+ // original data size in *inputtexels
+ int inputdatasize;
+ // flags supplied to the LoadTexture function
+ // (might be altered to remove TEXF_ALPHA), and GLTEXF_ private flags
+ int flags;
+ // pointer to one of the textype_ structs
+ textypeinfo_t *textype;
+ // one of the GLTEXTURETYPE_ values
+ int texturetype;
+ // palette if the texture is TEXTYPE_PALETTE
+ const unsigned int *palette;
+ // actual stored texture size after gl_picmip and gl_max_size are applied
+ // (power of 2 if gl_support_arb_texture_non_power_of_two is not supported)
+ int tilewidth, tileheight, tiledepth;
+ // 1 or 6 depending on texturetype
+ int sides;
+ // bytes per pixel
+ int bytesperpixel;
+ // GL_RGB or GL_RGBA
+ int glformat;
+ // 3 or 4
+ int glinternalformat;
+}
+gltexture_t;
+
+#define TEXTUREPOOL_SENTINEL 0xC0DEDBAD
+
+typedef struct gltexturepool_s
+{
+ unsigned int sentinel;
+ struct gltexture_s *gltchain;
+ struct gltexturepool_s *next;
+}
+gltexturepool_t;
+
+static gltexturepool_t *gltexturepoolchain = NULL;
+
+static unsigned char *resizebuffer = NULL, *colorconvertbuffer;
+static int resizebuffersize = 0;
+static unsigned char *texturebuffer;
+static int texturebuffersize = 0;
+
+static textypeinfo_t *R_GetTexTypeInfo(int textype, int flags)
+{
+ if ((flags & TEXF_COMPRESS) && gl_texturecompression.integer >= 1 && gl_support_texture_compression)
+ {
+ if (flags & TEXF_ALPHA)
+ {
+ switch(textype)
+ {
+ case TEXTYPE_PALETTE:
+ return &textype_palette_alpha_compress;
+ case TEXTYPE_RGB:
+ Host_Error("R_GetTexTypeInfo: RGB format has no alpha, TEXF_ALPHA not allowed");
+ return NULL;
+ case TEXTYPE_RGBA:
+ return &textype_rgba_alpha_compress;
+ default:
+ Host_Error("R_GetTexTypeInfo: unknown texture format");
+ return NULL;
+ }
+ }
+ else
+ {
+ switch(textype)
+ {
+ case TEXTYPE_PALETTE:
+ return &textype_palette_compress;
+ case TEXTYPE_RGB:
+ return &textype_rgb_compress;
+ case TEXTYPE_RGBA:
+ return &textype_rgba_compress;
+ default:
+ Host_Error("R_GetTexTypeInfo: unknown texture format");
+ return NULL;
+ }
+ }
+ }
+ else
+ {
+ if (flags & TEXF_ALPHA)
+ {
+ switch(textype)
+ {
+ case TEXTYPE_PALETTE:
+ return &textype_palette_alpha;
+ case TEXTYPE_RGB:
+ Host_Error("R_GetTexTypeInfo: RGB format has no alpha, TEXF_ALPHA not allowed");
+ return NULL;
+ case TEXTYPE_RGBA:
+ return &textype_rgba_alpha;
+ default:
+ Host_Error("R_GetTexTypeInfo: unknown texture format");
+ return NULL;
+ }
+ }
+ else
+ {
+ switch(textype)
+ {
+ case TEXTYPE_PALETTE:
+ return &textype_palette;
+ case TEXTYPE_RGB:
+ return &textype_rgb;
+ case TEXTYPE_RGBA:
+ return &textype_rgba;
+ default:
+ Host_Error("R_GetTexTypeInfo: unknown texture format");
+ return NULL;
+ }
+ }
+ }
+}