summaryrefslogtreecommitdiff
path: root/source/r_images.c
diff options
context:
space:
mode:
Diffstat (limited to 'source/r_images.c')
-rw-r--r--source/r_images.c1770
1 files changed, 1770 insertions, 0 deletions
diff --git a/source/r_images.c b/source/r_images.c
new file mode 100644
index 0000000..0218d25
--- /dev/null
+++ b/source/r_images.c
@@ -0,0 +1,1770 @@
+/*
+Copyright (C) 2003-2006 Andrey Nazarov
+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.
+
+*/
+
+//
+// images.c -- image reading and writing functions
+//
+
+#include "config.h"
+
+#ifdef USE_PNG
+#include <png.h>
+#endif
+
+#ifdef USE_JPEG
+#ifndef USE_PNG
+#include <setjmp.h>
+#endif
+#include <stdio.h>
+#include <jpeglib.h>
+#endif
+
+#include "q_shared.h"
+#include "com_public.h"
+#include "q_files.h"
+#include "q_list.h"
+#include "r_shared.h"
+
+/*
+=================================================================
+
+PCX LOADING
+
+=================================================================
+*/
+
+
+/*
+==============
+Image_LoadPCX
+==============
+*/
+void Image_LoadPCX( const char *filename, byte **pic, byte *palette, int *width, int *height ) {
+ byte *raw, *end;
+ pcx_t *pcx;
+ uint32 x, y, w, h;
+ int len;
+ int dataByte, runLength;
+ byte *out, *pix;
+
+ if( !filename || !pic ) {
+ Com_Error( ERR_FATAL, "LoadPCX: NULL" );
+ }
+
+ *pic = NULL;
+
+ //
+ // load the file
+ //
+ len = fs.LoadFile( filename, (void **)&raw );
+ if( !raw ) {
+ return;
+ }
+
+ //
+ // parse the PCX file
+ //
+ pcx = (pcx_t *)raw;
+
+ w = LittleShort( pcx->xmax ) + 1;
+ h = LittleShort( pcx->ymax ) + 1;
+
+ if( pcx->manufacturer != 0x0a
+ || pcx->version != 5
+ || pcx->encoding != 1
+ || pcx->bits_per_pixel != 8
+ || w > 640
+ || h > 480 )
+ {
+ Com_WPrintf( "LoadPCX: %s: unsupported format\n", filename );
+ return;
+ }
+
+#ifdef SOFTWARE_RENDERER
+ pix = out = R_Malloc( w * h );
+#else
+ pix = out = fs.AllocTempMem( w * h );
+#endif
+
+ if( palette ) {
+ if( len < 768 ) {
+ goto malformed;
+ }
+ memcpy( palette, ( byte * )pcx + len - 768, 768 );
+ }
+
+ raw = &pcx->data;
+ end = ( byte * )pcx + len;
+
+ for( y = 0; y < h; y++, pix += w ) {
+ for( x = 0; x < w; ) {
+ if( raw == end ) {
+ goto malformed;
+ }
+ dataByte = *raw++;
+
+ if( ( dataByte & 0xC0 ) == 0xC0 ) {
+ runLength = dataByte & 0x3F;
+ if( x + runLength > w ) {
+ goto malformed;
+ }
+ if( raw == end ) {
+ goto malformed;
+ }
+ dataByte = *raw++;
+ while( runLength-- ) {
+ pix[x++] = dataByte;
+ }
+ } else {
+ pix[x++] = dataByte;
+ }
+
+ }
+
+ }
+
+ if( width )
+ *width = w;
+ if( height )
+ *height = h;
+
+ *pic = out;
+
+ fs.FreeFile( pcx );
+ return;
+
+malformed:
+ Com_WPrintf( "LoadPCX: %s: file was malformed\n", filename );
+#ifdef SOFTWARE_RENDERER
+ com.Free( out );
+#else
+ fs.FreeFile( out );
+#endif
+ fs.FreeFile( pcx );
+}
+
+/*
+==============
+Image_WritePCX
+==============
+*/
+qboolean Image_WritePCX( const char *filename, const byte *data, int width,
+ int height, int rowbytes, byte *palette )
+{
+ int i, j, length;
+ pcx_t *pcx;
+ byte *pack;
+ qboolean ret = qfalse;
+ fileHandle_t f;
+
+ pcx = fs.AllocTempMem( width * height * 2 + 1000 );
+ pcx->manufacturer = 0x0a; // PCX id
+ pcx->version = 5; // 256 color
+ pcx->encoding = 1; // uncompressed
+ pcx->bits_per_pixel = 8; // 256 color
+ pcx->xmin = 0;
+ pcx->ymin = 0;
+ pcx->xmax = LittleShort( width - 1 );
+ pcx->ymax = LittleShort( height - 1 );
+ pcx->hres = LittleShort( width );
+ pcx->vres = LittleShort( height );
+ memset( pcx->palette, 0, sizeof( pcx->palette ) );
+ pcx->color_planes = 1; // chunky image
+ pcx->bytes_per_line = LittleShort( width );
+ pcx->palette_type = LittleShort( 2 ); // not a grey scale
+ memset( pcx->filler, 0, sizeof( pcx->filler ) );
+
+// pack the image
+ pack = &pcx->data;
+ for( i = 0; i < height; i++) {
+ for( j = 0; j < width; j++) {
+ if( ( *data & 0xc0 ) != 0xc0 ) {
+ *pack++ = *data++;
+ } else {
+ *pack++ = 0xc1;
+ *pack++ = *data++;
+ }
+ }
+ data += rowbytes - width;
+ }
+
+// write the palette
+ *pack++ = 0x0c; // palette ID byte
+ for( i = 0; i < 768; i++ )
+ *pack++ = *palette++;
+
+// write output file
+ fs.FOpenFile( filename, &f, FS_MODE_WRITE );
+ if( !f ) {
+ goto fail;
+ }
+
+ length = pack - ( byte * )pcx;
+ if( fs.Write( pcx, length, f ) == length ) {
+ ret = qtrue;
+ }
+
+ fs.FCloseFile( f );
+
+fail:
+ fs.FreeFile( pcx );
+ return ret;
+}
+
+#ifdef TRUECOLOR_RENDERER
+
+/*
+=========================================================
+
+TARGA LOADING
+
+=========================================================
+*/
+
+static qboolean tga_decode_bgr( byte *data, byte *pixels,
+ int columns, int rows, byte *maxp )
+{
+ int col, row;
+ uint32 *pixbuf;
+
+ for( row = rows - 1; row >= 0; row-- ) {
+ pixbuf = ( uint32 * )pixels + row * columns;
+
+ for( col = 0; col < columns; col++ ) {
+ *pixbuf++ = MakeColor( data[2], data[1], data[0], 255 );
+ data += 3;
+ }
+ }
+
+ return qtrue;
+}
+
+static qboolean tga_decode_bgra( byte *data, byte *pixels,
+ int columns, int rows, byte *maxp )
+{
+ int col, row;
+ uint32 *pixbuf;
+
+ for( row = rows - 1; row >= 0; row-- ) {
+ pixbuf = ( uint32 * )pixels + row * columns;
+
+ for( col = 0; col < columns; col++ ) {
+ *pixbuf++ = MakeColor( data[2], data[1], data[0], data[3] );
+ data += 4;
+ }
+ }
+
+ return qtrue;
+}
+
+static qboolean tga_decode_bgr_flip( byte *data, byte *pixels,
+ int columns, int rows, byte *maxp )
+{
+ int count;
+ uint32 *pixbuf;
+
+ pixbuf = ( uint32 * )pixels;
+ count = rows * columns;
+ do {
+ *pixbuf++ = MakeColor( data[2], data[1], data[0], 255 );
+ data += 3;
+ } while( --count );
+
+ return qtrue;
+}
+
+static qboolean tga_decode_bgra_flip( byte *data, byte *pixels,
+ int columns, int rows, byte *maxp )
+{
+ int count;
+ uint32 *pixbuf;
+
+ pixbuf = ( uint32 * )pixels;
+ count = rows * columns;
+ do {
+ *pixbuf++ = MakeColor( data[2], data[1], data[0], data[3] );
+ data += 4;
+ } while( --count );
+
+ return qtrue;
+}
+
+static qboolean tga_decode_bgr_rle( byte *data, byte *pixels,
+ int columns, int rows, byte *maxp )
+{
+ int col, row;
+ uint32 *pixbuf, color;
+ byte packetHeader, packetSize;
+ int j;
+
+ for( row = rows - 1; row >= 0; row-- ) {
+ pixbuf = ( uint32 * )pixels + row * columns;
+
+ for( col = 0; col < columns; ) {
+ packetHeader = *data++;
+ packetSize = 1 + ( packetHeader & 0x7f );
+
+ if( packetHeader & 0x80 ) {
+ /* run-length packet */
+ if( data + 3 > maxp ) {
+ return qfalse;
+ }
+ color = MakeColor( data[2], data[1], data[0], 255 );
+ data += 3;
+ for( j = 0; j < packetSize; j++ ) {
+ *pixbuf++ = color;
+
+ col++;
+ if( col == columns ) {
+ /* run spans across rows */
+ col = 0;
+
+ if( row > 0 )
+ row--;
+ else
+ goto breakOut;
+
+ pixbuf = ( uint32 * )pixels + row * columns;
+ }
+ }
+ } else {
+ /* non run-length packet */
+ if( data + 3 * packetSize > maxp ) {
+ return qfalse;
+ }
+ for( j = 0; j < packetSize; j++ ) {
+ *pixbuf++ = MakeColor( data[2], data[1], data[0], 255 );
+ data += 3;
+
+ col++;
+ if( col == columns ) {
+ /* run spans across rows */
+ col = 0;
+ if( row > 0 )
+ row--;
+ else
+ goto breakOut;
+ pixbuf = ( uint32 * )pixels + row * columns;
+ }
+ }
+ }
+ }
+breakOut: ;
+
+ }
+
+ return qtrue;
+
+}
+
+static qboolean tga_decode_bgra_rle( byte *data, byte *pixels,
+ int columns, int rows, byte *maxp )
+{
+ int col, row;
+ uint32 *pixbuf, color;
+ byte packetHeader, packetSize;
+ int j;
+
+ for( row = rows - 1; row >= 0; row-- ) {
+ pixbuf = ( uint32 * )pixels + row * columns;
+
+ for( col = 0; col < columns; ) {
+ packetHeader = *data++;
+ packetSize = 1 + ( packetHeader & 0x7f );
+
+ if( packetHeader & 0x80 ) {
+ /* run-length packet */
+ if( data + 4 > maxp ) {
+ return qfalse;
+ }
+ color = MakeColor( data[2], data[1], data[0], data[3] );
+ data += 4;
+ for( j = 0; j < packetSize; j++ ) {
+ *pixbuf++ = color;
+
+ col++;
+ if( col == columns ) {
+ /* run spans across rows */
+ col = 0;
+
+ if( row > 0 )
+ row--;
+ else
+ goto breakOut;
+
+ pixbuf = ( uint32 * )pixels + row * columns;
+ }
+ }
+ } else {
+ /* non run-length packet */
+ if( data + 4 * packetSize > maxp ) {
+ return qfalse;
+ }
+ for( j = 0; j < packetSize; j++ ) {
+ *pixbuf++ = MakeColor( data[2], data[1], data[0], data[3] );
+ data += 4;
+
+ col++;
+ if( col == columns ) {
+ /* run spans across rows */
+ col = 0;
+ if( row > 0 )
+ row--;
+ else
+ goto breakOut;
+ pixbuf = ( uint32 * )pixels + row * columns;
+ }
+ }
+ }
+ }
+breakOut: ;
+
+ }
+
+ return qtrue;
+
+}
+
+#define TARGA_HEADER_SIZE 18
+
+/*
+=============
+LoadTGA
+=============
+*/
+void Image_LoadTGA( const char *filename, byte **pic,
+ int *width, int *height )
+{
+ byte *buffer;
+ int length;
+ byte *pixels;
+ int offset, w, h;
+ qboolean (*decode)( byte *, byte *, int, int, byte * );
+ int id_length, image_type, pixel_size, attributes, bpp;
+
+ if( !filename || !pic ) {
+ Com_Error( ERR_FATAL, "LoadTGA: NULL" );
+ }
+
+ *pic = NULL;
+
+ //
+ // load the file
+ //
+ length = fs.LoadFile( filename, ( void ** )&buffer );
+ if( !buffer ) {
+ return;
+ }
+
+ if( length < TARGA_HEADER_SIZE ) {
+ Com_WPrintf( "LoadTGA: %s: file too small\n", filename );
+ goto finish;
+ }
+
+ id_length = buffer[0];
+ image_type = buffer[2];
+ w = MakeShort( buffer[12], buffer[13] );
+ h = MakeShort( buffer[14], buffer[15] );
+ pixel_size = buffer[16];
+ attributes = buffer[17];
+
+ // skip TARGA image comment
+ offset = TARGA_HEADER_SIZE + id_length;
+ if( offset + 4 > length ) {
+ Com_WPrintf( "LoadTGA: %s: offset out of range\n", filename );
+ goto finish;
+ }
+
+ if( pixel_size == 32 ) {
+ bpp = 4;
+ } else if( pixel_size == 24 ) {
+ bpp = 3;
+ } else {
+ Com_WPrintf( "LoadTGA: %s: only 32 and 24 bit targa RGB "
+ "images supported, this one is %d bit\n",
+ filename, pixel_size );
+ goto finish;
+ }
+
+ if( w < 1 || h < 1 || w > MAX_TEXTURE_SIZE || h > MAX_TEXTURE_SIZE ) {
+ Com_WPrintf( "LoadTGA: %s: has strange dimensions: %dx%d\n",
+ filename, w, h );
+ goto finish;
+ }
+
+ if( image_type == 2 ) {
+ if( offset + w * h * bpp > length ) {
+ Com_WPrintf( "LoadTGA: %s: malformed targa image\n", filename );
+ goto finish;
+ }
+ if( attributes & 32 ) {
+ if( pixel_size == 32 ) {
+ decode = tga_decode_bgra_flip;
+ } else {
+ decode = tga_decode_bgr_flip;
+ }
+ } else {
+ if( pixel_size == 32 ) {
+ decode = tga_decode_bgra;
+ } else {
+ decode = tga_decode_bgr;
+ }
+ }
+ } else if( image_type == 10 ) {
+ if( attributes & 32 ) {
+ Com_WPrintf( "LoadTGA: %s: vertically flipped, RLE encoded "
+ "images are not supported\n", filename );
+ goto finish;
+ }
+ if( pixel_size == 32 ) {
+ decode = tga_decode_bgra_rle;
+ } else {
+ decode = tga_decode_bgr_rle;
+ }
+ } else {
+ Com_WPrintf( "LoadTGA: %s: only type 2 and 10 targa RGB "
+ "images supported, this one is %d\n",
+ filename, image_type );
+ goto finish;
+ }
+
+ pixels = fs.AllocTempMem( w * h * 4 );
+
+ if( (*decode)( buffer + offset, pixels, w, h, buffer + length ) == qfalse ) {
+ fs.FreeFile( pixels );
+ goto finish;
+ }
+
+ *pic = pixels;
+ *width = w;
+ *height = h;
+
+finish:
+ fs.FreeFile( buffer );
+}
+
+/*
+=========================================================
+
+TARGA WRITING
+
+=========================================================
+*/
+
+/*
+=================
+Image_WriteTGA
+=================
+*/
+qboolean Image_WriteTGA( const char *filename, const byte *bgr,
+ int width, int height )
+{
+ int length;
+ fileHandle_t f;
+ byte header[TARGA_HEADER_SIZE];
+
+ fs.FOpenFile( filename, &f, FS_MODE_WRITE );
+ if( !f ) {
+ return qfalse;
+ }
+
+ memset( &header, 0, sizeof( header ) );
+ header[ 2] = 2; // uncompressed type
+ header[12] = width & 255;
+ header[13] = width >> 8;
+ header[14] = height & 255;
+ header[15] = height >> 8;
+ header[16] = 24; // pixel size
+
+ if( fs.Write( &header, sizeof( header ), f ) != sizeof( header ) ) {
+ goto fail;
+ }
+
+ length = width * height * 3;
+ if( fs.Write( bgr, length, f ) != length ) {
+ goto fail;
+ }
+
+ fs.FCloseFile( f );
+ return qtrue;
+
+fail:
+ fs.FCloseFile( f );
+ return qfalse;
+}
+
+
+/*
+=========================================================
+
+JPEG LOADING
+
+=========================================================
+*/
+
+#ifdef USE_JPEG
+
+typedef struct my_error_mgr {
+ struct jpeg_error_mgr pub;
+ jmp_buf setjmp_buffer;
+} *my_error_ptr;
+
+METHODDEF( void )my_error_exit( j_common_ptr cinfo ) {
+ my_error_ptr myerr = ( my_error_ptr )cinfo->err;
+
+ (*cinfo->err->output_message)( cinfo );
+
+ longjmp( myerr->setjmp_buffer, 1 );
+}
+
+
+METHODDEF( void ) mem_init_source( j_decompress_ptr cinfo ) { }
+
+METHODDEF( boolean ) mem_fill_input_buffer( j_decompress_ptr cinfo ) {
+ my_error_ptr jerr = ( my_error_ptr )cinfo->err;
+
+ longjmp( jerr->setjmp_buffer, 1 );
+ return TRUE;
+}
+
+
+METHODDEF( void ) mem_skip_input_data( j_decompress_ptr cinfo, long num_bytes ) {
+ struct jpeg_source_mgr *src = cinfo->src;
+ my_error_ptr jerr = ( my_error_ptr )cinfo->err;
+
+ if( src->bytes_in_buffer < num_bytes ) {
+ longjmp( jerr->setjmp_buffer, 1 );
+ }
+
+ src->next_input_byte += ( size_t )num_bytes;
+ src->bytes_in_buffer -= ( size_t )num_bytes;
+}
+
+METHODDEF( void ) mem_term_source( j_decompress_ptr cinfo ) { }
+
+
+METHODDEF( void ) jpeg_mem_src( j_decompress_ptr cinfo, byte *memory, int size ) {
+ 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 = mem_init_source;
+ cinfo->src->fill_input_buffer = mem_fill_input_buffer;
+ cinfo->src->skip_input_data = mem_skip_input_data;
+ cinfo->src->resync_to_restart = jpeg_resync_to_restart;
+ cinfo->src->term_source = mem_term_source;
+ cinfo->src->bytes_in_buffer = size;
+ cinfo->src->next_input_byte = memory;
+}
+
+/*
+=================
+LoadJPG
+=================
+*/
+void Image_LoadJPG( const char *filename, byte **pic, int *width, int *height ) {
+ struct jpeg_decompress_struct cinfo;
+ struct my_error_mgr jerr;
+ JSAMPARRAY buffer;
+ int row_stride;
+ byte *rawdata;
+ int rawlength;
+ byte *pixels;
+ byte *src, *dest;
+ int i;
+
+ if( !filename || !pic ) {
+ Com_Error( ERR_FATAL, "LoadJPG: NULL" );
+ }
+
+ *pic = NULL;
+ pixels = NULL;
+
+ rawlength = fs.LoadFile( filename, ( void ** )&rawdata );
+ if( !rawdata ) {
+ return;
+ }
+
+ if( rawlength < 10 || *( uint32 * )( rawdata + 6 ) != MakeLong( 'J', 'F', 'I', 'F' ) ) {
+ Com_WPrintf( "LoadJPG: %s: not a valid JPEG file\n", filename );
+ fs.FreeFile( rawdata );
+ return;
+ }
+
+ cinfo.err = jpeg_std_error( &jerr.pub );
+ jerr.pub.error_exit = my_error_exit;
+
+ jpeg_create_decompress( &cinfo );
+
+ if( setjmp( jerr.setjmp_buffer ) ) {
+ Com_WPrintf( "LoadJPG: %s: JPEGLIB signaled an error\n", filename );
+ jpeg_destroy_decompress( &cinfo );
+ if( pixels ) {
+ fs.FreeFile( pixels );
+ }
+ fs.FreeFile( rawdata );
+ return;
+ }
+
+ jpeg_mem_src( &cinfo, rawdata, rawlength );
+ jpeg_read_header( &cinfo, TRUE );
+ jpeg_start_decompress( &cinfo );
+
+ if( cinfo.output_components != 3 /*&& cinfo.output_components != 4*/ ) {
+ Com_WPrintf( "LoadJPG: %s: unsupported number of color components: %i\n",
+ filename, cinfo.output_components );
+ jpeg_destroy_decompress( &cinfo );
+ fs.FreeFile( rawdata );
+ return;
+ }
+
+ *width = cinfo.output_width;
+ *height = cinfo.output_height;
+
+ pixels = fs.AllocTempMem( cinfo.output_width * cinfo.output_height * 4 );
+
+ row_stride = cinfo.output_width * cinfo.output_components;
+
+ buffer = (*cinfo.mem->alloc_sarray)( ( j_common_ptr )&cinfo, JPOOL_IMAGE, row_stride, 1 );
+
+ dest = pixels;
+ while( cinfo.output_scanline < cinfo.output_height ) {
+ jpeg_read_scanlines( &cinfo, buffer, 1 );
+
+ src = ( byte * )buffer[0];
+ for( i = 0; i < cinfo.output_width; i++ ) {
+ *( uint32 * )dest = MakeColor( src[0], src[1], src[2], 255 );
+ src += 3;
+ dest += 4;
+ }
+ }
+
+ jpeg_finish_decompress( &cinfo );
+ jpeg_destroy_decompress( &cinfo );
+
+ fs.FreeFile( rawdata );
+
+ *pic = pixels;
+
+}
+
+/*
+=========================================================
+
+JPEG WRITING
+
+=========================================================
+*/
+
+#define OUTPUT_BUF_SIZE 4096
+
+typedef struct my_destination_mgr {
+ struct jpeg_destination_mgr pub; /* public fields */
+
+ fileHandle_t hFile; /* target stream */
+ JOCTET *buffer; /* start of buffer */
+} *my_dest_ptr;
+
+
+METHODDEF( void ) vfs_init_destination( j_compress_ptr cinfo ) {
+ my_dest_ptr dest = ( my_dest_ptr )cinfo->dest;
+
+ /* Allocate the output buffer --- it will be released when done with image */
+ dest->buffer = ( JOCTET * )(*cinfo->mem->alloc_small)( ( j_common_ptr )cinfo, JPOOL_IMAGE, OUTPUT_BUF_SIZE * sizeof( JOCTET ) );
+
+ dest->pub.next_output_byte = dest->buffer;
+ dest->pub.free_in_buffer = OUTPUT_BUF_SIZE;
+}
+
+METHODDEF( boolean ) vfs_empty_output_buffer( j_compress_ptr cinfo ) {
+ my_dest_ptr dest = ( my_dest_ptr )cinfo->dest;
+ my_error_ptr jerr = ( my_error_ptr )cinfo->err;
+
+ if( fs.Write( dest->buffer, OUTPUT_BUF_SIZE, dest->hFile ) != OUTPUT_BUF_SIZE ) {
+ longjmp( jerr->setjmp_buffer, 1 );
+ }
+
+ dest->pub.next_output_byte = dest->buffer;
+ dest->pub.free_in_buffer = OUTPUT_BUF_SIZE;
+
+ return TRUE;
+
+}
+
+METHODDEF( void ) vfs_term_destination( j_compress_ptr cinfo ) {
+ my_dest_ptr dest = ( my_dest_ptr )cinfo->dest;
+ my_error_ptr jerr = ( my_error_ptr )cinfo->err;
+ int remaining = OUTPUT_BUF_SIZE - dest->pub.free_in_buffer;
+
+ /* Write any data remaining in the buffer */
+ if( remaining > 0 ) {
+ if( fs.Write( dest->buffer, remaining, dest->hFile ) != remaining ) {
+ longjmp( jerr->setjmp_buffer, 1 );
+ }
+ }
+
+}
+
+
+METHODDEF( void ) jpeg_vfs_dst( j_compress_ptr cinfo, fileHandle_t hFile ) {
+ my_dest_ptr dest;
+
+ dest = ( my_dest_ptr )(*cinfo->mem->alloc_small)( ( j_common_ptr )cinfo, JPOOL_PERMANENT, sizeof( struct my_destination_mgr ) );
+ cinfo->dest = &dest->pub;
+
+ dest->pub.init_destination = vfs_init_destination;
+ dest->pub.empty_output_buffer = vfs_empty_output_buffer;
+ dest->pub.term_destination = vfs_term_destination;
+ dest->hFile = hFile;
+
+}
+
+/*
+=================
+Image_WriteJPG
+=================
+*/
+qboolean Image_WriteJPG( const char *filename, const byte *rgb, int width, int height, int quality ) {
+ struct jpeg_compress_struct cinfo;
+ struct my_error_mgr jerr;
+ fileHandle_t hFile;
+ JSAMPROW row_pointer[1];
+ int row_stride;
+
+ fs.FOpenFile( filename, &hFile, FS_MODE_WRITE );
+ if( !hFile ) {
+ Com_DPrintf( "WriteJPG: %s: couldn't create file\n", filename );
+ return qfalse;
+ }
+
+ cinfo.err = jpeg_std_error( &jerr.pub );
+ jerr.pub.error_exit = my_error_exit;
+
+ if( _setjmp( jerr.setjmp_buffer ) ) {
+ Com_DPrintf( "WriteJPG: %s: JPEGLIB signaled an error\n", filename );
+ jpeg_destroy_compress( &cinfo );
+ fs.FCloseFile( hFile );
+ return qfalse;
+ }
+
+ jpeg_create_compress( &cinfo );
+
+ jpeg_vfs_dst( &cinfo, hFile );
+
+ cinfo.image_width = width; // image width and height, in pixels
+ cinfo.image_height = height;
+ cinfo.input_components = 3; // # of color components per pixel
+ cinfo.in_color_space = JCS_RGB; // colorspace of input image
+
+ clamp( quality, 0, 100 );
+
+ jpeg_set_defaults( &cinfo );
+ jpeg_set_quality( &cinfo, quality, TRUE );
+
+ jpeg_start_compress( &cinfo, TRUE );
+
+ row_stride = width * 3; // JSAMPLEs per row in image_buffer
+
+ while( cinfo.next_scanline < cinfo.image_height ) {
+ row_pointer[0] = ( byte * )( &rgb[( cinfo.image_height - cinfo.next_scanline - 1 ) * row_stride] );
+ jpeg_write_scanlines( &cinfo, row_pointer, 1 );
+ }
+
+ jpeg_finish_compress( &cinfo );
+ fs.FCloseFile( hFile );
+
+ jpeg_destroy_compress( &cinfo );
+
+ return qtrue;
+}
+
+#endif /* USE_JPEG */
+
+
+#ifdef USE_PNG
+
+/*
+=========================================================
+
+PNG LOADING
+
+=========================================================
+*/
+
+struct pngReadStruct {
+ byte *data;
+ byte *maxp;
+};
+
+static void png_vfs_read_fn( png_structp png_ptr, png_bytep buf, png_size_t size ) {
+ struct pngReadStruct *r = png_get_io_ptr( png_ptr );
+
+ if( r->data + size > r->maxp ) {
+ size = r->maxp - r->data;
+ }
+ memcpy( buf, r->data, size );
+ r->data += size;
+}
+
+static void png_console_error_fn( png_structp png_ptr, png_const_charp error_msg ) {
+ char *f = png_get_error_ptr( png_ptr );
+
+ Com_EPrintf( "LoadPNG: %s: %s\n", f, error_msg );
+ longjmp( png_jmpbuf( png_ptr ), -1 );
+}
+
+static void png_console_warning_fn( png_structp png_ptr, png_const_charp warning_msg ) {
+ char *f = png_get_error_ptr( png_ptr );
+
+ Com_WPrintf( "LoadPNG: %s: %s\n", f, warning_msg );
+}
+
+/*
+=================
+LoadPNG
+=================
+*/
+void Image_LoadPNG( const char *filename, byte **pic, int *width, int *height ) {
+ byte *rawdata;
+ int rawlength;
+ byte *pixels;
+ byte *row_pointers[MAX_TEXTURE_SIZE];
+ png_uint_32 w, h, rowbytes, row;
+ int bitdepth, colortype;
+ png_structp png_ptr;
+ png_infop info_ptr;
+ struct pngReadStruct r;
+
+ if( !filename || !pic ) {
+ Com_Error( ERR_FATAL, "LoadPNG: NULL" );
+ }
+
+ *pic = NULL;
+ pixels = NULL;
+
+ rawlength = fs.LoadFile( filename, ( void ** )&rawdata );
+ if( !rawdata ) {
+ return;
+ }
+
+ png_ptr = png_create_read_struct( PNG_LIBPNG_VER_STRING,
+ ( png_voidp )filename, png_console_error_fn, png_console_warning_fn );
+ if( !png_ptr ) {
+ goto fail;
+ }
+
+ info_ptr = png_create_info_struct( png_ptr );
+ if( !info_ptr ) {
+ png_destroy_read_struct( &png_ptr, NULL, NULL );
+ goto fail;
+ }
+
+ if( setjmp( png_jmpbuf( png_ptr ) ) ) {
+ png_destroy_read_struct( &png_ptr, &info_ptr, NULL );
+ if( pixels ) {
+ fs.FreeFile( pixels );
+ }
+ goto fail;
+ }
+
+ r.data = rawdata;
+ r.maxp = rawdata + rawlength;
+ png_set_read_fn( png_ptr, ( png_voidp )&r, png_vfs_read_fn );
+
+ png_read_info( png_ptr, info_ptr );
+
+ if( !png_get_IHDR( png_ptr, info_ptr, &w, &h, &bitdepth, &colortype,
+ NULL, NULL, NULL ) )
+ {
+ png_destroy_read_struct( &png_ptr, &info_ptr, NULL );
+ goto fail;
+ }
+
+ if( w > MAX_TEXTURE_SIZE || h > MAX_TEXTURE_SIZE ) {
+ Com_EPrintf( "LoadPNG: %s: oversize image dimensions: %lux%lu\n",
+ filename, w, h );
+ png_destroy_read_struct( &png_ptr, &info_ptr, NULL );
+ goto fail;
+ }
+
+ switch( colortype ) {
+ case PNG_COLOR_TYPE_PALETTE:
+ png_set_palette_to_rgb( png_ptr );
+ break;
+ case PNG_COLOR_TYPE_GRAY:
+ if( bitdepth < 8 ) {
+ png_set_gray_1_2_4_to_8( png_ptr );
+ }
+ // fall through
+ case PNG_COLOR_TYPE_GRAY_ALPHA:
+ png_set_gray_to_rgb( png_ptr );
+ break;
+ }
+
+ if( bitdepth < 8 ) {
+ png_set_packing( png_ptr );
+ } else if( bitdepth == 16 ) {
+ png_set_strip_16( png_ptr );
+ }
+
+ if( png_get_valid( png_ptr, info_ptr, PNG_INFO_tRNS ) ) {
+ png_set_tRNS_to_alpha( png_ptr );
+ }
+
+ png_set_filler( png_ptr, 0xff, PNG_FILLER_AFTER );
+
+ png_read_update_info( png_ptr, info_ptr );
+
+ rowbytes = png_get_rowbytes( png_ptr, info_ptr );
+ pixels = fs.AllocTempMem( h * rowbytes );
+
+ for( row = 0; row < h; row++ ) {
+ row_pointers[row] = pixels + row * rowbytes;
+ }
+
+ png_read_image( png_ptr, row_pointers );
+
+ png_read_end( png_ptr, info_ptr );
+
+ png_destroy_read_struct( &png_ptr, &info_ptr, NULL );
+
+ *pic = pixels;
+ *width = w;
+ *height = h;
+
+fail:
+ fs.FreeFile( rawdata );
+}
+
+#endif /* USE_PNG */
+
+#endif /* TRUECOLOR_RENDERER */
+
+/*
+=========================================================
+
+IMAGE MANAGER
+
+=========================================================
+*/
+
+image_t r_images[MAX_RIMAGES];
+list_t r_imageHash[RIMAGES_HASH];
+int r_numImages;
+
+uint32 d_8to24table[256];
+
+#ifdef TRUECOLOR_RENDERER
+
+static cvar_t *r_override_textures;
+
+/*
+================
+R_ResampleTexture
+================
+*/
+void R_ResampleTexture( const byte *in, int inwidth, int inheight, byte *out, int outwidth, int outheight ) {
+ int i, j;
+ const byte *inrow1, *inrow2;
+ uint32 frac, fracstep;
+ uint32 p1[MAX_TEXTURE_SIZE], p2[MAX_TEXTURE_SIZE];
+ const byte *pix1, *pix2, *pix3, *pix4;
+ float heightScale;
+
+ if( outwidth > MAX_TEXTURE_SIZE ) {
+ Com_Error( ERR_FATAL, "ResampleTexture: outwidth > %d",
+ MAX_TEXTURE_SIZE );
+ }
+
+ 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;
+ }
+
+ heightScale = ( float )inheight / outheight;
+ inwidth <<= 2;
+ for( i = 0; i < outheight; i++ ) {
+ inrow1 = in + inwidth * ( int )( ( i + 0.25f ) * heightScale );
+ inrow2 = in + inwidth * ( int )( ( i + 0.75f ) * heightScale );
+ for( j = 0; j < outwidth; j++ ) {
+ pix1 = inrow1 + p1[j];
+ pix2 = inrow1 + p2[j];
+ pix3 = inrow2 + p1[j];
+ pix4 = inrow2 + p2[j];
+ out[0] = ( pix1[0] + pix2[0] + pix3[0] + pix4[0] ) >> 2;
+ out[1] = ( pix1[1] + pix2[1] + pix3[1] + pix4[1] ) >> 2;
+ out[2] = ( pix1[2] + pix2[2] + pix3[2] + pix4[2] ) >> 2;
+ out[3] = ( pix1[3] + pix2[3] + pix3[3] + pix4[3] ) >> 2;
+ out += 4;
+ }
+ }
+}
+
+#endif /* TRUECOLOR_RENDERER */
+
+/*
+===============
+R_ImageList_f
+===============
+*/
+static void R_ImageList_f( void ) {
+ int i;
+ image_t *image;
+ int texels;
+
+ Com_Printf( "------------------\n");
+ texels = 0;
+
+ for( i = 0, image = r_images; i < r_numImages; i++, image++ ) {
+ if( !image->registration_sequence )
+ continue;
+ texels += image->upload_width * image->upload_height;
+ switch( image->type ) {
+ case it_skin:
+ Com_Printf( "M" );
+ break;
+ case it_sprite:
+ Com_Printf( "S" );
+ break;
+ case it_wall:
+ Com_Printf( "W" );
+ break;
+ case it_pic:
+ Com_Printf( "P" );
+ break;
+ case it_sky:
+ Com_Printf( "Y" );
+ break;
+ case it_lightmap:
+ Com_Printf( "L" );
+ break;
+ case it_charset:
+ Com_Printf( "C" );
+ break;
+ default:
+ Com_Printf( " " );
+ break;
+ }
+
+ Com_Printf( " %4i %4i %s: %s\n", image->upload_width,
+ image->upload_height, ( image->flags & if_paletted ) ? "PAL" : "RGB",
+ image->name );
+ }
+ Com_Printf( "Total texel count (not counting mipmaps): %i\n", texels );
+}
+
+static image_t *R_AllocImageInternal( const char *name, uint32 hash ) {
+ int i;
+ image_t *image;
+
+ // find a free image_t slot
+ for( i = 0, image = r_images; i < r_numImages; i++, image++ ) {
+ if( !image->registration_sequence )
+ break;
+ }
+
+ if( i == r_numImages ) {
+ if( r_numImages == MAX_RIMAGES )
+ Com_Error( ERR_FATAL, "R_AllocImage: MAX_IMAGES" );
+ r_numImages++;
+ }
+
+ strcpy( image->name, name );
+ List_Append( &r_imageHash[hash], &image->entry );
+
+ image->registration_sequence = registration_sequence;
+
+ return image;
+}
+
+/*
+===============
+R_LookupImage
+
+Finds the given image of the given type.
+Case and extension insensitive.
+===============
+*/
+static image_t *R_LookupImage( const char *name, imagetype_t type,
+ uint32 hash, int baselength )
+{
+ image_t *image;
+
+ // look for it
+ LIST_FOR_EACH( image_t, image, &r_imageHash[hash], entry ) {
+ if( image->type != type ) {
+ continue;
+ }
+ if( !Q_stricmpn( image->name, name, baselength ) ) {
+ return image;
+ }
+ }
+
+ return NULL;
+}
+
+image_t *R_AllocImage( const char *name ) {
+ char buffer[MAX_QPATH];
+ char *ext;
+ uint32 hash;
+ image_t *image;
+ int length;
+
+ if( !name || !name[0] ) {
+ Com_Error( ERR_FATAL, "R_AllocImage: NULL" );
+ }
+
+ length = strlen( name );
+ if( length >= MAX_QPATH ) {
+ Com_Error( ERR_FATAL, "R_AllocImage: oversize name: %d chars", length );
+ }
+
+ strcpy( buffer, name );
+
+ ext = COM_FileExtension( buffer );
+ if( *ext == '.' ) {
+ *ext = 0;
+ } else {
+ ext = NULL;
+ }
+ hash = Com_HashPath( buffer, RIMAGES_HASH );
+ if( ext ) {
+ *ext = '.';
+ }
+
+ image = R_AllocImageInternal( buffer, hash );
+
+ return image;
+}
+
+
+image_t *R_CreateImage( const char *name, byte *pic, int width, int height,
+ imagetype_t type, imageflags_t flags )
+{
+ image_t *image;
+
+ image = R_AllocImage( name );
+ R_LoadImage( image, pic, width, height, type, flags );
+
+ return image;
+}
+
+
+#ifdef TRUECOLOR_RENDERER
+
+/*
+===============
+R_FindImage
+
+Finds or loads the given image (8 or 32 bit)
+===============
+*/
+image_t *R_FindImage( const char *name, imagetype_t type ) {
+ image_t *image;
+ byte *pic;
+ int width, height;
+ char buffer[MAX_QPATH];
+ char *ext;
+ int length;
+ uint32 hash, extHash;
+ imageflags_t flags;
+
+ if( !name || !name[0] ) {
+ Com_Error( ERR_FATAL, "R_FindImage: NULL" );
+ }
+
+ length = strlen( name );
+ if( length >= MAX_QPATH ) {
+ Com_Error( ERR_FATAL, "R_FindImage: oversize name: %d chars", length );
+ }
+
+ if( length <= 4 ) {
+ /* must have at least 1 char of basename
+ * and 4 chars of extension part */
+ return NULL;
+ }
+
+ length -= 4;
+ if( name[length] != '.' ) {
+ return NULL;
+ }
+
+ strcpy( buffer, name );
+ buffer[length] = 0;
+
+ hash = Com_HashPath( buffer, RIMAGES_HASH );
+
+ if( ( image = R_LookupImage( buffer, type, hash, length ) ) != NULL ) {
+ image->registration_sequence = registration_sequence;
+ return image;
+ }
+
+ ext = buffer + length;
+ Q_strlwr( ext + 1 );
+ extHash = MakeLong( '.', ext[1], ext[2], ext[3] );
+
+ //
+ // load the pic from disk
+ //
+ pic = NULL;
+ image = NULL;
+ flags = 0;
+
+ if( r_override_textures->integer ) {
+#ifdef USE_PNG
+ // try *.png
+ strcpy( ext, ".png" );
+ Image_LoadPNG( buffer, &pic, &width, &height );
+
+ if( !pic )
+#endif
+ {
+ // try *.tga
+ strcpy( ext, ".tga" );
+ Image_LoadTGA( buffer, &pic, &width, &height );
+
+#ifdef USE_JPEG
+ if( !pic ) {
+ // try *.jpg
+ strcpy( ext, ".jpg" );
+ Image_LoadJPG( buffer, &pic, &width, &height );
+ }
+#endif
+ }
+
+ if( pic ) {
+ // replacing 8 bit texture with 32 bit texture
+ if( extHash == EXTENSION_WAL ) {
+ flags |= if_replace_wal;
+ } else if( extHash == EXTENSION_PCX ) {
+ flags |= if_replace_pcx;
+ }
+ goto load;
+ }
+
+ switch( extHash ) {
+ case EXTENSION_PNG:
+ case EXTENSION_TGA:
+ case EXTENSION_JPG:
+ case EXTENSION_PCX:
+ strcpy( ext, ".pcx" );
+ Image_LoadPCX( buffer, &pic, NULL, &width, &height );
+ if( pic ) {
+ flags |= if_paletted;
+ goto load;
+ }
+ return NULL;
+ case EXTENSION_WAL:
+ strcpy( ext, ".wal" );
+ image = R_LoadWal( buffer );
+ return image;
+ }
+
+ return NULL;
+ }
+
+ switch( extHash ) {
+ case EXTENSION_PNG:
+#ifdef USE_PNG
+ // try *.png
+ strcpy( ext, ".png" );
+ Image_LoadPNG( buffer, &pic, &width, &height );
+ if( pic ) {
+ goto load;
+ }
+#endif
+
+ // try *.tga
+ strcpy( ext, ".tga" );
+ Image_LoadTGA( buffer, &pic, &width, &height );
+ if( pic ) {
+ goto load;
+ }
+
+#ifdef USE_JPEG
+ // try *.jpg
+ strcpy( ext, ".jpg" );
+ Image_LoadJPG( buffer, &pic, &width, &height );
+ if( pic ) {
+ goto load;
+ }
+#endif
+
+ // try *.pcx
+ strcpy( ext, ".pcx" );
+ Image_LoadPCX( buffer, &pic, NULL, &width, &height );
+ if( pic ) {
+ flags |= if_paletted;
+ goto load;
+ }
+ return NULL;
+
+ case EXTENSION_TGA:
+ strcpy( ext, ".tga" );
+ Image_LoadTGA( buffer, &pic, &width, &height );
+ if( pic ) {
+ goto load;
+ }
+
+#ifdef USE_PNG
+ // try *.png
+ strcpy( ext, ".png" );
+ Image_LoadPNG( buffer, &pic, &width, &height );
+ if( pic ) {
+ goto load;
+ }
+#endif
+
+#ifdef USE_JPEG
+ // try *.jpg
+ strcpy( ext, ".jpg" );
+ Image_LoadJPG( buffer, &pic, &width, &height );
+ if( pic ) {
+ goto load;
+ }
+#endif
+
+ // try *.pcx
+ strcpy( ext, ".pcx" );
+ Image_LoadPCX( buffer, &pic, NULL, &width, &height );
+ if( pic ) {
+ flags |= if_paletted;
+ goto load;
+ }
+ return NULL;
+
+ case EXTENSION_JPG:
+#ifdef USE_JPEG
+ strcpy( ext, ".jpg" );
+ Image_LoadJPG( buffer, &pic, &width, &height );
+ if( pic ) {
+ goto load;
+ }
+#endif
+
+#ifdef USE_PNG
+ // try *.png
+ strcpy( ext, ".png" );
+ Image_LoadPNG( buffer, &pic, &width, &height );
+ if( pic ) {
+ goto load;
+ }
+#endif
+
+ // try *.tga
+ strcpy( ext, ".tga" );
+ Image_LoadTGA( buffer, &pic, &width, &height );
+ if( pic ) {
+ goto load;
+ }
+
+ // try *.pcx
+ strcpy( ext, ".pcx" );
+ Image_LoadPCX( buffer, &pic, NULL, &width, &height );
+ if( pic ) {
+ flags |= if_paletted;
+ goto load;
+ }
+ return NULL;
+
+
+ case EXTENSION_PCX:
+ strcpy( ext, ".pcx" );
+ Image_LoadPCX( buffer, &pic, NULL, &width, &height );
+ if( pic ) {
+ flags |= if_paletted;
+ goto load;
+ }
+
+#ifdef USE_PNG
+ // try *.png
+ strcpy( ext, ".png" );
+ Image_LoadPNG( buffer, &pic, &width, &height );
+ if( pic ) {
+ goto load;
+ }
+#endif
+
+ // try *.tga
+ strcpy( ext, ".tga" );
+ Image_LoadTGA( buffer, &pic, &width, &height );
+ if( pic ) {
+ goto load;
+ }
+
+#ifdef USE_JPEG
+ // try *.jpg
+ strcpy( ext, ".jpg" );
+ Image_LoadJPG( buffer, &pic, &width, &height );
+ if( pic ) {
+ goto load;
+ }
+#endif
+
+ return NULL;
+
+ case EXTENSION_WAL:
+ strcpy( ext, ".wal" );
+ image = R_LoadWal( buffer );
+ if( image ) {
+ return image;
+ }
+
+ // FIXME: no way to figure correct texture dimensions here
+
+#ifdef USE_PNG
+ // try *.png
+ strcpy( ext, ".png" );
+ Image_LoadPNG( buffer, &pic, &width, &height );
+ if( pic ) {
+ goto load;
+ }
+#endif
+
+ // try *.tga
+ strcpy( ext, ".tga" );
+ Image_LoadTGA( buffer, &pic, &width, &height );
+ if( pic ) {
+ goto load;
+ }
+
+#ifdef USE_JPEG
+ // try *.jpg
+ strcpy( ext, ".jpg" );
+ Image_LoadJPG( buffer, &pic, &width, &height );
+ if( pic ) {
+ goto load;
+ }
+#endif
+
+ return NULL;
+
+ default:
+ return NULL;
+
+ }
+
+load:
+ image = R_AllocImageInternal( buffer, hash );
+ R_LoadImage( image, pic, width, height, type, flags );
+ return image;
+}
+
+#else /* TRUECOLOR_RENDERER */
+
+/*
+===============
+R_FindImage
+
+Finds or loads the given image (8 bit)
+===============
+*/
+image_t *R_FindImage( const char *name, imagetype_t type ) {
+ image_t *image;
+ byte *pic;
+ int width, height;
+ char buffer[MAX_QPATH];
+ char *ext;
+ int length;
+ uint32 hash, extHash;
+
+ if( !name || !name[0] ) {
+ Com_Error( ERR_FATAL, "R_FindImage: NULL" );
+ }
+
+ length = strlen( name );
+ if( length >= MAX_QPATH ) {
+ Com_Error( ERR_FATAL, "R_FindImage: oversize name: %d chars", length );
+ }
+
+ if( length <= 4 ) {
+ /* must have at least 1 char of basename
+ * and 4 chars of extension part */
+ return NULL;
+ }
+
+ length -= 4;
+ if( name[length] != '.' ) {
+ return NULL;
+ }
+
+ strcpy( buffer, name );
+ Q_strlwr( buffer );
+ buffer[length] = 0;
+
+ hash = Com_HashPath( buffer, RIMAGES_HASH );
+
+ if( ( image = R_LookupImage( buffer, type, hash, length ) ) != NULL ) {
+ image->registration_sequence = registration_sequence;
+ return image;
+ }
+
+ ext = buffer + length;
+ extHash = MakeLong( '.', ext[1], ext[2], ext[3] );
+
+ *ext = '.';
+
+ //
+ // load the pic from disk
+ //
+ if( extHash == EXTENSION_JPG || extHash == EXTENSION_TGA || extHash == EXTENSION_PNG ) {
+ strcpy( ext, ".pcx" );
+ extHash = EXTENSION_PCX;
+ }
+ if( extHash == EXTENSION_PCX ) {
+ Image_LoadPCX( buffer, &pic, NULL, &width, &height );
+ if( !pic ) {
+ return NULL;
+ }
+ image = R_AllocImageInternal( buffer, hash );
+ R_LoadImage( image, pic, width, height, type, if_paletted );
+ return image;
+ }
+
+ if( extHash == EXTENSION_WAL ) {
+ image = R_LoadWal( buffer );
+ return image;
+ }
+
+ return NULL;
+}
+
+#endif /* !TRUECOLOR_RENDERER */
+
+/*
+================
+R_FreeUnusedImages
+
+Any image that was not touched on this registration sequence
+will be freed.
+================
+*/
+void R_FreeUnusedImages( void ) {
+ image_t *image, *last;
+
+ last = r_images + r_numImages;
+ for( image = r_images; image != last; image++ ) {
+ if( image->registration_sequence == registration_sequence ) {
+#ifdef SOFTWARE_RENDERER
+ Com_PageInMemory( image->pixels[0], image->width * image->height * VID_BYTES );
+#endif
+ continue; // used this sequence
+ }
+ if( !image->registration_sequence )
+ continue; // free image_t slot
+ if( image->type == it_pic || image->type == it_charset )
+ continue; // don't free pics
+ if( image->flags & if_auto ) {
+ if( image->type != it_lightmap ) {
+ continue; // never free r_notexture or particle texture
+ // always free lightmaps
+ }
+ }
+
+ // delete it from hash table
+ List_Remove( &image->entry );
+
+ // free it
+ R_FreeImage( image );
+
+ memset( image, 0, sizeof( *image ) );
+ }
+}
+
+void R_FreeAllImages( void ) {
+ image_t *image, *last;
+ int i;
+
+ last = r_images + r_numImages;
+ for( image = r_images; image != last; image++ ) {
+ if( !image->registration_sequence )
+ continue; // free image_t slot
+ // free it
+ R_FreeImage( image );
+
+ memset( image, 0, sizeof( *image ) );
+ }
+
+ Com_DPrintf( "R_FreeAllImages: %i images freed\n", r_numImages );
+
+ r_numImages = 0;
+ for( i = 0; i < RIMAGES_HASH; i++ ) {
+ List_Init( &r_imageHash[i] );
+ }
+}
+
+/*
+===============
+R_GetPalette
+===============
+*/
+void R_GetPalette( byte **dest ) {
+ int i;
+ byte *pic, *src;
+ byte palette[768];
+ int width, height;
+
+ /* get the palette */
+ Image_LoadPCX( "pics/colormap.pcx", &pic, palette, &width, &height );
+ if( !pic ) {
+ Com_Error( ERR_FATAL, "Couldn't load pics/colormap.pcx" );
+ }
+
+ src = palette;
+ for( i = 0; i < 255; i++ ) {
+ d_8to24table[i] = MakeColor( src[0], src[1], src[2], 255 );
+ src += 3;
+ }
+
+ /* 255 is transparent*/
+ d_8to24table[i] = MakeColor( src[0], src[1], src[2], 0 );
+
+ if( dest ) {
+ *dest = R_Malloc( width * height );
+ memcpy( *dest, pic, width * height );
+ }
+
+ fs.FreeFile( pic );
+}
+
+void R_InitImageManager( void ) {
+ int i;
+
+#ifdef TRUECOLOR_RENDERER
+ r_override_textures = cvar.Get( "r_override_textures", "0", CVAR_ARCHIVE|CVAR_LATCHED );
+#endif
+ cmd.AddCommand( "imagelist", R_ImageList_f );
+
+ for( i = 0; i < RIMAGES_HASH; i++ ) {
+ List_Init( &r_imageHash[i] );
+ }
+}
+
+void R_ShutdownImageManager( void ) {
+ cmd.RemoveCommand( "imagelist" );
+}
+