ref: c05151aa1b7ac81866cf937c973e7038035dfe53
dir: /ref_gl/gl_image.c/
/* Copyright (C) 1997-2001 Id Software, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "gl_local.h" image_t gltextures[MAX_GLTEXTURES]; int numgltextures; int base_textureid; // gltextures[i] = base_textureid+i static byte intensitytable[256]; static unsigned char gammatable[256]; cvar_t *intensity; unsigned d_8to24table[256]; qboolean GL_Upload8 (byte *data, int width, int height, qboolean mipmap, qboolean is_sky ); qboolean GL_Upload32 (unsigned *data, int width, int height, qboolean mipmap); int gl_solid_format = 3; int gl_alpha_format = 4; int gl_tex_solid_format = 3; int gl_tex_alpha_format = 4; int gl_filter_min = GL_LINEAR_MIPMAP_NEAREST; int gl_filter_max = GL_LINEAR; void GL_SetTexturePalette( unsigned palette[256] ) { int i; unsigned char temptable[768]; 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; } if ( qglColorTableEXT && gl_ext_palettedtexture->value ) { qglColorTableEXT( GL_SHARED_TEXTURE_PALETTE_EXT, GL_RGB, 256, GL_RGB, GL_UNSIGNED_BYTE, temptable ); } } void GL_EnableMultitexture( qboolean enable ) { if ( !qglSelectTextureSGIS ) return; if ( enable ) { GL_SelectTexture( GL_TEXTURE1_SGIS ); qglEnable( GL_TEXTURE_2D ); GL_TexEnv( GL_REPLACE ); } else { GL_SelectTexture( GL_TEXTURE1_SGIS ); qglDisable( GL_TEXTURE_2D ); GL_TexEnv( GL_REPLACE ); } GL_SelectTexture( GL_TEXTURE0_SGIS ); GL_TexEnv( GL_REPLACE ); } void GL_SelectTexture( GLenum texture ) { int tmu; if ( !qglSelectTextureSGIS ) return; if ( texture == GL_TEXTURE0_SGIS ) tmu = 0; else tmu = 1; if ( tmu == gl_state.currenttmu ) return; gl_state.currenttmu = tmu; if ( tmu == 0 ) qglSelectTextureSGIS( GL_TEXTURE0_SGIS ); else qglSelectTextureSGIS( GL_TEXTURE1_SGIS ); } void GL_TexEnv( GLenum mode ) { static int lastmodes[2] = { -1, -1 }; if ( mode != lastmodes[gl_state.currenttmu] ) { qglTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, mode ); lastmodes[gl_state.currenttmu] = mode; } } void GL_Bind (int texnum) { extern image_t *draw_chars; if (gl_nobind->value && draw_chars) // performance evaluation option texnum = draw_chars->texnum; if ( gl_state.currenttextures[gl_state.currenttmu] == texnum) return; gl_state.currenttextures[gl_state.currenttmu] = texnum; qglBindTexture (GL_TEXTURE_2D, texnum); } void GL_MBind( GLenum target, int texnum ) { GL_SelectTexture( target ); if ( target == GL_TEXTURE0_SGIS ) { if ( gl_state.currenttextures[0] == texnum ) return; } else { if ( gl_state.currenttextures[1] == texnum ) return; } GL_Bind( texnum ); } typedef struct { char *name; int minimize, maximize; } glmode_t; glmode_t modes[] = { {"GL_NEAREST", GL_NEAREST, GL_NEAREST}, {"GL_LINEAR", GL_LINEAR, GL_LINEAR}, {"GL_NEAREST_MIPMAP_NEAREST", GL_NEAREST_MIPMAP_NEAREST, GL_NEAREST}, {"GL_LINEAR_MIPMAP_NEAREST", GL_LINEAR_MIPMAP_NEAREST, GL_LINEAR}, {"GL_NEAREST_MIPMAP_LINEAR", GL_NEAREST_MIPMAP_LINEAR, GL_NEAREST}, {"GL_LINEAR_MIPMAP_LINEAR", GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR} }; #define NUM_GL_MODES (sizeof(modes) / sizeof (glmode_t)) typedef struct { char *name; int mode; } gltmode_t; gltmode_t gl_alpha_modes[] = { {"default", 4}, {"GL_RGBA", GL_RGBA}, {"GL_RGBA8", GL_RGBA8}, {"GL_RGB5_A1", GL_RGB5_A1}, {"GL_RGBA4", GL_RGBA4}, {"GL_RGBA2", GL_RGBA2}, }; #define NUM_GL_ALPHA_MODES (sizeof(gl_alpha_modes) / sizeof (gltmode_t)) gltmode_t gl_solid_modes[] = { {"default", 3}, {"GL_RGB", GL_RGB}, {"GL_RGB8", GL_RGB8}, {"GL_RGB5", GL_RGB5}, {"GL_RGB4", GL_RGB4}, {"GL_R3_G3_B2", GL_R3_G3_B2}, #ifdef GL_RGB2_EXT {"GL_RGB2", GL_RGB2_EXT}, #endif }; #define NUM_GL_SOLID_MODES (sizeof(gl_solid_modes) / sizeof (gltmode_t)) /* =============== GL_TextureMode =============== */ void GL_TextureMode( char *string ) { int i; image_t *glt; for (i=0 ; i< NUM_GL_MODES ; i++) { if ( !Q_stricmp( modes[i].name, string ) ) break; } if (i == NUM_GL_MODES) { ri.Con_Printf (PRINT_ALL, "bad filter name\n"); return; } gl_filter_min = modes[i].minimize; gl_filter_max = modes[i].maximize; // change all the existing mipmap texture objects for (i=0, glt=gltextures ; i<numgltextures ; i++, glt++) { if (glt->type != it_pic && glt->type != it_sky ) { GL_Bind (glt->texnum); qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_min); qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max); } } } /* =============== GL_TextureAlphaMode =============== */ void GL_TextureAlphaMode( char *string ) { int i; for (i=0 ; i< NUM_GL_ALPHA_MODES ; i++) { if ( !Q_stricmp( gl_alpha_modes[i].name, string ) ) break; } if (i == NUM_GL_ALPHA_MODES) { ri.Con_Printf (PRINT_ALL, "bad alpha texture mode name\n"); return; } gl_tex_alpha_format = gl_alpha_modes[i].mode; } /* =============== GL_TextureSolidMode =============== */ void GL_TextureSolidMode( char *string ) { int i; for (i=0 ; i< NUM_GL_SOLID_MODES ; i++) { if ( !Q_stricmp( gl_solid_modes[i].name, string ) ) break; } if (i == NUM_GL_SOLID_MODES) { ri.Con_Printf (PRINT_ALL, "bad solid texture mode name\n"); return; } gl_tex_solid_format = gl_solid_modes[i].mode; } /* =============== GL_ImageList_f =============== */ void GL_ImageList_f (void) { int i; image_t *image; int texels; const char *palstrings[2] = { "RGB", "PAL" }; ri.Con_Printf (PRINT_ALL, "------------------\n"); texels = 0; for (i=0, image=gltextures ; i<numgltextures ; i++, image++) { if (image->texnum <= 0) continue; texels += image->upload_width*image->upload_height; switch (image->type) { case it_skin: ri.Con_Printf (PRINT_ALL, "M"); break; case it_sprite: ri.Con_Printf (PRINT_ALL, "S"); break; case it_wall: ri.Con_Printf (PRINT_ALL, "W"); break; case it_pic: ri.Con_Printf (PRINT_ALL, "P"); break; default: ri.Con_Printf (PRINT_ALL, " "); break; } ri.Con_Printf (PRINT_ALL, " %3i %3i %s: %s\n", image->upload_width, image->upload_height, palstrings[image->paletted], image->name); } ri.Con_Printf (PRINT_ALL, "Total texel count (not counting mipmaps): %i\n", texels); } /* ============================================================================= scrap allocation Allocate all the little status bar obejcts into a single texture to crutch up inefficient hardware / drivers ============================================================================= */ #define MAX_SCRAPS 1 #define BLOCK_WIDTH 256 #define BLOCK_HEIGHT 256 int scrap_allocated[MAX_SCRAPS][BLOCK_WIDTH]; byte scrap_texels[MAX_SCRAPS][BLOCK_WIDTH*BLOCK_HEIGHT]; qboolean scrap_dirty; // returns a texture number and the position inside it int Scrap_AllocBlock (int w, int h, int *x, int *y) { int i, j; int best, best2; int texnum; for (texnum=0 ; texnum<MAX_SCRAPS ; texnum++) { best = BLOCK_HEIGHT; for (i=0 ; i<BLOCK_WIDTH-w ; i++) { best2 = 0; for (j=0 ; j<w ; j++) { if (scrap_allocated[texnum][i+j] >= best) break; if (scrap_allocated[texnum][i+j] > best2) best2 = scrap_allocated[texnum][i+j]; } if (j == w) { // this is a valid spot *x = i; *y = best = best2; } } if (best + h > BLOCK_HEIGHT) continue; for (i=0 ; i<w ; i++) scrap_allocated[texnum][*x + i] = best + h; return texnum; } return -1; // Sys_Error ("Scrap_AllocBlock: full"); } int scrap_uploads; void Scrap_Upload (void) { scrap_uploads++; GL_Bind(TEXNUM_SCRAPS); GL_Upload8 (scrap_texels[0], BLOCK_WIDTH, BLOCK_HEIGHT, false, false ); scrap_dirty = false; } /* ================================================================= PCX LOADING ================================================================= */ /* ============== LoadPCX ============== */ void LoadPCX (char *filename, byte **pic, byte **palette, int *width, int *height) { byte *raw; pcx_t *pcx; int x, y; int len; int dataByte, runLength; byte *out, *pix; *pic = NULL; *palette = NULL; // // load the file // len = ri.FS_LoadFile (filename, (void **)&raw); if (!raw) { ri.Con_Printf (PRINT_DEVELOPER, "Bad pcx file %s\n", filename); return; } // // parse the PCX file // pcx = (pcx_t *)raw; pcx->xmin = LittleShort(pcx->xmin); pcx->ymin = LittleShort(pcx->ymin); pcx->xmax = LittleShort(pcx->xmax); pcx->ymax = LittleShort(pcx->ymax); pcx->hres = LittleShort(pcx->hres); pcx->vres = LittleShort(pcx->vres); pcx->bytes_per_line = LittleShort(pcx->bytes_per_line); pcx->palette_type = LittleShort(pcx->palette_type); raw = &pcx->data; if (pcx->manufacturer != 0x0a || pcx->version != 5 || pcx->encoding != 1 || pcx->bits_per_pixel != 8 || pcx->xmax >= 640 || pcx->ymax >= 480) { ri.Con_Printf (PRINT_ALL, "Bad pcx file %s\n", filename); return; } out = malloc ( (pcx->ymax+1) * (pcx->xmax+1) ); *pic = out; pix = out; if (palette) { *palette = malloc(768); memcpy (*palette, (byte *)pcx + len - 768, 768); } if (width) *width = pcx->xmax+1; if (height) *height = pcx->ymax+1; for (y=0 ; y<=pcx->ymax ; y++, pix += pcx->xmax+1) { for (x=0 ; x<=pcx->xmax ; ) { dataByte = *raw++; if((dataByte & 0xC0) == 0xC0) { runLength = dataByte & 0x3F; dataByte = *raw++; } else runLength = 1; while(runLength-- > 0) pix[x++] = dataByte; } } if ( raw - (byte *)pcx > len) { ri.Con_Printf (PRINT_DEVELOPER, "PCX file %s was malformed", filename); free (*pic); *pic = NULL; } ri.FS_FreeFile (pcx); } /* ========================================================= TARGA LOADING ========================================================= */ 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; /* ============= LoadTGA ============= */ void 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 = ri.FS_LoadFile (name, (void **)&buffer); if (!buffer) { ri.Con_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++; if (targa_header.image_type!=2 && targa_header.image_type!=10) ri.Sys_Error (ERR_DROP, "LoadTGA: Only type 2 and 10 targa RGB images supported\n"); if (targa_header.colormap_type !=0 || (targa_header.pixel_size!=32 && targa_header.pixel_size!=24)) ri.Sys_Error (ERR_DROP, "LoadTGA: Only 32 or 24 bit images supported (no colormaps)\n"); 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:; } } ri.FS_FreeFile (buffer); } /* ==================================================================== 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_ResampleTexture ================ */ 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; } } } /* ================ 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_Upload32 Returns has_alpha =============== */ 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] = gl_state.d_16to8table[c]; scaled += 4; } } int upload_width, upload_height; qboolean uploaded_paletted; qboolean GL_Upload32 (unsigned *data, int width, int height, qboolean mipmap) { int samples; unsigned scaled[256*256]; unsigned char paletted_texture[256*256]; int scaled_width, scaled_height; int i, c; byte *scan; int comp; uploaded_paletted = false; for (scaled_width = 1 ; scaled_width < width ; scaled_width<<=1) ; if (gl_round_down->value && scaled_width > width && mipmap) scaled_width >>= 1; for (scaled_height = 1 ; scaled_height < height ; scaled_height<<=1) ; if (gl_round_down->value && scaled_height > height && mipmap) scaled_height >>= 1; // let people sample down the world textures for speed if (mipmap) { scaled_width >>= (int)gl_picmip->value; scaled_height >>= (int)gl_picmip->value; } // don't ever bother with >256 textures if (scaled_width > 256) scaled_width = 256; if (scaled_height > 256) scaled_height = 256; if (scaled_width < 1) scaled_width = 1; if (scaled_height < 1) scaled_height = 1; upload_width = scaled_width; upload_height = scaled_height; if (scaled_width * scaled_height > sizeof(scaled)/4) ri.Sys_Error (ERR_DROP, "GL_Upload32: too big"); // scan the texture for any non-255 alpha c = width*height; scan = ((byte *)data) + 3; samples = gl_solid_format; for (i=0 ; i<c ; i++, scan += 4) { if ( *scan != 255 ) { samples = gl_alpha_format; break; } } if (samples == gl_solid_format) comp = gl_tex_solid_format; else if (samples == gl_alpha_format) comp = gl_tex_alpha_format; else { ri.Con_Printf (PRINT_ALL, "Unknown number of texture components %i\n", samples); comp = samples; } #if 0 if (mipmap) gluBuild2DMipmaps (GL_TEXTURE_2D, samples, width, height, GL_RGBA, GL_UNSIGNED_BYTE, trans); else if (scaled_width == width && scaled_height == height) qglTexImage2D (GL_TEXTURE_2D, 0, comp, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, trans); else { gluScaleImage (GL_RGBA, width, height, GL_UNSIGNED_BYTE, trans, scaled_width, scaled_height, GL_UNSIGNED_BYTE, scaled); qglTexImage2D (GL_TEXTURE_2D, 0, comp, scaled_width, scaled_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, scaled); } #else if (scaled_width == width && scaled_height == height) { if (!mipmap) { if ( qglColorTableEXT && gl_ext_palettedtexture->value && samples == gl_solid_format ) { uploaded_paletted = true; GL_BuildPalettedTexture( paletted_texture, ( unsigned char * ) data, scaled_width, scaled_height ); qglTexImage2D( GL_TEXTURE_2D, 0, GL_COLOR_INDEX8_EXT, scaled_width, scaled_height, 0, GL_COLOR_INDEX, GL_UNSIGNED_BYTE, paletted_texture ); } else { qglTexImage2D (GL_TEXTURE_2D, 0, comp, scaled_width, scaled_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); } goto done; } memcpy (scaled, data, width*height*4); } else GL_ResampleTexture (data, width, height, scaled, scaled_width, scaled_height); GL_LightScaleTexture (scaled, scaled_width, scaled_height, !mipmap ); if ( qglColorTableEXT && gl_ext_palettedtexture->value && ( samples == gl_solid_format ) ) { uploaded_paletted = true; GL_BuildPalettedTexture( paletted_texture, ( unsigned char * ) scaled, scaled_width, scaled_height ); qglTexImage2D( GL_TEXTURE_2D, 0, GL_COLOR_INDEX8_EXT, scaled_width, scaled_height, 0, GL_COLOR_INDEX, GL_UNSIGNED_BYTE, paletted_texture ); } else { qglTexImage2D( GL_TEXTURE_2D, 0, comp, scaled_width, scaled_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, scaled ); } if (mipmap) { int miplevel; miplevel = 0; while (scaled_width > 1 || scaled_height > 1) { GL_MipMap ((byte *)scaled, scaled_width, scaled_height); scaled_width >>= 1; scaled_height >>= 1; if (scaled_width < 1) scaled_width = 1; if (scaled_height < 1) scaled_height = 1; miplevel++; if ( qglColorTableEXT && gl_ext_palettedtexture->value && samples == gl_solid_format ) { uploaded_paletted = true; GL_BuildPalettedTexture( paletted_texture, ( unsigned char * ) scaled, scaled_width, scaled_height ); qglTexImage2D( GL_TEXTURE_2D, miplevel, GL_COLOR_INDEX8_EXT, scaled_width, scaled_height, 0, GL_COLOR_INDEX, GL_UNSIGNED_BYTE, paletted_texture ); } else { qglTexImage2D (GL_TEXTURE_2D, miplevel, comp, scaled_width, scaled_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, scaled); } } } done: ; #endif if (mipmap) { qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_min); qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max); } else { qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_max); qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max); } return (samples == gl_alpha_format); } /* =============== GL_Upload8 Returns has_alpha =============== */ /* static qboolean IsPowerOf2( int value ) { int i = 1; while ( 1 ) { if ( value == i ) return true; if ( i > value ) return false; i <<= 1; } } */ qboolean GL_Upload8 (byte *data, int width, int height, qboolean mipmap, qboolean is_sky ) { unsigned trans[512*256]; int i, s; int p; s = width*height; if (s > sizeof(trans)/4) ri.Sys_Error (ERR_DROP, "GL_Upload8: too large"); if ( qglColorTableEXT && gl_ext_palettedtexture->value && is_sky ) { qglTexImage2D( GL_TEXTURE_2D, 0, GL_COLOR_INDEX8_EXT, width, height, 0, GL_COLOR_INDEX, GL_UNSIGNED_BYTE, data ); qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_max); qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max); } else { 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, mipmap); } } /* ================ GL_LoadPic This is also used as an entry point for the generated r_notexture ================ */ image_t *GL_LoadPic (char *name, byte *pic, int width, int height, imagetype_t type, int bits) { image_t *image; int i; // 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) ri.Sys_Error (ERR_DROP, "MAX_GLTEXTURES"); numgltextures++; } image = &gltextures[i]; if (strlen(name) >= sizeof(image->name)) ri.Sys_Error (ERR_DROP, "Draw_LoadPic: \"%s\" is too long", name); strcpy (image->name, name); image->registration_sequence = registration_sequence; image->width = width; image->height = height; image->type = type; if (type == it_skin && bits == 8) R_FloodFillSkin(pic, width, height); // 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)*BLOCK_WIDTH + x + j] = pic[k]; image->texnum = TEXNUM_SCRAPS + texnum; image->scrap = true; image->has_alpha = true; image->sl = (x+0.01)/(float)BLOCK_WIDTH; image->sh = (x+image->width-0.01)/(float)BLOCK_WIDTH; image->tl = (y+0.01)/(float)BLOCK_WIDTH; image->th = (y+image->height-0.01)/(float)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 != it_pic && image->type != it_sky), image->type == it_sky ); else image->has_alpha = GL_Upload32 ((unsigned *)pic, width, height, (image->type != it_pic && image->type != it_sky) ); 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; } /* ================ GL_LoadWal ================ */ image_t *GL_LoadWal (char *name) { miptex_t *mt; int width, height, ofs; image_t *image; ri.FS_LoadFile (name, (void **)&mt); if (!mt) { ri.Con_Printf (PRINT_ALL, "GL_FindImage: can't load %s\n", name); return r_notexture; } width = LittleLong (mt->width); height = LittleLong (mt->height); ofs = LittleLong (mt->offsets[0]); image = GL_LoadPic (name, (byte *)mt + ofs, width, height, it_wall, 8); ri.FS_FreeFile ((void *)mt); return image; } /* =============== GL_FindImage Finds or loads the given image =============== */ image_t *GL_FindImage (char *name, imagetype_t type) { image_t *image; int i, len; byte *pic, *palette; int width, height; if (!name) return NULL; // ri.Sys_Error (ERR_DROP, "GL_FindImage: NULL name"); len = strlen(name); if (len<5) return NULL; // ri.Sys_Error (ERR_DROP, "GL_FindImage: bad name: %s", name); // look for it for (i=0, image=gltextures ; i<numgltextures ; i++,image++) { if (!strcmp(name, image->name)) { image->registration_sequence = registration_sequence; 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) return NULL; // ri.Sys_Error (ERR_DROP, "GL_FindImage: can't load %s", name); image = GL_LoadPic (name, pic, width, height, type, 8); } else if (!strcmp(name+len-4, ".wal")) { image = GL_LoadWal (name); } else if (!strcmp(name+len-4, ".tga")) { LoadTGA (name, &pic, &width, &height); if (!pic) return NULL; // ri.Sys_Error (ERR_DROP, "GL_FindImage: can't load %s", name); image = GL_LoadPic (name, pic, width, height, type, 32); } else return NULL; // ri.Sys_Error (ERR_DROP, "GL_FindImage: bad extension on: %s", name); if (pic) free(pic); if (palette) free(palette); return image; } /* =============== R_RegisterSkin =============== */ struct image_s *R_RegisterSkin (char *name) { return GL_FindImage (name, it_skin); } /* ================ GL_FreeUnusedImages Any image that was not touched on this registration sequence will be freed. ================ */ void GL_FreeUnusedImages (void) { int i; image_t *image; // never free r_notexture or particle texture r_notexture->registration_sequence = registration_sequence; r_particletexture->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) continue; // don't free pics // 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; // get the palette LoadPCX ("pics/colormap.pcx", &pic, &pal, &width, &height); if (!pal) ri.Sys_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]; 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; } /* =============== GL_InitImages =============== */ void GL_InitImages (void) { int i, j; float g = vid_gamma->value; registration_sequence = 1; // init intensity conversions intensity = ri.Cvar_Get ("intensity", "2", 0); if ( intensity->value <= 1 ) ri.Cvar_Set( "intensity", "1" ); gl_state.inverse_intensity = 1 / intensity->value; Draw_GetPalette (); if ( qglColorTableEXT ) { ri.FS_LoadFile( "pics/16to8.dat", &gl_state.d_16to8table ); if ( !gl_state.d_16to8table ) ri.Sys_Error( ERR_FATAL, "Couldn't load pics/16to8.pcx"); } if ( gl_config.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*intensity->value; if (j > 255) j = 255; intensitytable[i] = j; } } /* =============== GL_ShutdownImages =============== */ void GL_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 // free it qglDeleteTextures (1, &image->texnum); memset (image, 0, sizeof(*image)); } }