aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorludwig <ludwig@edf5b092-35ff-0310-97b2-ce42778d08ea>2008-02-14 11:13:18 +0000
committerludwig <ludwig@edf5b092-35ff-0310-97b2-ce42778d08ea>2008-02-14 11:13:18 +0000
commit2acf691603cb0fb7c8b580befbc7be49b9300e53 (patch)
tree1c1ec858e4702df6225f5a454187abe61cf7fbe1
parent9ce8f89cbcac7539ee4c7313ab1828d95100dce1 (diff)
downloadioquake3-aero-2acf691603cb0fb7c8b580befbc7be49b9300e53.tar.gz
ioquake3-aero-2acf691603cb0fb7c8b580befbc7be49b9300e53.zip
move all image decoders into separate files
git-svn-id: svn://svn.icculus.org/quake3/trunk@1257 edf5b092-35ff-0310-97b2-ce42778d08ea
-rw-r--r--Makefile4
-rw-r--r--code/qcommon/qfiles.h44
-rw-r--r--code/renderer/tr_image.c1216
-rw-r--r--code/renderer/tr_image_bmp.c236
-rw-r--r--code/renderer/tr_image_bmp.h1
-rw-r--r--code/renderer/tr_image_jpg.c581
-rw-r--r--code/renderer/tr_image_jpg.h1
-rw-r--r--code/renderer/tr_image_pcx.c166
-rw-r--r--code/renderer/tr_image_pcx.h1
-rw-r--r--code/renderer/tr_image_tga.c317
-rw-r--r--code/renderer/tr_image_tga.h1
11 files changed, 1313 insertions, 1255 deletions
diff --git a/Makefile b/Makefile
index e42a14e..ad85bf1 100644
--- a/Makefile
+++ b/Makefile
@@ -1175,6 +1175,10 @@ Q3OBJ = \
$(B)/client/tr_font.o \
$(B)/client/tr_image.o \
$(B)/client/tr_image_png.o \
+ $(B)/client/tr_image_jpg.o \
+ $(B)/client/tr_image_bmp.o \
+ $(B)/client/tr_image_tga.o \
+ $(B)/client/tr_image_pcx.o \
$(B)/client/tr_init.o \
$(B)/client/tr_light.o \
$(B)/client/tr_main.o \
diff --git a/code/qcommon/qfiles.h b/code/qcommon/qfiles.h
index 7b7ffe2..7f1ef96 100644
--- a/code/qcommon/qfiles.h
+++ b/code/qcommon/qfiles.h
@@ -69,50 +69,6 @@ typedef struct {
int jtrgLength; // number of jump table targets
} vmHeader_t;
-
-/*
-========================================================================
-
-PCX files are used for 8 bit images
-
-========================================================================
-*/
-
-typedef struct {
- char manufacturer;
- char version;
- char encoding;
- char bits_per_pixel;
- unsigned short xmin,ymin,xmax,ymax;
- unsigned short hres,vres;
- unsigned char palette[48];
- char reserved;
- char color_planes;
- unsigned short bytes_per_line;
- unsigned short palette_type;
- char filler[58];
- unsigned char data; // unbounded
-} pcx_t;
-
-
-/*
-========================================================================
-
-TGA files are used for 24/32 bit images
-
-========================================================================
-*/
-
-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;
-
-
-
/*
========================================================================
diff --git a/code/renderer/tr_image.c b/code/renderer/tr_image.c
index a478609..81a478e 100644
--- a/code/renderer/tr_image.c
+++ b/code/renderer/tr_image.c
@@ -22,22 +22,12 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
// tr_image.c
#include "tr_local.h"
-/*
- * Include file for users of JPEG library.
- * You will need to have included system headers that define at least
- * the typedefs FILE and size_t before you can include jpeglib.h.
- * (stdio.h is sufficient on ANSI-conforming systems.)
- * You may also wish to include "jerror.h".
- */
-
-#define JPEG_INTERNALS
-#include "../jpeg-6/jpeglib.h"
+#include "tr_image_bmp.h"
+#include "tr_image_jpg.h"
+#include "tr_image_pcx.h"
#include "tr_image_png.h"
-
-static void LoadBMP( const char *name, byte **pic, int *width, int *height );
-static void LoadTGA( const char *name, byte **pic, int *width, int *height );
-static void LoadJPG( const char *name, byte **pic, int *width, int *height );
+#include "tr_image_tga.h"
static byte s_intensitytable[256];
static unsigned char s_gammatable[256];
@@ -799,1202 +789,6 @@ image_t *R_CreateImage( const char *name, const byte *pic, int width, int height
return image;
}
-
-/*
-=========================================================
-
-BMP LOADING
-
-=========================================================
-*/
-typedef struct
-{
- char id[2];
- unsigned fileSize;
- unsigned reserved0;
- unsigned bitmapDataOffset;
- unsigned bitmapHeaderSize;
- unsigned width;
- unsigned height;
- unsigned short planes;
- unsigned short bitsPerPixel;
- unsigned compression;
- unsigned bitmapDataSize;
- unsigned hRes;
- unsigned vRes;
- unsigned colors;
- unsigned importantColors;
- unsigned char palette[256][4];
-} BMPHeader_t;
-
-static void LoadBMP( const char *name, byte **pic, int *width, int *height )
-{
- int columns, rows;
- unsigned numPixels;
- byte *pixbuf;
- int row, column;
- byte *buf_p;
- byte *end;
- byte *buffer = NULL;
- int length;
- BMPHeader_t bmpHeader;
- byte *bmpRGBA;
-
- *pic = NULL;
-
- if(width)
- *width = 0;
-
- if(height)
- *height = 0;
-
- //
- // load the file
- //
- length = ri.FS_ReadFile( ( char * ) name, (void **)&buffer);
- if (!buffer || length < 0) {
- return;
- }
-
- if (length < 54)
- {
- ri.Error( ERR_DROP, "LoadBMP: header too short (%s)\n", name );
- }
-
- buf_p = buffer;
- end = buffer + length;
-
- bmpHeader.id[0] = *buf_p++;
- bmpHeader.id[1] = *buf_p++;
- bmpHeader.fileSize = LittleLong( * ( int * ) buf_p );
- buf_p += 4;
- bmpHeader.reserved0 = LittleLong( * ( int * ) buf_p );
- buf_p += 4;
- bmpHeader.bitmapDataOffset = LittleLong( * ( int * ) buf_p );
- buf_p += 4;
- bmpHeader.bitmapHeaderSize = LittleLong( * ( int * ) buf_p );
- buf_p += 4;
- bmpHeader.width = LittleLong( * ( int * ) buf_p );
- buf_p += 4;
- bmpHeader.height = LittleLong( * ( int * ) buf_p );
- buf_p += 4;
- bmpHeader.planes = LittleShort( * ( short * ) buf_p );
- buf_p += 2;
- bmpHeader.bitsPerPixel = LittleShort( * ( short * ) buf_p );
- buf_p += 2;
- bmpHeader.compression = LittleLong( * ( int * ) buf_p );
- buf_p += 4;
- bmpHeader.bitmapDataSize = LittleLong( * ( int * ) buf_p );
- buf_p += 4;
- bmpHeader.hRes = LittleLong( * ( int * ) buf_p );
- buf_p += 4;
- bmpHeader.vRes = LittleLong( * ( int * ) buf_p );
- buf_p += 4;
- bmpHeader.colors = LittleLong( * ( int * ) buf_p );
- buf_p += 4;
- bmpHeader.importantColors = LittleLong( * ( int * ) buf_p );
- buf_p += 4;
-
- if ( bmpHeader.bitsPerPixel == 8 )
- {
- if (buf_p + sizeof(bmpHeader.palette) > end)
- ri.Error( ERR_DROP, "LoadBMP: header too short (%s)\n", name );
-
- Com_Memcpy( bmpHeader.palette, buf_p, sizeof( bmpHeader.palette ) );
- buf_p += sizeof(bmpHeader.palette);
- }
-
- if (buffer + bmpHeader.bitmapDataOffset > end)
- {
- ri.Error( ERR_DROP, "LoadBMP: invalid offset value in header (%s)\n", name );
- }
-
- buf_p = buffer + bmpHeader.bitmapDataOffset;
-
- if ( bmpHeader.id[0] != 'B' && bmpHeader.id[1] != 'M' )
- {
- ri.Error( ERR_DROP, "LoadBMP: only Windows-style BMP files supported (%s)\n", name );
- }
- if ( bmpHeader.fileSize != length )
- {
- ri.Error( ERR_DROP, "LoadBMP: header size does not match file size (%u vs. %u) (%s)\n", bmpHeader.fileSize, length, name );
- }
- if ( bmpHeader.compression != 0 )
- {
- ri.Error( ERR_DROP, "LoadBMP: only uncompressed BMP files supported (%s)\n", name );
- }
- if ( bmpHeader.bitsPerPixel < 8 )
- {
- ri.Error( ERR_DROP, "LoadBMP: monochrome and 4-bit BMP files not supported (%s)\n", name );
- }
-
- switch ( bmpHeader.bitsPerPixel )
- {
- case 8:
- case 16:
- case 24:
- case 32:
- break;
- default:
- ri.Error( ERR_DROP, "LoadBMP: illegal pixel_size '%hu' in file '%s'\n", bmpHeader.bitsPerPixel, name );
- break;
- }
-
- columns = bmpHeader.width;
- rows = bmpHeader.height;
- if ( rows < 0 )
- rows = -rows;
- numPixels = columns * rows;
-
- if(columns <= 0 || !rows || numPixels > 0x1FFFFFFF // 4*1FFFFFFF == 0x7FFFFFFC < 0x7FFFFFFF
- || ((numPixels * 4) / columns) / 4 != rows)
- {
- ri.Error (ERR_DROP, "LoadBMP: %s has an invalid image size\n", name);
- }
- if(buf_p + numPixels*bmpHeader.bitsPerPixel/8 > end)
- {
- ri.Error (ERR_DROP, "LoadBMP: file truncated (%s)\n", name);
- }
-
- if ( width )
- *width = columns;
- if ( height )
- *height = rows;
-
- bmpRGBA = ri.Malloc( numPixels * 4 );
- *pic = bmpRGBA;
-
-
- for ( row = rows-1; row >= 0; row-- )
- {
- pixbuf = bmpRGBA + row*columns*4;
-
- for ( column = 0; column < columns; column++ )
- {
- unsigned char red, green, blue, alpha;
- int palIndex;
- unsigned short shortPixel;
-
- switch ( bmpHeader.bitsPerPixel )
- {
- case 8:
- palIndex = *buf_p++;
- *pixbuf++ = bmpHeader.palette[palIndex][2];
- *pixbuf++ = bmpHeader.palette[palIndex][1];
- *pixbuf++ = bmpHeader.palette[palIndex][0];
- *pixbuf++ = 0xff;
- break;
- case 16:
- shortPixel = * ( unsigned short * ) pixbuf;
- pixbuf += 2;
- *pixbuf++ = ( shortPixel & ( 31 << 10 ) ) >> 7;
- *pixbuf++ = ( shortPixel & ( 31 << 5 ) ) >> 2;
- *pixbuf++ = ( shortPixel & ( 31 ) ) << 3;
- *pixbuf++ = 0xff;
- break;
-
- case 24:
- blue = *buf_p++;
- green = *buf_p++;
- red = *buf_p++;
- *pixbuf++ = red;
- *pixbuf++ = green;
- *pixbuf++ = blue;
- *pixbuf++ = 255;
- break;
- case 32:
- blue = *buf_p++;
- green = *buf_p++;
- red = *buf_p++;
- alpha = *buf_p++;
- *pixbuf++ = red;
- *pixbuf++ = green;
- *pixbuf++ = blue;
- *pixbuf++ = alpha;
- break;
- }
- }
- }
-
- ri.FS_FreeFile( buffer );
-
-}
-
-
-/*
-=================================================================
-
-PCX LOADING
-
-=================================================================
-*/
-
-
-/*
-==============
-LoadPCX
-==============
-*/
-static void LoadPCX ( const char *filename, byte **pic, byte **palette, int *width, int *height)
-{
- byte *raw;
- pcx_t *pcx;
- int x, y;
- int len;
- int dataByte, runLength;
- byte *out, *pix;
- unsigned xmax, ymax;
-
- *pic = NULL;
- *palette = NULL;
-
- //
- // load the file
- //
- len = ri.FS_ReadFile( ( char * ) filename, (void **)&raw);
- if (!raw) {
- return;
- }
-
- //
- // parse the PCX file
- //
- pcx = (pcx_t *)raw;
- raw = &pcx->data;
-
- xmax = LittleShort(pcx->xmax);
- ymax = LittleShort(pcx->ymax);
-
- if (pcx->manufacturer != 0x0a
- || pcx->version != 5
- || pcx->encoding != 1
- || pcx->bits_per_pixel != 8
- || xmax >= 1024
- || ymax >= 1024)
- {
- ri.Printf (PRINT_ALL, "Bad pcx file %s (%i x %i) (%i x %i)\n", filename, xmax+1, ymax+1, pcx->xmax, pcx->ymax);
- return;
- }
-
- out = ri.Malloc ( (ymax+1) * (xmax+1) );
-
- *pic = out;
-
- pix = out;
-
- if (palette)
- {
- *palette = ri.Malloc(768);
- Com_Memcpy (*palette, (byte *)pcx + len - 768, 768);
- }
-
- if (width)
- *width = xmax+1;
- if (height)
- *height = ymax+1;
-// FIXME: use bytes_per_line here?
-
- for (y=0 ; y<=ymax ; y++, pix += xmax+1)
- {
- for (x=0 ; x<=xmax ; )
- {
- dataByte = *raw++;
-
- if((dataByte & 0xC0) == 0xC0)
- {
- runLength = dataByte & 0x3F;
- dataByte = *raw++;
- }
- else
- runLength = 1;
-
- while(runLength-- > 0)
- pix[x++] = dataByte;
- }
-
- }
-
- if ( raw - (byte *)pcx > len)
- {
- ri.Printf (PRINT_DEVELOPER, "PCX file %s was malformed", filename);
- ri.Free (*pic);
- *pic = NULL;
- }
-
- ri.FS_FreeFile (pcx);
-}
-
-
-/*
-==============
-LoadPCX32
-==============
-*/
-static void LoadPCX32 ( const char *filename, byte **pic, int *width, int *height) {
- byte *palette;
- byte *pic8;
- int i, c, p;
- byte *pic32;
-
- LoadPCX (filename, &pic8, &palette, width, height);
- if (!pic8) {
- *pic = NULL;
- return;
- }
-
- // LoadPCX32 ensures width, height < 1024
- c = (*width) * (*height);
- pic32 = *pic = ri.Malloc(4 * c );
- for (i = 0 ; i < c ; i++) {
- p = pic8[i];
- pic32[0] = palette[p*3];
- pic32[1] = palette[p*3 + 1];
- pic32[2] = palette[p*3 + 2];
- pic32[3] = 255;
- pic32 += 4;
- }
-
- ri.Free (pic8);
- ri.Free (palette);
-}
-
-/*
-=========================================================
-
-TARGA LOADING
-
-=========================================================
-*/
-
-/*
-=============
-LoadTGA
-=============
-*/
-static void LoadTGA ( const char *name, byte **pic, int *width, int *height)
-{
- unsigned columns, rows, numPixels;
- byte *pixbuf;
- int row, column;
- byte *buf_p;
- byte *end;
- byte *buffer = NULL;
- TargaHeader targa_header;
- byte *targa_rgba;
- int length;
-
- *pic = NULL;
-
- if(width)
- *width = 0;
- if(height)
- *height = 0;
-
- //
- // load the file
- //
- length = ri.FS_ReadFile ( ( char * ) name, (void **)&buffer);
- if (!buffer || length < 0) {
- return;
- }
-
- if(length < 18)
- {
- ri.Error( ERR_DROP, "LoadTGA: header too short (%s)\n", name );
- }
-
- buf_p = buffer;
- end = buffer + length;
-
- targa_header.id_length = buf_p[0];
- targa_header.colormap_type = buf_p[1];
- targa_header.image_type = buf_p[2];
-
- memcpy(&targa_header.colormap_index, &buf_p[3], 2);
- memcpy(&targa_header.colormap_length, &buf_p[5], 2);
- targa_header.colormap_size = buf_p[7];
- memcpy(&targa_header.x_origin, &buf_p[8], 2);
- memcpy(&targa_header.y_origin, &buf_p[10], 2);
- memcpy(&targa_header.width, &buf_p[12], 2);
- memcpy(&targa_header.height, &buf_p[14], 2);
- targa_header.pixel_size = buf_p[16];
- targa_header.attributes = buf_p[17];
-
- targa_header.colormap_index = LittleShort(targa_header.colormap_index);
- targa_header.colormap_length = LittleShort(targa_header.colormap_length);
- targa_header.x_origin = LittleShort(targa_header.x_origin);
- targa_header.y_origin = LittleShort(targa_header.y_origin);
- targa_header.width = LittleShort(targa_header.width);
- targa_header.height = LittleShort(targa_header.height);
-
- buf_p += 18;
-
- if (targa_header.image_type!=2
- && targa_header.image_type!=10
- && targa_header.image_type != 3 )
- {
- ri.Error (ERR_DROP, "LoadTGA: Only type 2 (RGB), 3 (gray), and 10 (RGB) TGA images supported\n");
- }
-
- if ( targa_header.colormap_type != 0 )
- {
- ri.Error( ERR_DROP, "LoadTGA: colormaps not supported\n" );
- }
-
- if ( ( targa_header.pixel_size != 32 && targa_header.pixel_size != 24 ) && targa_header.image_type != 3 )
- {
- ri.Error (ERR_DROP, "LoadTGA: Only 32 or 24 bit images supported (no colormaps)\n");
- }
-
- columns = targa_header.width;
- rows = targa_header.height;
- numPixels = columns * rows * 4;
-
- if(!columns || !rows || numPixels > 0x7FFFFFFF || numPixels / columns / 4 != rows)
- {
- ri.Error (ERR_DROP, "LoadTGA: %s has an invalid image size\n", name);
- }
-
-
- targa_rgba = ri.Malloc (numPixels);
-
- if (targa_header.id_length != 0)
- {
- if (buf_p + targa_header.id_length > end)
- ri.Error( ERR_DROP, "LoadTGA: header too short (%s)\n", name );
-
- buf_p += targa_header.id_length; // skip TARGA image comment
- }
-
- if ( targa_header.image_type==2 || targa_header.image_type == 3 )
- {
- if(buf_p + columns*rows*targa_header.pixel_size/8 > end)
- {
- ri.Error (ERR_DROP, "LoadTGA: file truncated (%s)\n", name);
- }
-
- // Uncompressed RGB or gray scale image
- for(row=rows-1; row>=0; row--)
- {
- pixbuf = targa_rgba + row*columns*4;
- for(column=0; column<columns; column++)
- {
- unsigned char red,green,blue,alphabyte;
- switch (targa_header.pixel_size)
- {
-
- case 8:
- blue = *buf_p++;
- green = blue;
- red = blue;
- *pixbuf++ = red;
- *pixbuf++ = green;
- *pixbuf++ = blue;
- *pixbuf++ = 255;
- break;
-
- case 24:
- blue = *buf_p++;
- green = *buf_p++;
- red = *buf_p++;
- *pixbuf++ = red;
- *pixbuf++ = green;
- *pixbuf++ = blue;
- *pixbuf++ = 255;
- break;
- case 32:
- blue = *buf_p++;
- green = *buf_p++;
- red = *buf_p++;
- alphabyte = *buf_p++;
- *pixbuf++ = red;
- *pixbuf++ = green;
- *pixbuf++ = blue;
- *pixbuf++ = alphabyte;
- break;
- default:
- ri.Error( ERR_DROP, "LoadTGA: illegal pixel_size '%d' in file '%s'\n", targa_header.pixel_size, name );
- break;
- }
- }
- }
- }
- else if (targa_header.image_type==10) { // Runlength encoded RGB images
- unsigned char red,green,blue,alphabyte,packetHeader,packetSize,j;
-
- red = 0;
- green = 0;
- blue = 0;
- alphabyte = 0xff;
-
- for(row=rows-1; row>=0; row--) {
- pixbuf = targa_rgba + row*columns*4;
- for(column=0; column<columns; ) {
- if(buf_p + 1 > end)
- ri.Error (ERR_DROP, "LoadTGA: file truncated (%s)\n", name);
- packetHeader= *buf_p++;
- packetSize = 1 + (packetHeader & 0x7f);
- if (packetHeader & 0x80) { // run-length packet
- if(buf_p + targa_header.pixel_size/8 > end)
- ri.Error (ERR_DROP, "LoadTGA: file truncated (%s)\n", name);
- switch (targa_header.pixel_size) {
- case 24:
- blue = *buf_p++;
- green = *buf_p++;
- red = *buf_p++;
- alphabyte = 255;
- break;
- case 32:
- blue = *buf_p++;
- green = *buf_p++;
- red = *buf_p++;
- alphabyte = *buf_p++;
- break;
- default:
- ri.Error( ERR_DROP, "LoadTGA: illegal pixel_size '%d' in file '%s'\n", targa_header.pixel_size, name );
- break;
- }
-
- for(j=0;j<packetSize;j++) {
- *pixbuf++=red;
- *pixbuf++=green;
- *pixbuf++=blue;
- *pixbuf++=alphabyte;
- column++;
- if (column==columns) { // run spans across rows
- column=0;
- if (row>0)
- row--;
- else
- goto breakOut;
- pixbuf = targa_rgba + row*columns*4;
- }
- }
- }
- else { // non run-length packet
-
- if(buf_p + targa_header.pixel_size/8*packetSize > end)
- ri.Error (ERR_DROP, "LoadTGA: file truncated (%s)\n", name);
- for(j=0;j<packetSize;j++) {
- switch (targa_header.pixel_size) {
- case 24:
- blue = *buf_p++;
- green = *buf_p++;
- red = *buf_p++;
- *pixbuf++ = red;
- *pixbuf++ = green;
- *pixbuf++ = blue;
- *pixbuf++ = 255;
- break;
- case 32:
- blue = *buf_p++;
- green = *buf_p++;
- red = *buf_p++;
- alphabyte = *buf_p++;
- *pixbuf++ = red;
- *pixbuf++ = green;
- *pixbuf++ = blue;
- *pixbuf++ = alphabyte;
- break;
- default:
- ri.Error( ERR_DROP, "LoadTGA: illegal pixel_size '%d' in file '%s'\n", targa_header.pixel_size, name );
- break;
- }
- column++;
- if (column==columns) { // pixel packet run spans across rows
- column=0;
- if (row>0)
- row--;
- else
- goto breakOut;
- pixbuf = targa_rgba + row*columns*4;
- }
- }
- }
- }
- breakOut:;
- }
- }
-
-#if 0
- // TTimo: this is the chunk of code to ensure a behavior that meets TGA specs
- // bit 5 set => top-down
- if (targa_header.attributes & 0x20) {
- unsigned char *flip = (unsigned char*)malloc (columns*4);
- unsigned char *src, *dst;
-
- for (row = 0; row < rows/2; row++) {
- src = targa_rgba + row * 4 * columns;
- dst = targa_rgba + (rows - row - 1) * 4 * columns;
-
- memcpy (flip, src, columns*4);
- memcpy (src, dst, columns*4);
- memcpy (dst, flip, columns*4);
- }
- free (flip);
- }
-#endif
- // instead we just print a warning
- if (targa_header.attributes & 0x20) {
- ri.Printf( PRINT_WARNING, "WARNING: '%s' TGA file header declares top-down image, ignoring\n", name);
- }
-
- if (width)
- *width = columns;
- if (height)
- *height = rows;
-
- *pic = targa_rgba;
-
- ri.FS_FreeFile (buffer);
-}
-static void LoadJPG( const char *filename, unsigned char **pic, int *width, int *height ) {
- /* This struct contains the JPEG decompression parameters and pointers to
- * working space (which is allocated as needed by the JPEG library).
- */
- struct jpeg_decompress_struct cinfo = {NULL};
- /* We use our private extension JPEG error handler.
- * Note that this struct must live as long as the main JPEG parameter
- * struct, to avoid dangling-pointer problems.
- */
- /* This struct represents a JPEG error handler. It is declared separately
- * because applications often want to supply a specialized error handler
- * (see the second half of this file for an example). But here we just
- * take the easy way out and use the standard error handler, which will
- * print a message on stderr and call exit() if compression fails.
- * Note that this struct must live as long as the main JPEG parameter
- * struct, to avoid dangling-pointer problems.
- */
- struct jpeg_error_mgr jerr;
- /* More stuff */
- JSAMPARRAY buffer; /* Output row buffer */
- unsigned row_stride; /* physical row width in output buffer */
- unsigned pixelcount, memcount;
- unsigned char *out;
- byte *fbuffer;
- byte *buf;
-
- /* In this example we want to open the input file before doing anything else,
- * so that the setjmp() error recovery below can assume the file is open.
- * VERY IMPORTANT: use "b" option to fopen() if you are on a machine that
- * requires it in order to read binary files.
- */
-
- ri.FS_ReadFile ( ( char * ) filename, (void **)&fbuffer);
- if (!fbuffer) {
- return;
- }
-
- /* Step 1: allocate and initialize JPEG decompression object */
-
- /* We have to set up the error handler first, in case the initialization
- * step fails. (Unlikely, but it could happen if you are out of memory.)
- * This routine fills in the contents of struct jerr, and returns jerr's
- * address which we place into the link field in cinfo.
- */
- cinfo.err = jpeg_std_error(&jerr);
-
- /* Now we can initialize the JPEG decompression object. */
- jpeg_create_decompress(&cinfo);
-
- /* Step 2: specify data source (eg, a file) */
-
- jpeg_stdio_src(&cinfo, fbuffer);
-
- /* Step 3: read file parameters with jpeg_read_header() */
-
- (void) jpeg_read_header(&cinfo, TRUE);
- /* We can ignore the return value from jpeg_read_header since
- * (a) suspension is not possible with the stdio data source, and
- * (b) we passed TRUE to reject a tables-only JPEG file as an error.
- * See libjpeg.doc for more info.
- */
-
- /* Step 4: set parameters for decompression */
-
- /* In this example, we don't need to change any of the defaults set by
- * jpeg_read_header(), so we do nothing here.
- */
-
- /* Step 5: Start decompressor */
-
- (void) jpeg_start_decompress(&cinfo);
- /* We can ignore the return value since suspension is not possible
- * with the stdio data source.
- */
-
- /* We may need to do some setup of our own at this point before reading
- * the data. After jpeg_start_decompress() we have the correct scaled
- * output image dimensions available, as well as the output colormap
- * if we asked for color quantization.
- * In this example, we need to make an output work buffer of the right size.
- */
- /* JSAMPLEs per row in output buffer */
-
- pixelcount = cinfo.output_width * cinfo.output_height;
-
- if(!cinfo.output_width || !cinfo.output_height
- || ((pixelcount * 4) / cinfo.output_width) / 4 != cinfo.output_height
- || pixelcount > 0x1FFFFFFF || cinfo.output_components > 4) // 4*1FFFFFFF == 0x7FFFFFFC < 0x7FFFFFFF
- {
- ri.Error (ERR_DROP, "LoadJPG: %s has an invalid image size: %dx%d*4=%d, components: %d\n", filename,
- cinfo.output_width, cinfo.output_height, pixelcount * 4, cinfo.output_components);
- }
-
- memcount = pixelcount * 4;
- row_stride = cinfo.output_width * cinfo.output_components;
-
- out = ri.Malloc(memcount);
-
- *width = cinfo.output_width;
- *height = cinfo.output_height;
-
- /* Step 6: while (scan lines remain to be read) */
- /* jpeg_read_scanlines(...); */
-
- /* Here we use the library's state variable cinfo.output_scanline as the
- * loop counter, so that we don't have to keep track ourselves.
- */
- while (cinfo.output_scanline < cinfo.output_height) {
- /* jpeg_read_scanlines expects an array of pointers to scanlines.
- * Here the array is only one element long, but you could ask for
- * more than one scanline at a time if that's more convenient.
- */
- buf = ((out+(row_stride*cinfo.output_scanline)));
- buffer = &buf;
- (void) jpeg_read_scanlines(&cinfo, buffer, 1);
- }
-
- buf = out;
-
- // If we are processing an 8-bit JPEG (greyscale), we'll have to convert
- // the greyscale values to RGBA.
- if(cinfo.output_components == 1)
- {
- int sindex = pixelcount, dindex = memcount;
- unsigned char greyshade;
-
- // Only pixelcount number of bytes have been written.
- // Expand the color values over the rest of the buffer, starting
- // from the end.
- do
- {
- greyshade = buf[--sindex];
-
- buf[--dindex] = 255;
- buf[--dindex] = greyshade;
- buf[--dindex] = greyshade;
- buf[--dindex] = greyshade;
- } while(sindex);
- }
- else
- {
- // clear all the alphas to 255
- int i;
-
- for ( i = 3 ; i < memcount ; i+=4 )
- {
- buf[i] = 255;
- }
- }
-
- *pic = out;
-
- /* Step 7: Finish decompression */
-
- (void) jpeg_finish_decompress(&cinfo);
- /* We can ignore the return value since suspension is not possible
- * with the stdio data source.
- */
-
- /* Step 8: Release JPEG decompression object */
-
- /* This is an important step since it will release a good deal of memory. */
- jpeg_destroy_decompress(&cinfo);
-
- /* After finish_decompress, we can close the input file.
- * Here we postpone it until after no more JPEG errors are possible,
- * so as to simplify the setjmp error logic above. (Actually, I don't
- * think that jpeg_destroy can do an error exit, but why assume anything...)
- */
- ri.FS_FreeFile (fbuffer);
-
- /* At this point you may want to check to see whether any corrupt-data
- * warnings occurred (test whether jerr.pub.num_warnings is nonzero).
- */
-
- /* And we're done! */
-}
-
-
-/* Expanded data destination object for stdio output */
-
-typedef struct {
- struct jpeg_destination_mgr pub; /* public fields */
-
- byte* outfile; /* target stream */
- int size;
-} my_destination_mgr;
-
-typedef my_destination_mgr * my_dest_ptr;
-
-
-/*
- * Initialize destination --- called by jpeg_start_compress
- * before any data is actually written.
- */
-
-void init_destination (j_compress_ptr cinfo)
-{
- my_dest_ptr dest = (my_dest_ptr) cinfo->dest;
-
- dest->pub.next_output_byte = dest->outfile;
- dest->pub.free_in_buffer = dest->size;
-}
-
-
-/*
- * Empty the output buffer --- called whenever buffer fills up.
- *
- * In typical applications, this should write the entire output buffer
- * (ignoring the current state of next_output_byte & free_in_buffer),
- * reset the pointer & count to the start of the buffer, and return TRUE
- * indicating that the buffer has been dumped.
- *
- * In applications that need to be able to suspend compression due to output
- * overrun, a FALSE return indicates that the buffer cannot be emptied now.
- * In this situation, the compressor will return to its caller (possibly with
- * an indication that it has not accepted all the supplied scanlines). The
- * application should resume compression after it has made more room in the
- * output buffer. Note that there are substantial restrictions on the use of
- * suspension --- see the documentation.
- *
- * When suspending, the compressor will back up to a convenient restart point
- * (typically the start of the current MCU). next_output_byte & free_in_buffer
- * indicate where the restart point will be if the current call returns FALSE.
- * Data beyond this point will be regenerated after resumption, so do not
- * write it out when emptying the buffer externally.
- */
-
-boolean empty_output_buffer (j_compress_ptr cinfo)
-{
- return TRUE;
-}
-
-
-/*
- * Compression initialization.
- * Before calling this, all parameters and a data destination must be set up.
- *
- * We require a write_all_tables parameter as a failsafe check when writing
- * multiple datastreams from the same compression object. Since prior runs
- * will have left all the tables marked sent_table=TRUE, a subsequent run
- * would emit an abbreviated stream (no tables) by default. This may be what
- * is wanted, but for safety's sake it should not be the default behavior:
- * programmers should have to make a deliberate choice to emit abbreviated
- * images. Therefore the documentation and examples should encourage people
- * to pass write_all_tables=TRUE; then it will take active thought to do the
- * wrong thing.
- */
-
-GLOBAL void
-jpeg_start_compress (j_compress_ptr cinfo, boolean write_all_tables)
-{
- if (cinfo->global_state != CSTATE_START)
- ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state);
-
- if (write_all_tables)
- jpeg_suppress_tables(cinfo, FALSE); /* mark all tables to be written */
-
- /* (Re)initialize error mgr and destination modules */
- (*cinfo->err->reset_error_mgr) ((j_common_ptr) cinfo);
- (*cinfo->dest->init_destination) (cinfo);
- /* Perform master selection of active modules */
- jinit_compress_master(cinfo);
- /* Set up for the first pass */
- (*cinfo->master->prepare_for_pass) (cinfo);
- /* Ready for application to drive first pass through jpeg_write_scanlines
- * or jpeg_write_raw_data.
- */
- cinfo->next_scanline = 0;
- cinfo->global_state = (cinfo->raw_data_in ? CSTATE_RAW_OK : CSTATE_SCANNING);
-}
-
-
-/*
- * Write some scanlines of data to the JPEG compressor.
- *
- * The return value will be the number of lines actually written.
- * This should be less than the supplied num_lines only in case that
- * the data destination module has requested suspension of the compressor,
- * or if more than image_height scanlines are passed in.
- *
- * Note: we warn about excess calls to jpeg_write_scanlines() since
- * this likely signals an application programmer error. However,
- * excess scanlines passed in the last valid call are *silently* ignored,
- * so that the application need not adjust num_lines for end-of-image
- * when using a multiple-scanline buffer.
- */
-
-GLOBAL JDIMENSION
-jpeg_write_scanlines (j_compress_ptr cinfo, JSAMPARRAY scanlines,
- JDIMENSION num_lines)
-{
- JDIMENSION row_ctr, rows_left;
-
- if (cinfo->global_state != CSTATE_SCANNING)
- ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state);
- if (cinfo->next_scanline >= cinfo->image_height)
- WARNMS(cinfo, JWRN_TOO_MUCH_DATA);
-
- /* Call progress monitor hook if present */
- if (cinfo->progress != NULL) {
- cinfo->progress->pass_counter = (long) cinfo->next_scanline;
- cinfo->progress->pass_limit = (long) cinfo->image_height;
- (*cinfo->progress->progress_monitor) ((j_common_ptr) cinfo);
- }
-
- /* Give master control module another chance if this is first call to
- * jpeg_write_scanlines. This lets output of the frame/scan headers be
- * delayed so that application can write COM, etc, markers between
- * jpeg_start_compress and jpeg_write_scanlines.
- */
- if (cinfo->master->call_pass_startup)
- (*cinfo->master->pass_startup) (cinfo);
-
- /* Ignore any extra scanlines at bottom of image. */
- rows_left = cinfo->image_height - cinfo->next_scanline;
- if (num_lines > rows_left)
- num_lines = rows_left;
-
- row_ctr = 0;
- (*cinfo->main->process_data) (cinfo, scanlines, &row_ctr, num_lines);
- cinfo->next_scanline += row_ctr;
- return row_ctr;
-}
-
-/*
- * Terminate destination --- called by jpeg_finish_compress
- * after all data has been written. Usually needs to flush buffer.
- *
- * NB: *not* called by jpeg_abort or jpeg_destroy; surrounding
- * application must deal with any cleanup that should happen even
- * for error exit.
- */
-
-static int hackSize;
-
-void term_destination (j_compress_ptr cinfo)
-{
- my_dest_ptr dest = (my_dest_ptr) cinfo->dest;
- size_t datacount = dest->size - dest->pub.free_in_buffer;
- hackSize = datacount;
-}
-
-
-/*
- * Prepare for output to a stdio stream.
- * The caller must have already opened the stream, and is responsible
- * for closing it after finishing compression.
- */
-
-void jpegDest (j_compress_ptr cinfo, byte* outfile, int size)
-{
- my_dest_ptr dest;
-
- /* The destination object is made permanent so that multiple JPEG images
- * can be written to the same file without re-executing jpeg_stdio_dest.
- * This makes it dangerous to use this manager and a different destination
- * manager serially with the same JPEG object, because their private object
- * sizes may be different. Caveat programmer.
- */
- if (cinfo->dest == NULL) { /* first time for this JPEG object? */
- cinfo->dest = (struct jpeg_destination_mgr *)
- (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT,
- sizeof(my_destination_mgr));
- }
-
- dest = (my_dest_ptr) cinfo->dest;
- dest->pub.init_destination = init_destination;
- dest->pub.empty_output_buffer = empty_output_buffer;
- dest->pub.term_destination = term_destination;
- dest->outfile = outfile;
- dest->size = size;
-}
-
-void SaveJPG(char * filename, int quality, int image_width, int image_height, unsigned char *image_buffer) {
- /* This struct contains the JPEG compression parameters and pointers to
- * working space (which is allocated as needed by the JPEG library).
- * It is possible to have several such structures, representing multiple
- * compression/decompression processes, in existence at once. We refer
- * to any one struct (and its associated working data) as a "JPEG object".
- */
- struct jpeg_compress_struct cinfo;
- /* This struct represents a JPEG error handler. It is declared separately
- * because applications often want to supply a specialized error handler
- * (see the second half of this file for an example). But here we just
- * take the easy way out and use the standard error handler, which will
- * print a message on stderr and call exit() if compression fails.
- * Note that this struct must live as long as the main JPEG parameter
- * struct, to avoid dangling-pointer problems.
- */
- struct jpeg_error_mgr jerr;
- /* More stuff */
- JSAMPROW row_pointer[1]; /* pointer to JSAMPLE row[s] */
- int row_stride; /* physical row width in image buffer */
- unsigned char *out;
-
- /* Step 1: allocate and initialize JPEG compression object */
-
- /* We have to set up the error handler first, in case the initialization
- * step fails. (Unlikely, but it could happen if you are out of memory.)
- * This routine fills in the contents of struct jerr, and returns jerr's
- * address which we place into the link field in cinfo.
- */
- cinfo.err = jpeg_std_error(&jerr);
- /* Now we can initialize the JPEG compression object. */
- jpeg_create_compress(&cinfo);
-
- /* Step 2: specify data destination (eg, a file) */
- /* Note: steps 2 and 3 can be done in either order. */
-
- /* Here we use the library-supplied code to send compressed data to a
- * stdio stream. You can also write your own code to do something else.
- * VERY IMPORTANT: use "b" option to fopen() if you are on a machine that
- * requires it in order to write binary files.
- */
- out = ri.Hunk_AllocateTempMemory(image_width*image_height*4);
- jpegDest(&cinfo, out, image_width*image_height*4);
-
- /* Step 3: set parameters for compression */
-
- /* First we supply a description of the input image.
- * Four fields of the cinfo struct must be filled in:
- */
- cinfo.image_width = image_width; /* image width and height, in pixels */
- cinfo.image_height = image_height;
- cinfo.input_components = 4; /* # of color components per pixel */
- cinfo.in_color_space = JCS_RGB; /* colorspace of input image */
- /* Now use the library's routine to set default compression parameters.
- * (You must set at least cinfo.in_color_space before calling this,
- * since the defaults depend on the source color space.)
- */
- jpeg_set_defaults(&cinfo);
- /* Now you can set any non-default parameters you wish to.
- * Here we just illustrate the use of quality (quantization table) scaling:
- */
- jpeg_set_quality(&cinfo, quality, TRUE /* limit to baseline-JPEG values */);
- /* If quality is set high, disable chroma subsampling */
- if (quality >= 85) {
- cinfo.comp_info[0].h_samp_factor = 1;
- cinfo.comp_info[0].v_samp_factor = 1;
- }
-
- /* Step 4: Start compressor */
-
- /* TRUE ensures that we will write a complete interchange-JPEG file.
- * Pass TRUE unless you are very sure of what you're doing.
- */
- jpeg_start_compress(&cinfo, TRUE);
-
- /* Step 5: while (scan lines remain to be written) */
- /* jpeg_write_scanlines(...); */
-
- /* Here we use the library's state variable cinfo.next_scanline as the
- * loop counter, so that we don't have to keep track ourselves.
- * To keep things simple, we pass one scanline per call; you can pass
- * more if you wish, though.
- */
- row_stride = image_width * 4; /* JSAMPLEs per row in image_buffer */
-
- while (cinfo.next_scanline < cinfo.image_height) {
- /* jpeg_write_scanlines expects an array of pointers to scanlines.
- * Here the array is only one element long, but you could pass
- * more than one scanline at a time if that's more convenient.
- */
- row_pointer[0] = & image_buffer[((cinfo.image_height-1)*row_stride)-cinfo.next_scanline * row_stride];
- (void) jpeg_write_scanlines(&cinfo, row_pointer, 1);
- }
-
- /* Step 6: Finish compression */
-
- jpeg_finish_compress(&cinfo);
- /* After finish_compress, we can close the output file. */
- ri.FS_WriteFile( filename, out, hackSize );
-
- ri.Hunk_FreeTempMemory(out);
-
- /* Step 7: release JPEG compression object */
-
- /* This is an important step since it will release a good deal of memory. */
- jpeg_destroy_compress(&cinfo);
-
- /* And we're done! */
-}
-
-/*
-=================
-SaveJPGToBuffer
-=================
-*/
-int SaveJPGToBuffer( byte *buffer, int quality,
- int image_width, int image_height,
- byte *image_buffer )
-{
- struct jpeg_compress_struct cinfo;
- struct jpeg_error_mgr jerr;
- JSAMPROW row_pointer[1]; /* pointer to JSAMPLE row[s] */
- int row_stride; /* physical row width in image buffer */
-
- /* Step 1: allocate and initialize JPEG compression object */
- cinfo.err = jpeg_std_error(&jerr);
- /* Now we can initialize the JPEG compression object. */
- jpeg_create_compress(&cinfo);
-
- /* Step 2: specify data destination (eg, a file) */
- /* Note: steps 2 and 3 can be done in either order. */
- jpegDest(&cinfo, buffer, image_width*image_height*4);
-
- /* Step 3: set parameters for compression */
- cinfo.image_width = image_width; /* image width and height, in pixels */
- cinfo.image_height = image_height;
- cinfo.input_components = 4; /* # of color components per pixel */
- cinfo.in_color_space = JCS_RGB; /* colorspace of input image */
-
- jpeg_set_defaults(&cinfo);
- jpeg_set_quality(&cinfo, quality, TRUE /* limit to baseline-JPEG values */);
- /* If quality is set high, disable chroma subsampling */
- if (quality >= 85) {
- cinfo.comp_info[0].h_samp_factor = 1;
- cinfo.comp_info[0].v_samp_factor = 1;
- }
-
- /* Step 4: Start compressor */
- jpeg_start_compress(&cinfo, TRUE);
-
- /* Step 5: while (scan lines remain to be written) */
- /* jpeg_write_scanlines(...); */
- row_stride = image_width * 4; /* JSAMPLEs per row in image_buffer */
-
- while (cinfo.next_scanline < cinfo.image_height) {
- /* jpeg_write_scanlines expects an array of pointers to scanlines.
- * Here the array is only one element long, but you could pass
- * more than one scanline at a time if that's more convenient.
- */
- row_pointer[0] = & image_buffer[((cinfo.image_height-1)*row_stride)-cinfo.next_scanline * row_stride];
- (void) jpeg_write_scanlines(&cinfo, row_pointer, 1);
- }
-
- /* Step 6: Finish compression */
- jpeg_finish_compress(&cinfo);
-
- /* Step 7: release JPEG compression object */
- jpeg_destroy_compress(&cinfo);
-
- /* And we're done! */
- return hackSize;
-}
-
//===================================================================
typedef struct
@@ -2011,7 +805,7 @@ static imageExtToLoaderMap_t imageLoaders[ ] =
{ "jpg", LoadJPG },
{ "jpeg", LoadJPG },
{ "png", LoadPNG },
- { "pcx", LoadPCX32 },
+ { "pcx", LoadPCX },
{ "bmp", LoadBMP }
};
diff --git a/code/renderer/tr_image_bmp.c b/code/renderer/tr_image_bmp.c
new file mode 100644
index 0000000..191843f
--- /dev/null
+++ b/code/renderer/tr_image_bmp.c
@@ -0,0 +1,236 @@
+/*
+===========================================================================
+Copyright (C) 1999-2005 Id Software, Inc.
+
+This file is part of Quake III Arena source code.
+
+Quake III Arena 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 III Arena 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 III Arena source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+
+#include "tr_local.h"
+
+typedef struct
+{
+ char id[2];
+ unsigned fileSize;
+ unsigned reserved0;
+ unsigned bitmapDataOffset;
+ unsigned bitmapHeaderSize;
+ unsigned width;
+ unsigned height;
+ unsigned short planes;
+ unsigned short bitsPerPixel;
+ unsigned compression;
+ unsigned bitmapDataSize;
+ unsigned hRes;
+ unsigned vRes;
+ unsigned colors;
+ unsigned importantColors;
+ unsigned char palette[256][4];
+} BMPHeader_t;
+
+void LoadBMP( const char *name, byte **pic, int *width, int *height )
+{
+ int columns, rows;
+ unsigned numPixels;
+ byte *pixbuf;
+ int row, column;
+ byte *buf_p;
+ byte *end;
+ byte *buffer = NULL;
+ int length;
+ BMPHeader_t bmpHeader;
+ byte *bmpRGBA;
+
+ *pic = NULL;
+
+ if(width)
+ *width = 0;
+
+ if(height)
+ *height = 0;
+
+ //
+ // load the file
+ //
+ length = ri.FS_ReadFile( ( char * ) name, (void **)&buffer);
+ if (!buffer || length < 0) {
+ return;
+ }
+
+ if (length < 54)
+ {
+ ri.Error( ERR_DROP, "LoadBMP: header too short (%s)\n", name );
+ }
+
+ buf_p = buffer;
+ end = buffer + length;
+
+ bmpHeader.id[0] = *buf_p++;
+ bmpHeader.id[1] = *buf_p++;
+ bmpHeader.fileSize = LittleLong( * ( int * ) buf_p );
+ buf_p += 4;
+ bmpHeader.reserved0 = LittleLong( * ( int * ) buf_p );
+ buf_p += 4;
+ bmpHeader.bitmapDataOffset = LittleLong( * ( int * ) buf_p );
+ buf_p += 4;
+ bmpHeader.bitmapHeaderSize = LittleLong( * ( int * ) buf_p );
+ buf_p += 4;
+ bmpHeader.width = LittleLong( * ( int * ) buf_p );
+ buf_p += 4;
+ bmpHeader.height = LittleLong( * ( int * ) buf_p );
+ buf_p += 4;
+ bmpHeader.planes = LittleShort( * ( short * ) buf_p );
+ buf_p += 2;
+ bmpHeader.bitsPerPixel = LittleShort( * ( short * ) buf_p );
+ buf_p += 2;
+ bmpHeader.compression = LittleLong( * ( int * ) buf_p );
+ buf_p += 4;
+ bmpHeader.bitmapDataSize = LittleLong( * ( int * ) buf_p );
+ buf_p += 4;
+ bmpHeader.hRes = LittleLong( * ( int * ) buf_p );
+ buf_p += 4;
+ bmpHeader.vRes = LittleLong( * ( int * ) buf_p );
+ buf_p += 4;
+ bmpHeader.colors = LittleLong( * ( int * ) buf_p );
+ buf_p += 4;
+ bmpHeader.importantColors = LittleLong( * ( int * ) buf_p );
+ buf_p += 4;
+
+ if ( bmpHeader.bitsPerPixel == 8 )
+ {
+ if (buf_p + sizeof(bmpHeader.palette) > end)
+ ri.Error( ERR_DROP, "LoadBMP: header too short (%s)\n", name );
+
+ Com_Memcpy( bmpHeader.palette, buf_p, sizeof( bmpHeader.palette ) );
+ buf_p += sizeof(bmpHeader.palette);
+ }
+
+ if (buffer + bmpHeader.bitmapDataOffset > end)
+ {
+ ri.Error( ERR_DROP, "LoadBMP: invalid offset value in header (%s)\n", name );
+ }
+
+ buf_p = buffer + bmpHeader.bitmapDataOffset;
+
+ if ( bmpHeader.id[0] != 'B' && bmpHeader.id[1] != 'M' )
+ {
+ ri.Error( ERR_DROP, "LoadBMP: only Windows-style BMP files supported (%s)\n", name );
+ }
+ if ( bmpHeader.fileSize != length )
+ {
+ ri.Error( ERR_DROP, "LoadBMP: header size does not match file size (%u vs. %u) (%s)\n", bmpHeader.fileSize, length, name );
+ }
+ if ( bmpHeader.compression != 0 )
+ {
+ ri.Error( ERR_DROP, "LoadBMP: only uncompressed BMP files supported (%s)\n", name );
+ }
+ if ( bmpHeader.bitsPerPixel < 8 )
+ {
+ ri.Error( ERR_DROP, "LoadBMP: monochrome and 4-bit BMP files not supported (%s)\n", name );
+ }
+
+ switch ( bmpHeader.bitsPerPixel )
+ {
+ case 8:
+ case 16:
+ case 24:
+ case 32:
+ break;
+ default:
+ ri.Error( ERR_DROP, "LoadBMP: illegal pixel_size '%hu' in file '%s'\n", bmpHeader.bitsPerPixel, name );
+ break;
+ }
+
+ columns = bmpHeader.width;
+ rows = bmpHeader.height;
+ if ( rows < 0 )
+ rows = -rows;
+ numPixels = columns * rows;
+
+ if(columns <= 0 || !rows || numPixels > 0x1FFFFFFF // 4*1FFFFFFF == 0x7FFFFFFC < 0x7FFFFFFF
+ || ((numPixels * 4) / columns) / 4 != rows)
+ {
+ ri.Error (ERR_DROP, "LoadBMP: %s has an invalid image size\n", name);
+ }
+ if(buf_p + numPixels*bmpHeader.bitsPerPixel/8 > end)
+ {
+ ri.Error (ERR_DROP, "LoadBMP: file truncated (%s)\n", name);
+ }
+
+ if ( width )
+ *width = columns;
+ if ( height )
+ *height = rows;
+
+ bmpRGBA = ri.Malloc( numPixels * 4 );
+ *pic = bmpRGBA;
+
+
+ for ( row = rows-1; row >= 0; row-- )
+ {
+ pixbuf = bmpRGBA + row*columns*4;
+
+ for ( column = 0; column < columns; column++ )
+ {
+ unsigned char red, green, blue, alpha;
+ int palIndex;
+ unsigned short shortPixel;
+
+ switch ( bmpHeader.bitsPerPixel )
+ {
+ case 8:
+ palIndex = *buf_p++;
+ *pixbuf++ = bmpHeader.palette[palIndex][2];
+ *pixbuf++ = bmpHeader.palette[palIndex][1];
+ *pixbuf++ = bmpHeader.palette[palIndex][0];
+ *pixbuf++ = 0xff;
+ break;
+ case 16:
+ shortPixel = * ( unsigned short * ) pixbuf;
+ pixbuf += 2;
+ *pixbuf++ = ( shortPixel & ( 31 << 10 ) ) >> 7;
+ *pixbuf++ = ( shortPixel & ( 31 << 5 ) ) >> 2;
+ *pixbuf++ = ( shortPixel & ( 31 ) ) << 3;
+ *pixbuf++ = 0xff;
+ break;
+
+ case 24:
+ blue = *buf_p++;
+ green = *buf_p++;
+ red = *buf_p++;
+ *pixbuf++ = red;
+ *pixbuf++ = green;
+ *pixbuf++ = blue;
+ *pixbuf++ = 255;
+ break;
+ case 32:
+ blue = *buf_p++;
+ green = *buf_p++;
+ red = *buf_p++;
+ alpha = *buf_p++;
+ *pixbuf++ = red;
+ *pixbuf++ = green;
+ *pixbuf++ = blue;
+ *pixbuf++ = alpha;
+ break;
+ }
+ }
+ }
+
+ ri.FS_FreeFile( buffer );
+
+}
diff --git a/code/renderer/tr_image_bmp.h b/code/renderer/tr_image_bmp.h
new file mode 100644
index 0000000..98b16b9
--- /dev/null
+++ b/code/renderer/tr_image_bmp.h
@@ -0,0 +1 @@
+void LoadBMP( const char *name, byte **pic, int *width, int *height );
diff --git a/code/renderer/tr_image_jpg.c b/code/renderer/tr_image_jpg.c
new file mode 100644
index 0000000..cae7ccb
--- /dev/null
+++ b/code/renderer/tr_image_jpg.c
@@ -0,0 +1,581 @@
+/*
+===========================================================================
+Copyright (C) 1999-2005 Id Software, Inc.
+
+This file is part of Quake III Arena source code.
+
+Quake III Arena 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 III Arena 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 III Arena source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+
+#include "tr_local.h"
+
+/*
+ * Include file for users of JPEG library.
+ * You will need to have included system headers that define at least
+ * the typedefs FILE and size_t before you can include jpeglib.h.
+ * (stdio.h is sufficient on ANSI-conforming systems.)
+ * You may also wish to include "jerror.h".
+ */
+
+#define JPEG_INTERNALS
+#include "../jpeg-6/jpeglib.h"
+
+void LoadJPG( const char *filename, unsigned char **pic, int *width, int *height ) {
+ /* This struct contains the JPEG decompression parameters and pointers to
+ * working space (which is allocated as needed by the JPEG library).
+ */
+ struct jpeg_decompress_struct cinfo = {NULL};
+ /* We use our private extension JPEG error handler.
+ * Note that this struct must live as long as the main JPEG parameter
+ * struct, to avoid dangling-pointer problems.
+ */
+ /* This struct represents a JPEG error handler. It is declared separately
+ * because applications often want to supply a specialized error handler
+ * (see the second half of this file for an example). But here we just
+ * take the easy way out and use the standard error handler, which will
+ * print a message on stderr and call exit() if compression fails.
+ * Note that this struct must live as long as the main JPEG parameter
+ * struct, to avoid dangling-pointer problems.
+ */
+ struct jpeg_error_mgr jerr;
+ /* More stuff */
+ JSAMPARRAY buffer; /* Output row buffer */
+ unsigned row_stride; /* physical row width in output buffer */
+ unsigned pixelcount, memcount;
+ unsigned char *out;
+ byte *fbuffer;
+ byte *buf;
+
+ /* In this example we want to open the input file before doing anything else,
+ * so that the setjmp() error recovery below can assume the file is open.
+ * VERY IMPORTANT: use "b" option to fopen() if you are on a machine that
+ * requires it in order to read binary files.
+ */
+
+ ri.FS_ReadFile ( ( char * ) filename, (void **)&fbuffer);
+ if (!fbuffer) {
+ return;
+ }
+
+ /* Step 1: allocate and initialize JPEG decompression object */
+
+ /* We have to set up the error handler first, in case the initialization
+ * step fails. (Unlikely, but it could happen if you are out of memory.)
+ * This routine fills in the contents of struct jerr, and returns jerr's
+ * address which we place into the link field in cinfo.
+ */
+ cinfo.err = jpeg_std_error(&jerr);
+
+ /* Now we can initialize the JPEG decompression object. */
+ jpeg_create_decompress(&cinfo);
+
+ /* Step 2: specify data source (eg, a file) */
+
+ jpeg_stdio_src(&cinfo, fbuffer);
+
+ /* Step 3: read file parameters with jpeg_read_header() */
+
+ (void) jpeg_read_header(&cinfo, TRUE);
+ /* We can ignore the return value from jpeg_read_header since
+ * (a) suspension is not possible with the stdio data source, and
+ * (b) we passed TRUE to reject a tables-only JPEG file as an error.
+ * See libjpeg.doc for more info.
+ */
+
+ /* Step 4: set parameters for decompression */
+
+ /* In this example, we don't need to change any of the defaults set by
+ * jpeg_read_header(), so we do nothing here.
+ */
+
+ /* Step 5: Start decompressor */
+
+ (void) jpeg_start_decompress(&cinfo);
+ /* We can ignore the return value since suspension is not possible
+ * with the stdio data source.
+ */
+
+ /* We may need to do some setup of our own at this point before reading
+ * the data. After jpeg_start_decompress() we have the correct scaled
+ * output image dimensions available, as well as the output colormap
+ * if we asked for color quantization.
+ * In this example, we need to make an output work buffer of the right size.
+ */
+ /* JSAMPLEs per row in output buffer */
+
+ pixelcount = cinfo.output_width * cinfo.output_height;
+
+ if(!cinfo.output_width || !cinfo.output_height
+ || ((pixelcount * 4) / cinfo.output_width) / 4 != cinfo.output_height
+ || pixelcount > 0x1FFFFFFF || cinfo.output_components > 4) // 4*1FFFFFFF == 0x7FFFFFFC < 0x7FFFFFFF
+ {
+ ri.Error (ERR_DROP, "LoadJPG: %s has an invalid image size: %dx%d*4=%d, components: %d\n", filename,
+ cinfo.output_width, cinfo.output_height, pixelcount * 4, cinfo.output_components);
+ }
+
+ memcount = pixelcount * 4;
+ row_stride = cinfo.output_width * cinfo.output_components;
+
+ out = ri.Malloc(memcount);
+
+ *width = cinfo.output_width;
+ *height = cinfo.output_height;
+
+ /* Step 6: while (scan lines remain to be read) */
+ /* jpeg_read_scanlines(...); */
+
+ /* Here we use the library's state variable cinfo.output_scanline as the
+ * loop counter, so that we don't have to keep track ourselves.
+ */
+ while (cinfo.output_scanline < cinfo.output_height) {
+ /* jpeg_read_scanlines expects an array of pointers to scanlines.
+ * Here the array is only one element long, but you could ask for
+ * more than one scanline at a time if that's more convenient.
+ */
+ buf = ((out+(row_stride*cinfo.output_scanline)));
+ buffer = &buf;
+ (void) jpeg_read_scanlines(&cinfo, buffer, 1);
+ }
+
+ buf = out;
+
+ // If we are processing an 8-bit JPEG (greyscale), we'll have to convert
+ // the greyscale values to RGBA.
+ if(cinfo.output_components == 1)
+ {
+ int sindex = pixelcount, dindex = memcount;
+ unsigned char greyshade;
+
+ // Only pixelcount number of bytes have been written.
+ // Expand the color values over the rest of the buffer, starting
+ // from the end.
+ do
+ {
+ greyshade = buf[--sindex];
+
+ buf[--dindex] = 255;
+ buf[--dindex] = greyshade;
+ buf[--dindex] = greyshade;
+ buf[--dindex] = greyshade;
+ } while(sindex);
+ }
+ else
+ {
+ // clear all the alphas to 255
+ int i;
+
+ for ( i = 3 ; i < memcount ; i+=4 )
+ {
+ buf[i] = 255;
+ }
+ }
+
+ *pic = out;
+
+ /* Step 7: Finish decompression */
+
+ (void) jpeg_finish_decompress(&cinfo);
+ /* We can ignore the return value since suspension is not possible
+ * with the stdio data source.
+ */
+
+ /* Step 8: Release JPEG decompression object */
+
+ /* This is an important step since it will release a good deal of memory. */
+ jpeg_destroy_decompress(&cinfo);
+
+ /* After finish_decompress, we can close the input file.
+ * Here we postpone it until after no more JPEG errors are possible,
+ * so as to simplify the setjmp error logic above. (Actually, I don't
+ * think that jpeg_destroy can do an error exit, but why assume anything...)
+ */
+ ri.FS_FreeFile (fbuffer);
+
+ /* At this point you may want to check to see whether any corrupt-data
+ * warnings occurred (test whether jerr.pub.num_warnings is nonzero).
+ */
+
+ /* And we're done! */
+}
+
+
+/* Expanded data destination object for stdio output */
+
+typedef struct {
+ struct jpeg_destination_mgr pub; /* public fields */
+
+ byte* outfile; /* target stream */
+ int size;
+} my_destination_mgr;
+
+typedef my_destination_mgr * my_dest_ptr;
+
+
+/*
+ * Initialize destination --- called by jpeg_start_compress
+ * before any data is actually written.
+ */
+
+void init_destination (j_compress_ptr cinfo)
+{
+ my_dest_ptr dest = (my_dest_ptr) cinfo->dest;
+
+ dest->pub.next_output_byte = dest->outfile;
+ dest->pub.free_in_buffer = dest->size;
+}
+
+
+/*
+ * Empty the output buffer --- called whenever buffer fills up.
+ *
+ * In typical applications, this should write the entire output buffer
+ * (ignoring the current state of next_output_byte & free_in_buffer),
+ * reset the pointer & count to the start of the buffer, and return TRUE
+ * indicating that the buffer has been dumped.
+ *
+ * In applications that need to be able to suspend compression due to output
+ * overrun, a FALSE return indicates that the buffer cannot be emptied now.
+ * In this situation, the compressor will return to its caller (possibly with
+ * an indication that it has not accepted all the supplied scanlines). The
+ * application should resume compression after it has made more room in the
+ * output buffer. Note that there are substantial restrictions on the use of
+ * suspension --- see the documentation.
+ *
+ * When suspending, the compressor will back up to a convenient restart point
+ * (typically the start of the current MCU). next_output_byte & free_in_buffer
+ * indicate where the restart point will be if the current call returns FALSE.
+ * Data beyond this point will be regenerated after resumption, so do not
+ * write it out when emptying the buffer externally.
+ */
+
+boolean empty_output_buffer (j_compress_ptr cinfo)
+{
+ return TRUE;
+}
+
+
+/*
+ * Compression initialization.
+ * Before calling this, all parameters and a data destination must be set up.
+ *
+ * We require a write_all_tables parameter as a failsafe check when writing
+ * multiple datastreams from the same compression object. Since prior runs
+ * will have left all the tables marked sent_table=TRUE, a subsequent run
+ * would emit an abbreviated stream (no tables) by default. This may be what
+ * is wanted, but for safety's sake it should not be the default behavior:
+ * programmers should have to make a deliberate choice to emit abbreviated
+ * images. Therefore the documentation and examples should encourage people
+ * to pass write_all_tables=TRUE; then it will take active thought to do the
+ * wrong thing.
+ */
+
+GLOBAL void
+jpeg_start_compress (j_compress_ptr cinfo, boolean write_all_tables)
+{
+ if (cinfo->global_state != CSTATE_START)
+ ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state);
+
+ if (write_all_tables)
+ jpeg_suppress_tables(cinfo, FALSE); /* mark all tables to be written */
+
+ /* (Re)initialize error mgr and destination modules */
+ (*cinfo->err->reset_error_mgr) ((j_common_ptr) cinfo);
+ (*cinfo->dest->init_destination) (cinfo);
+ /* Perform master selection of active modules */
+ jinit_compress_master(cinfo);
+ /* Set up for the first pass */
+ (*cinfo->master->prepare_for_pass) (cinfo);
+ /* Ready for application to drive first pass through jpeg_write_scanlines
+ * or jpeg_write_raw_data.
+ */
+ cinfo->next_scanline = 0;
+ cinfo->global_state = (cinfo->raw_data_in ? CSTATE_RAW_OK : CSTATE_SCANNING);
+}
+
+
+/*
+ * Write some scanlines of data to the JPEG compressor.
+ *
+ * The return value will be the number of lines actually written.
+ * This should be less than the supplied num_lines only in case that
+ * the data destination module has requested suspension of the compressor,
+ * or if more than image_height scanlines are passed in.
+ *
+ * Note: we warn about excess calls to jpeg_write_scanlines() since
+ * this likely signals an application programmer error. However,
+ * excess scanlines passed in the last valid call are *silently* ignored,
+ * so that the application need not adjust num_lines for end-of-image
+ * when using a multiple-scanline buffer.
+ */
+
+GLOBAL JDIMENSION
+jpeg_write_scanlines (j_compress_ptr cinfo, JSAMPARRAY scanlines,
+ JDIMENSION num_lines)
+{
+ JDIMENSION row_ctr, rows_left;
+
+ if (cinfo->global_state != CSTATE_SCANNING)
+ ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state);
+ if (cinfo->next_scanline >= cinfo->image_height)
+ WARNMS(cinfo, JWRN_TOO_MUCH_DATA);
+
+ /* Call progress monitor hook if present */
+ if (cinfo->progress != NULL) {
+ cinfo->progress->pass_counter = (long) cinfo->next_scanline;
+ cinfo->progress->pass_limit = (long) cinfo->image_height;
+ (*cinfo->progress->progress_monitor) ((j_common_ptr) cinfo);
+ }
+
+ /* Give master control module another chance if this is first call to
+ * jpeg_write_scanlines. This lets output of the frame/scan headers be
+ * delayed so that application can write COM, etc, markers between
+ * jpeg_start_compress and jpeg_write_scanlines.
+ */
+ if (cinfo->master->call_pass_startup)
+ (*cinfo->master->pass_startup) (cinfo);
+
+ /* Ignore any extra scanlines at bottom of image. */
+ rows_left = cinfo->image_height - cinfo->next_scanline;
+ if (num_lines > rows_left)
+ num_lines = rows_left;
+
+ row_ctr = 0;
+ (*cinfo->main->process_data) (cinfo, scanlines, &row_ctr, num_lines);
+ cinfo->next_scanline += row_ctr;
+ return row_ctr;
+}
+
+/*
+ * Terminate destination --- called by jpeg_finish_compress
+ * after all data has been written. Usually needs to flush buffer.
+ *
+ * NB: *not* called by jpeg_abort or jpeg_destroy; surrounding
+ * application must deal with any cleanup that should happen even
+ * for error exit.
+ */
+
+static int hackSize;
+
+void term_destination (j_compress_ptr cinfo)
+{
+ my_dest_ptr dest = (my_dest_ptr) cinfo->dest;
+ size_t datacount = dest->size - dest->pub.free_in_buffer;
+ hackSize = datacount;
+}
+
+
+/*
+ * Prepare for output to a stdio stream.
+ * The caller must have already opened the stream, and is responsible
+ * for closing it after finishing compression.
+ */
+
+void jpegDest (j_compress_ptr cinfo, byte* outfile, int size)
+{
+ my_dest_ptr dest;
+
+ /* The destination object is made permanent so that multiple JPEG images
+ * can be written to the same file without re-executing jpeg_stdio_dest.
+ * This makes it dangerous to use this manager and a different destination
+ * manager serially with the same JPEG object, because their private object
+ * sizes may be different. Caveat programmer.
+ */
+ if (cinfo->dest == NULL) { /* first time for this JPEG object? */
+ cinfo->dest = (struct jpeg_destination_mgr *)
+ (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT,
+ sizeof(my_destination_mgr));
+ }
+
+ dest = (my_dest_ptr) cinfo->dest;
+ dest->pub.init_destination = init_destination;
+ dest->pub.empty_output_buffer = empty_output_buffer;
+ dest->pub.term_destination = term_destination;
+ dest->outfile = outfile;
+ dest->size = size;
+}
+
+void SaveJPG(char * filename, int quality, int image_width, int image_height, unsigned char *image_buffer) {
+ /* This struct contains the JPEG compression parameters and pointers to
+ * working space (which is allocated as needed by the JPEG library).
+ * It is possible to have several such structures, representing multiple
+ * compression/decompression processes, in existence at once. We refer
+ * to any one struct (and its associated working data) as a "JPEG object".
+ */
+ struct jpeg_compress_struct cinfo;
+ /* This struct represents a JPEG error handler. It is declared separately
+ * because applications often want to supply a specialized error handler
+ * (see the second half of this file for an example). But here we just
+ * take the easy way out and use the standard error handler, which will
+ * print a message on stderr and call exit() if compression fails.
+ * Note that this struct must live as long as the main JPEG parameter
+ * struct, to avoid dangling-pointer problems.
+ */
+ struct jpeg_error_mgr jerr;
+ /* More stuff */
+ JSAMPROW row_pointer[1]; /* pointer to JSAMPLE row[s] */
+ int row_stride; /* physical row width in image buffer */
+ unsigned char *out;
+
+ /* Step 1: allocate and initialize JPEG compression object */
+
+ /* We have to set up the error handler first, in case the initialization
+ * step fails. (Unlikely, but it could happen if you are out of memory.)
+ * This routine fills in the contents of struct jerr, and returns jerr's
+ * address which we place into the link field in cinfo.
+ */
+ cinfo.err = jpeg_std_error(&jerr);
+ /* Now we can initialize the JPEG compression object. */
+ jpeg_create_compress(&cinfo);
+
+ /* Step 2: specify data destination (eg, a file) */
+ /* Note: steps 2 and 3 can be done in either order. */
+
+ /* Here we use the library-supplied code to send compressed data to a
+ * stdio stream. You can also write your own code to do something else.
+ * VERY IMPORTANT: use "b" option to fopen() if you are on a machine that
+ * requires it in order to write binary files.
+ */
+ out = ri.Hunk_AllocateTempMemory(image_width*image_height*4);
+ jpegDest(&cinfo, out, image_width*image_height*4);
+
+ /* Step 3: set parameters for compression */
+
+ /* First we supply a description of the input image.
+ * Four fields of the cinfo struct must be filled in:
+ */
+ cinfo.image_width = image_width; /* image width and height, in pixels */
+ cinfo.image_height = image_height;
+ cinfo.input_components = 4; /* # of color components per pixel */
+ cinfo.in_color_space = JCS_RGB; /* colorspace of input image */
+ /* Now use the library's routine to set default compression parameters.
+ * (You must set at least cinfo.in_color_space before calling this,
+ * since the defaults depend on the source color space.)
+ */
+ jpeg_set_defaults(&cinfo);
+ /* Now you can set any non-default parameters you wish to.
+ * Here we just illustrate the use of quality (quantization table) scaling:
+ */
+ jpeg_set_quality(&cinfo, quality, TRUE /* limit to baseline-JPEG values */);
+ /* If quality is set high, disable chroma subsampling */
+ if (quality >= 85) {
+ cinfo.comp_info[0].h_samp_factor = 1;
+ cinfo.comp_info[0].v_samp_factor = 1;
+ }
+
+ /* Step 4: Start compressor */
+
+ /* TRUE ensures that we will write a complete interchange-JPEG file.
+ * Pass TRUE unless you are very sure of what you're doing.
+ */
+ jpeg_start_compress(&cinfo, TRUE);
+
+ /* Step 5: while (scan lines remain to be written) */
+ /* jpeg_write_scanlines(...); */
+
+ /* Here we use the library's state variable cinfo.next_scanline as the
+ * loop counter, so that we don't have to keep track ourselves.
+ * To keep things simple, we pass one scanline per call; you can pass
+ * more if you wish, though.
+ */
+ row_stride = image_width * 4; /* JSAMPLEs per row in image_buffer */
+
+ while (cinfo.next_scanline < cinfo.image_height) {
+ /* jpeg_write_scanlines expects an array of pointers to scanlines.
+ * Here the array is only one element long, but you could pass
+ * more than one scanline at a time if that's more convenient.
+ */
+ row_pointer[0] = & image_buffer[((cinfo.image_height-1)*row_stride)-cinfo.next_scanline * row_stride];
+ (void) jpeg_write_scanlines(&cinfo, row_pointer, 1);
+ }
+
+ /* Step 6: Finish compression */
+
+ jpeg_finish_compress(&cinfo);
+ /* After finish_compress, we can close the output file. */
+ ri.FS_WriteFile( filename, out, hackSize );
+
+ ri.Hunk_FreeTempMemory(out);
+
+ /* Step 7: release JPEG compression object */
+
+ /* This is an important step since it will release a good deal of memory. */
+ jpeg_destroy_compress(&cinfo);
+
+ /* And we're done! */
+}
+
+/*
+=================
+SaveJPGToBuffer
+=================
+*/
+int SaveJPGToBuffer( byte *buffer, int quality,
+ int image_width, int image_height,
+ byte *image_buffer )
+{
+ struct jpeg_compress_struct cinfo;
+ struct jpeg_error_mgr jerr;
+ JSAMPROW row_pointer[1]; /* pointer to JSAMPLE row[s] */
+ int row_stride; /* physical row width in image buffer */
+
+ /* Step 1: allocate and initialize JPEG compression object */
+ cinfo.err = jpeg_std_error(&jerr);
+ /* Now we can initialize the JPEG compression object. */
+ jpeg_create_compress(&cinfo);
+
+ /* Step 2: specify data destination (eg, a file) */
+ /* Note: steps 2 and 3 can be done in either order. */
+ jpegDest(&cinfo, buffer, image_width*image_height*4);
+
+ /* Step 3: set parameters for compression */
+ cinfo.image_width = image_width; /* image width and height, in pixels */
+ cinfo.image_height = image_height;
+ cinfo.input_components = 4; /* # of color components per pixel */
+ cinfo.in_color_space = JCS_RGB; /* colorspace of input image */
+
+ jpeg_set_defaults(&cinfo);
+ jpeg_set_quality(&cinfo, quality, TRUE /* limit to baseline-JPEG values */);
+ /* If quality is set high, disable chroma subsampling */
+ if (quality >= 85) {
+ cinfo.comp_info[0].h_samp_factor = 1;
+ cinfo.comp_info[0].v_samp_factor = 1;
+ }
+
+ /* Step 4: Start compressor */
+ jpeg_start_compress(&cinfo, TRUE);
+
+ /* Step 5: while (scan lines remain to be written) */
+ /* jpeg_write_scanlines(...); */
+ row_stride = image_width * 4; /* JSAMPLEs per row in image_buffer */
+
+ while (cinfo.next_scanline < cinfo.image_height) {
+ /* jpeg_write_scanlines expects an array of pointers to scanlines.
+ * Here the array is only one element long, but you could pass
+ * more than one scanline at a time if that's more convenient.
+ */
+ row_pointer[0] = & image_buffer[((cinfo.image_height-1)*row_stride)-cinfo.next_scanline * row_stride];
+ (void) jpeg_write_scanlines(&cinfo, row_pointer, 1);
+ }
+
+ /* Step 6: Finish compression */
+ jpeg_finish_compress(&cinfo);
+
+ /* Step 7: release JPEG compression object */
+ jpeg_destroy_compress(&cinfo);
+
+ /* And we're done! */
+ return hackSize;
+}
diff --git a/code/renderer/tr_image_jpg.h b/code/renderer/tr_image_jpg.h
new file mode 100644
index 0000000..159c613
--- /dev/null
+++ b/code/renderer/tr_image_jpg.h
@@ -0,0 +1 @@
+void LoadJPG( const char *name, byte **pic, int *width, int *height );
diff --git a/code/renderer/tr_image_pcx.c b/code/renderer/tr_image_pcx.c
new file mode 100644
index 0000000..f369b76
--- /dev/null
+++ b/code/renderer/tr_image_pcx.c
@@ -0,0 +1,166 @@
+/*
+===========================================================================
+Copyright (C) 1999-2005 Id Software, Inc.
+
+This file is part of Quake III Arena source code.
+
+Quake III Arena 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 III Arena 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 III Arena source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+
+#include "tr_local.h"
+
+/*
+========================================================================
+
+PCX files are used for 8 bit images
+
+========================================================================
+*/
+
+typedef struct {
+ char manufacturer;
+ char version;
+ char encoding;
+ char bits_per_pixel;
+ unsigned short xmin,ymin,xmax,ymax;
+ unsigned short hres,vres;
+ unsigned char palette[48];
+ char reserved;
+ char color_planes;
+ unsigned short bytes_per_line;
+ unsigned short palette_type;
+ char filler[58];
+ unsigned char data; // unbounded
+} pcx_t;
+
+
+static void _LoadPCX ( const char *filename, byte **pic, byte **palette, int *width, int *height)
+{
+ byte *raw;
+ pcx_t *pcx;
+ int x, y;
+ int len;
+ int dataByte, runLength;
+ byte *out, *pix;
+ unsigned xmax, ymax;
+
+ *pic = NULL;
+ *palette = NULL;
+
+ //
+ // load the file
+ //
+ len = ri.FS_ReadFile( ( char * ) filename, (void **)&raw);
+ if (!raw) {
+ return;
+ }
+
+ //
+ // parse the PCX file
+ //
+ pcx = (pcx_t *)raw;
+ raw = &pcx->data;
+
+ xmax = LittleShort(pcx->xmax);
+ ymax = LittleShort(pcx->ymax);
+
+ if (pcx->manufacturer != 0x0a
+ || pcx->version != 5
+ || pcx->encoding != 1
+ || pcx->bits_per_pixel != 8
+ || xmax >= 1024
+ || ymax >= 1024)
+ {
+ ri.Printf (PRINT_ALL, "Bad pcx file %s (%i x %i) (%i x %i)\n", filename, xmax+1, ymax+1, pcx->xmax, pcx->ymax);
+ return;
+ }
+
+ out = ri.Malloc ( (ymax+1) * (xmax+1) );
+
+ *pic = out;
+
+ pix = out;
+
+ if (palette)
+ {
+ *palette = ri.Malloc(768);
+ Com_Memcpy (*palette, (byte *)pcx + len - 768, 768);
+ }
+
+ if (width)
+ *width = xmax+1;
+ if (height)
+ *height = ymax+1;
+// FIXME: use bytes_per_line here?
+
+ for (y=0 ; y<=ymax ; y++, pix += xmax+1)
+ {
+ for (x=0 ; x<=xmax ; )
+ {
+ dataByte = *raw++;
+
+ if((dataByte & 0xC0) == 0xC0)
+ {
+ runLength = dataByte & 0x3F;
+ dataByte = *raw++;
+ }
+ else
+ runLength = 1;
+
+ while(runLength-- > 0)
+ pix[x++] = dataByte;
+ }
+
+ }
+
+ if ( raw - (byte *)pcx > len)
+ {
+ ri.Printf (PRINT_DEVELOPER, "PCX file %s was malformed", filename);
+ ri.Free (*pic);
+ *pic = NULL;
+ }
+
+ ri.FS_FreeFile (pcx);
+}
+
+
+void LoadPCX ( const char *filename, byte **pic, int *width, int *height) {
+ byte *palette;
+ byte *pic8;
+ int i, c, p;
+ byte *pic32;
+
+ _LoadPCX (filename, &pic8, &palette, width, height);
+ if (!pic8) {
+ *pic = NULL;
+ return;
+ }
+
+ // LoadPCX32 ensures width, height < 1024
+ c = (*width) * (*height);
+ pic32 = *pic = ri.Malloc(4 * c );
+ for (i = 0 ; i < c ; i++) {
+ p = pic8[i];
+ pic32[0] = palette[p*3];
+ pic32[1] = palette[p*3 + 1];
+ pic32[2] = palette[p*3 + 2];
+ pic32[3] = 255;
+ pic32 += 4;
+ }
+
+ ri.Free (pic8);
+ ri.Free (palette);
+}
diff --git a/code/renderer/tr_image_pcx.h b/code/renderer/tr_image_pcx.h
new file mode 100644
index 0000000..0c8a5f7
--- /dev/null
+++ b/code/renderer/tr_image_pcx.h
@@ -0,0 +1 @@
+void LoadPCX( const char *name, byte **pic, int *width, int *height );
diff --git a/code/renderer/tr_image_tga.c b/code/renderer/tr_image_tga.c
new file mode 100644
index 0000000..b375404
--- /dev/null
+++ b/code/renderer/tr_image_tga.c
@@ -0,0 +1,317 @@
+/*
+===========================================================================
+Copyright (C) 1999-2005 Id Software, Inc.
+
+This file is part of Quake III Arena source code.
+
+Quake III Arena 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 III Arena 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 III Arena source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+
+#include "tr_local.h"
+
+/*
+========================================================================
+
+TGA files are used for 24/32 bit images
+
+========================================================================
+*/
+
+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;
+
+void LoadTGA ( const char *name, byte **pic, int *width, int *height)
+{
+ unsigned columns, rows, numPixels;
+ byte *pixbuf;
+ int row, column;
+ byte *buf_p;
+ byte *end;
+ byte *buffer = NULL;
+ TargaHeader targa_header;
+ byte *targa_rgba;
+ int length;
+
+ *pic = NULL;
+
+ if(width)
+ *width = 0;
+ if(height)
+ *height = 0;
+
+ //
+ // load the file
+ //
+ length = ri.FS_ReadFile ( ( char * ) name, (void **)&buffer);
+ if (!buffer || length < 0) {
+ return;
+ }
+
+ if(length < 18)
+ {
+ ri.Error( ERR_DROP, "LoadTGA: header too short (%s)\n", name );
+ }
+
+ buf_p = buffer;
+ end = buffer + length;
+
+ targa_header.id_length = buf_p[0];
+ targa_header.colormap_type = buf_p[1];
+ targa_header.image_type = buf_p[2];
+
+ memcpy(&targa_header.colormap_index, &buf_p[3], 2);
+ memcpy(&targa_header.colormap_length, &buf_p[5], 2);
+ targa_header.colormap_size = buf_p[7];
+ memcpy(&targa_header.x_origin, &buf_p[8], 2);
+ memcpy(&targa_header.y_origin, &buf_p[10], 2);
+ memcpy(&targa_header.width, &buf_p[12], 2);
+ memcpy(&targa_header.height, &buf_p[14], 2);
+ targa_header.pixel_size = buf_p[16];
+ targa_header.attributes = buf_p[17];
+
+ targa_header.colormap_index = LittleShort(targa_header.colormap_index);
+ targa_header.colormap_length = LittleShort(targa_header.colormap_length);
+ targa_header.x_origin = LittleShort(targa_header.x_origin);
+ targa_header.y_origin = LittleShort(targa_header.y_origin);
+ targa_header.width = LittleShort(targa_header.width);
+ targa_header.height = LittleShort(targa_header.height);
+
+ buf_p += 18;
+
+ if (targa_header.image_type!=2
+ && targa_header.image_type!=10
+ && targa_header.image_type != 3 )
+ {
+ ri.Error (ERR_DROP, "LoadTGA: Only type 2 (RGB), 3 (gray), and 10 (RGB) TGA images supported\n");
+ }
+
+ if ( targa_header.colormap_type != 0 )
+ {
+ ri.Error( ERR_DROP, "LoadTGA: colormaps not supported\n" );
+ }
+
+ if ( ( targa_header.pixel_size != 32 && targa_header.pixel_size != 24 ) && targa_header.image_type != 3 )
+ {
+ ri.Error (ERR_DROP, "LoadTGA: Only 32 or 24 bit images supported (no colormaps)\n");
+ }
+
+ columns = targa_header.width;
+ rows = targa_header.height;
+ numPixels = columns * rows * 4;
+
+ if(!columns || !rows || numPixels > 0x7FFFFFFF || numPixels / columns / 4 != rows)
+ {
+ ri.Error (ERR_DROP, "LoadTGA: %s has an invalid image size\n", name);
+ }
+
+
+ targa_rgba = ri.Malloc (numPixels);
+
+ if (targa_header.id_length != 0)
+ {
+ if (buf_p + targa_header.id_length > end)
+ ri.Error( ERR_DROP, "LoadTGA: header too short (%s)\n", name );
+
+ buf_p += targa_header.id_length; // skip TARGA image comment
+ }
+
+ if ( targa_header.image_type==2 || targa_header.image_type == 3 )
+ {
+ if(buf_p + columns*rows*targa_header.pixel_size/8 > end)
+ {
+ ri.Error (ERR_DROP, "LoadTGA: file truncated (%s)\n", name);
+ }
+
+ // Uncompressed RGB or gray scale image
+ for(row=rows-1; row>=0; row--)
+ {
+ pixbuf = targa_rgba + row*columns*4;
+ for(column=0; column<columns; column++)
+ {
+ unsigned char red,green,blue,alphabyte;
+ switch (targa_header.pixel_size)
+ {
+
+ case 8:
+ blue = *buf_p++;
+ green = blue;
+ red = blue;
+ *pixbuf++ = red;
+ *pixbuf++ = green;
+ *pixbuf++ = blue;
+ *pixbuf++ = 255;
+ break;
+
+ case 24:
+ blue = *buf_p++;
+ green = *buf_p++;
+ red = *buf_p++;
+ *pixbuf++ = red;
+ *pixbuf++ = green;
+ *pixbuf++ = blue;
+ *pixbuf++ = 255;
+ break;
+ case 32:
+ blue = *buf_p++;
+ green = *buf_p++;
+ red = *buf_p++;
+ alphabyte = *buf_p++;
+ *pixbuf++ = red;
+ *pixbuf++ = green;
+ *pixbuf++ = blue;
+ *pixbuf++ = alphabyte;
+ break;
+ default:
+ ri.Error( ERR_DROP, "LoadTGA: illegal pixel_size '%d' in file '%s'\n", targa_header.pixel_size, name );
+ break;
+ }
+ }
+ }
+ }
+ else if (targa_header.image_type==10) { // Runlength encoded RGB images
+ unsigned char red,green,blue,alphabyte,packetHeader,packetSize,j;
+
+ red = 0;
+ green = 0;
+ blue = 0;
+ alphabyte = 0xff;
+
+ for(row=rows-1; row>=0; row--) {
+ pixbuf = targa_rgba + row*columns*4;
+ for(column=0; column<columns; ) {
+ if(buf_p + 1 > end)
+ ri.Error (ERR_DROP, "LoadTGA: file truncated (%s)\n", name);
+ packetHeader= *buf_p++;
+ packetSize = 1 + (packetHeader & 0x7f);
+ if (packetHeader & 0x80) { // run-length packet
+ if(buf_p + targa_header.pixel_size/8 > end)
+ ri.Error (ERR_DROP, "LoadTGA: file truncated (%s)\n", name);
+ switch (targa_header.pixel_size) {
+ case 24:
+ blue = *buf_p++;
+ green = *buf_p++;
+ red = *buf_p++;
+ alphabyte = 255;
+ break;
+ case 32:
+ blue = *buf_p++;
+ green = *buf_p++;
+ red = *buf_p++;
+ alphabyte = *buf_p++;
+ break;
+ default:
+ ri.Error( ERR_DROP, "LoadTGA: illegal pixel_size '%d' in file '%s'\n", targa_header.pixel_size, name );
+ break;
+ }
+
+ for(j=0;j<packetSize;j++) {
+ *pixbuf++=red;
+ *pixbuf++=green;
+ *pixbuf++=blue;
+ *pixbuf++=alphabyte;
+ column++;
+ if (column==columns) { // run spans across rows
+ column=0;
+ if (row>0)
+ row--;
+ else
+ goto breakOut;
+ pixbuf = targa_rgba + row*columns*4;
+ }
+ }
+ }
+ else { // non run-length packet
+
+ if(buf_p + targa_header.pixel_size/8*packetSize > end)
+ ri.Error (ERR_DROP, "LoadTGA: file truncated (%s)\n", name);
+ for(j=0;j<packetSize;j++) {
+ switch (targa_header.pixel_size) {
+ case 24:
+ blue = *buf_p++;
+ green = *buf_p++;
+ red = *buf_p++;
+ *pixbuf++ = red;
+ *pixbuf++ = green;
+ *pixbuf++ = blue;
+ *pixbuf++ = 255;
+ break;
+ case 32:
+ blue = *buf_p++;
+ green = *buf_p++;
+ red = *buf_p++;
+ alphabyte = *buf_p++;
+ *pixbuf++ = red;
+ *pixbuf++ = green;
+ *pixbuf++ = blue;
+ *pixbuf++ = alphabyte;
+ break;
+ default:
+ ri.Error( ERR_DROP, "LoadTGA: illegal pixel_size '%d' in file '%s'\n", targa_header.pixel_size, name );
+ break;
+ }
+ column++;
+ if (column==columns) { // pixel packet run spans across rows
+ column=0;
+ if (row>0)
+ row--;
+ else
+ goto breakOut;
+ pixbuf = targa_rgba + row*columns*4;
+ }
+ }
+ }
+ }
+ breakOut:;
+ }
+ }
+
+#if 0
+ // TTimo: this is the chunk of code to ensure a behavior that meets TGA specs
+ // bit 5 set => top-down
+ if (targa_header.attributes & 0x20) {
+ unsigned char *flip = (unsigned char*)malloc (columns*4);
+ unsigned char *src, *dst;
+
+ for (row = 0; row < rows/2; row++) {
+ src = targa_rgba + row * 4 * columns;
+ dst = targa_rgba + (rows - row - 1) * 4 * columns;
+
+ memcpy (flip, src, columns*4);
+ memcpy (src, dst, columns*4);
+ memcpy (dst, flip, columns*4);
+ }
+ free (flip);
+ }
+#endif
+ // instead we just print a warning
+ if (targa_header.attributes & 0x20) {
+ ri.Printf( PRINT_WARNING, "WARNING: '%s' TGA file header declares top-down image, ignoring\n", name);
+ }
+
+ if (width)
+ *width = columns;
+ if (height)
+ *height = rows;
+
+ *pic = targa_rgba;
+
+ ri.FS_FreeFile (buffer);
+}
diff --git a/code/renderer/tr_image_tga.h b/code/renderer/tr_image_tga.h
new file mode 100644
index 0000000..05e9916
--- /dev/null
+++ b/code/renderer/tr_image_tga.h
@@ -0,0 +1 @@
+void LoadTGA( const char *name, byte **pic, int *width, int *height );