/* =========================================================================== 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" #ifdef _WIN32 #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 #else // _WIN32 #include #ifdef PNG_SUPPORT #include #endif // PNG_SUPPORT #endif // _WIN32 #ifdef _WIN32 // Let's not use GLU on Linux/macOS -flibit #define USE_GLU_MIPMAP #endif // _WIN32 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_filter_min; int gl_filter_max; #if 0 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 ); } } #endif 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)) #if 0 // removed 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)) #endif /* =============== 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; itype != 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; itype != 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; itexnum <= 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= 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= 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=0; row--) { pixbuf = targa_rgba + row*columns*4; for(column=0; column0) row--; else goto breakOut; pixbuf = targa_rgba + row*columns*4; } } } else { // non run-length packet for(j=0;j0) 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; yheight; 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; itmpBuf[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 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>16); frac += fracstep; } frac = 3*(fracstep>>2); for (i=0 ; i>16); frac += fracstep; } for (i=0 ; i> 1; for (j=0 ; j>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>= 1; out = in; for (i=0 ; i>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 =============== */ 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_GLU_MIPMAP 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); #else // USE_GLU_MIPMAP 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); } } #endif // USE_GLU_MIPMAP // 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 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 ; itexnum) 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 ; iheight ; i++) for (j=0 ; jwidth ; 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) 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; ihash) { // 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; iregistration_sequence = registration_sequence; for (i=0, image=gltextures; iregistration_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_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; iregistration_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; iregistration_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)); } }