123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766 |
- /*
- ===========================================================================
- Doom 3 BFG Edition GPL Source Code
- Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company.
- This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").
- Doom 3 BFG Edition 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 3 of the License, or
- (at your option) any later version.
- Doom 3 BFG Edition 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 Doom 3 BFG Edition Source Code. If not, see <http://www.gnu.org/licenses/>.
- In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below.
- If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
- ===========================================================================
- */
- #pragma hdrstop
- #include "../idlib/precompiled.h"
- #include "tr_local.h"
- /*
- This file only has a single entry point:
- void R_LoadImage( const char *name, byte **pic, int *width, int *height, bool makePowerOf2 );
- */
- /*
- * 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".
- */
- #include "jpeg-6/jpeglib.h"
- // hooks from jpeg lib to our system
- void jpg_Error( const char *fmt, ... ) {
- va_list argptr;
- char msg[2048];
- va_start (argptr,fmt);
- vsprintf (msg,fmt,argptr);
- va_end (argptr);
- common->FatalError( "%s", msg );
- }
- void jpg_Printf( const char *fmt, ... ) {
- va_list argptr;
- char msg[2048];
- va_start (argptr,fmt);
- vsprintf (msg,fmt,argptr);
- va_end (argptr);
- common->Printf( "%s", msg );
- }
- /*
- ================
- R_WriteTGA
- ================
- */
- void R_WriteTGA( const char *filename, const byte *data, int width, int height, bool flipVertical, const char * basePath ) {
- byte *buffer;
- int i;
- int bufferSize = width*height*4 + 18;
- int imgStart = 18;
- idTempArray<byte> buf( bufferSize );
- buffer = (byte *)buf.Ptr();
- memset( buffer, 0, 18 );
- buffer[2] = 2; // uncompressed type
- buffer[12] = width&255;
- buffer[13] = width>>8;
- buffer[14] = height&255;
- buffer[15] = height>>8;
- buffer[16] = 32; // pixel size
- if ( !flipVertical ) {
- buffer[17] = (1<<5); // flip bit, for normal top to bottom raster order
- }
- // swap rgb to bgr
- for ( i=imgStart ; i<bufferSize ; i+=4 ) {
- buffer[i] = data[i-imgStart+2]; // blue
- buffer[i+1] = data[i-imgStart+1]; // green
- buffer[i+2] = data[i-imgStart+0]; // red
- buffer[i+3] = data[i-imgStart+3]; // alpha
- }
- fileSystem->WriteFile( filename, buffer, bufferSize, basePath );
- }
- static void LoadTGA( const char *name, byte **pic, int *width, int *height, ID_TIME_T *timestamp );
- static void LoadJPG( const char *name, byte **pic, int *width, int *height, ID_TIME_T *timestamp );
- /*
- ========================================================================
- 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;
- /*
- =========================================================
- TARGA LOADING
- =========================================================
- */
- /*
- =============
- LoadTGA
- =============
- */
- static void LoadTGA( const char *name, byte **pic, int *width, int *height, ID_TIME_T *timestamp ) {
- int columns, rows, numPixels, fileSize, numBytes;
- byte *pixbuf;
- int row, column;
- byte *buf_p;
- byte *buffer;
- TargaHeader targa_header;
- byte *targa_rgba;
- if ( !pic ) {
- fileSystem->ReadFile( name, NULL, timestamp );
- return; // just getting timestamp
- }
- *pic = NULL;
- //
- // load the file
- //
- fileSize = fileSystem->ReadFile( name, (void **)&buffer, timestamp );
- if ( !buffer ) {
- return;
- }
- buf_p = buffer;
- targa_header.id_length = *buf_p++;
- targa_header.colormap_type = *buf_p++;
- targa_header.image_type = *buf_p++;
-
- targa_header.colormap_index = LittleShort ( *(short *)buf_p );
- buf_p += 2;
- targa_header.colormap_length = LittleShort ( *(short *)buf_p );
- buf_p += 2;
- targa_header.colormap_size = *buf_p++;
- targa_header.x_origin = LittleShort ( *(short *)buf_p );
- buf_p += 2;
- targa_header.y_origin = LittleShort ( *(short *)buf_p );
- buf_p += 2;
- targa_header.width = LittleShort ( *(short *)buf_p );
- buf_p += 2;
- targa_header.height = LittleShort ( *(short *)buf_p );
- buf_p += 2;
- targa_header.pixel_size = *buf_p++;
- targa_header.attributes = *buf_p++;
- if ( targa_header.image_type != 2 && targa_header.image_type != 10 && targa_header.image_type != 3 ) {
- common->Error( "LoadTGA( %s ): Only type 2 (RGB), 3 (gray), and 10 (RGB) TGA images supported\n", name );
- }
- if ( targa_header.colormap_type != 0 ) {
- common->Error( "LoadTGA( %s ): colormaps not supported\n", name );
- }
- if ( ( targa_header.pixel_size != 32 && targa_header.pixel_size != 24 ) && targa_header.image_type != 3 ) {
- common->Error( "LoadTGA( %s ): Only 32 or 24 bit images supported (no colormaps)\n", name );
- }
- if ( targa_header.image_type == 2 || targa_header.image_type == 3 ) {
- numBytes = targa_header.width * targa_header.height * ( targa_header.pixel_size >> 3 );
- if ( numBytes > fileSize - 18 - targa_header.id_length ) {
- common->Error( "LoadTGA( %s ): incomplete file\n", name );
- }
- }
- columns = targa_header.width;
- rows = targa_header.height;
- numPixels = columns * rows;
- if ( width ) {
- *width = columns;
- }
- if ( height ) {
- *height = rows;
- }
- targa_rgba = (byte *)R_StaticAlloc(numPixels*4, TAG_IMAGE);
- *pic = targa_rgba;
- if ( targa_header.id_length != 0 ) {
- buf_p += targa_header.id_length; // skip TARGA image comment
- }
-
- if ( targa_header.image_type == 2 || targa_header.image_type == 3 )
- {
- // 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:
- common->Error( "LoadTGA( %s ): illegal pixel_size '%d'\n", name, targa_header.pixel_size );
- 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; ) {
- packetHeader= *buf_p++;
- packetSize = 1 + (packetHeader & 0x7f);
- if ( packetHeader & 0x80 ) { // run-length packet
- switch( targa_header.pixel_size ) {
- case 24:
- blue = *buf_p++;
- green = *buf_p++;
- red = *buf_p++;
- alphabyte = 255;
- break;
- case 32:
- blue = *buf_p++;
- green = *buf_p++;
- red = *buf_p++;
- alphabyte = *buf_p++;
- break;
- default:
- common->Error( "LoadTGA( %s ): illegal pixel_size '%d'\n", name, targa_header.pixel_size );
- break;
- }
-
- for( j = 0; j < packetSize; j++ ) {
- *pixbuf++=red;
- *pixbuf++=green;
- *pixbuf++=blue;
- *pixbuf++=alphabyte;
- column++;
- if ( column == columns ) { // run spans across rows
- column = 0;
- if ( row > 0) {
- row--;
- }
- else {
- goto breakOut;
- }
- pixbuf = targa_rgba + row*columns*4;
- }
- }
- }
- else { // non run-length packet
- for( j = 0; j < packetSize; j++ ) {
- switch( targa_header.pixel_size ) {
- case 24:
- blue = *buf_p++;
- green = *buf_p++;
- red = *buf_p++;
- *pixbuf++ = red;
- *pixbuf++ = green;
- *pixbuf++ = blue;
- *pixbuf++ = 255;
- break;
- case 32:
- blue = *buf_p++;
- green = *buf_p++;
- red = *buf_p++;
- alphabyte = *buf_p++;
- *pixbuf++ = red;
- *pixbuf++ = green;
- *pixbuf++ = blue;
- *pixbuf++ = alphabyte;
- break;
- default:
- common->Error( "LoadTGA( %s ): illegal pixel_size '%d'\n", name, targa_header.pixel_size );
- 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 ( (targa_header.attributes & (1<<5)) ) { // image flp bit
- if ( width != NULL && height != NULL ) {
- R_VerticalFlip( *pic, *width, *height );
- }
- }
- fileSystem->FreeFile( buffer );
- }
- /*
- =========================================================
- JPG LOADING
- Interfaces with the huge libjpeg
- =========================================================
- */
- /*
- =============
- LoadJPG
- =============
- */
- static void LoadJPG( const char *filename, unsigned char **pic, int *width, int *height, ID_TIME_T *timestamp ) {
- /* 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;
- /* 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 */
- int row_stride; /* physical row width in output buffer */
- unsigned char *out;
- byte *fbuffer;
- byte *bbuf;
- /* 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.
- */
- // JDC: because fill_input_buffer() blindly copies INPUT_BUF_SIZE bytes,
- // we need to make sure the file buffer is padded or it may crash
- if ( pic ) {
- *pic = NULL; // until proven otherwise
- }
- {
- int len;
- idFile *f;
- f = fileSystem->OpenFileRead( filename );
- if ( !f ) {
- return;
- }
- len = f->Length();
- if ( timestamp ) {
- *timestamp = f->Timestamp();
- }
- if ( !pic ) {
- fileSystem->CloseFile( f );
- return; // just getting timestamp
- }
- fbuffer = (byte *)Mem_ClearedAlloc( len + 4096, TAG_JPG );
- f->Read( fbuffer, len );
- fileSystem->CloseFile( f );
- }
- /* 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 */
- row_stride = cinfo.output_width * cinfo.output_components;
- if (cinfo.output_components!=4) {
- common->DWarning( "JPG %s is unsupported color depth (%d)",
- filename, cinfo.output_components);
- }
- out = (byte *)R_StaticAlloc(cinfo.output_width*cinfo.output_height*4, TAG_IMAGE);
- *pic = out;
- *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.
- */
- bbuf = ((out+(row_stride*cinfo.output_scanline)));
- buffer = &bbuf;
- (void) jpeg_read_scanlines(&cinfo, buffer, 1);
- }
- // clear all the alphas to 255
- {
- int i, j;
- byte *buf;
- buf = *pic;
- j = cinfo.output_width * cinfo.output_height * 4;
- for ( i = 3 ; i < j ; i+=4 ) {
- buf[i] = 255;
- }
- }
- /* 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...)
- */
- Mem_Free( 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! */
- }
- //===================================================================
- /*
- =================
- R_LoadImage
- Loads any of the supported image types into a cannonical
- 32 bit format.
- Automatically attempts to load .jpg files if .tga files fail to load.
- *pic will be NULL if the load failed.
- Anything that is going to make this into a texture would use
- makePowerOf2 = true, but something loading an image as a lookup
- table of some sort would leave it in identity form.
- It is important to do this at image load time instead of texture load
- time for bump maps.
- Timestamp may be NULL if the value is going to be ignored
- If pic is NULL, the image won't actually be loaded, it will just find the
- timestamp.
- =================
- */
- void R_LoadImage( const char *cname, byte **pic, int *width, int *height, ID_TIME_T *timestamp, bool makePowerOf2 ) {
- idStr name = cname;
- if ( pic ) {
- *pic = NULL;
- }
- if ( timestamp ) {
- *timestamp = FILE_NOT_FOUND_TIMESTAMP;
- }
- if ( width ) {
- *width = 0;
- }
- if ( height ) {
- *height = 0;
- }
- name.DefaultFileExtension( ".tga" );
- if (name.Length()<5) {
- return;
- }
- name.ToLower();
- idStr ext;
- name.ExtractFileExtension( ext );
- if ( ext == "tga" ) {
- LoadTGA( name.c_str(), pic, width, height, timestamp ); // try tga first
- if ( ( pic && *pic == 0 ) || ( timestamp && *timestamp == -1 ) ) { //-V595
- name.StripFileExtension();
- name.DefaultFileExtension( ".jpg" );
- LoadJPG( name.c_str(), pic, width, height, timestamp );
- }
- } else if ( ext == "jpg" ) {
- LoadJPG( name.c_str(), pic, width, height, timestamp );
- }
- if ( ( width && *width < 1 ) || ( height && *height < 1 ) ) {
- if ( pic && *pic ) {
- R_StaticFree( *pic );
- *pic = 0;
- }
- }
- //
- // convert to exact power of 2 sizes
- //
- /*
- if ( pic && *pic && makePowerOf2 ) {
- int w, h;
- int scaled_width, scaled_height;
- byte *resampledBuffer;
- w = *width;
- h = *height;
- for (scaled_width = 1 ; scaled_width < w ; scaled_width<<=1)
- ;
- for (scaled_height = 1 ; scaled_height < h ; scaled_height<<=1)
- ;
- if ( scaled_width != w || scaled_height != h ) {
- resampledBuffer = R_ResampleTexture( *pic, w, h, scaled_width, scaled_height );
- R_StaticFree( *pic );
- *pic = resampledBuffer;
- *width = scaled_width;
- *height = scaled_height;
- }
- }
- */
- }
- /*
- =======================
- R_LoadCubeImages
- Loads six files with proper extensions
- =======================
- */
- bool R_LoadCubeImages( const char *imgName, cubeFiles_t extensions, byte *pics[6], int *outSize, ID_TIME_T *timestamp ) {
- int i, j;
- char *cameraSides[6] = { "_forward.tga", "_back.tga", "_left.tga", "_right.tga",
- "_up.tga", "_down.tga" };
- char *axisSides[6] = { "_px.tga", "_nx.tga", "_py.tga", "_ny.tga",
- "_pz.tga", "_nz.tga" };
- char **sides;
- char fullName[MAX_IMAGE_NAME];
- int width, height, size = 0;
- if ( extensions == CF_CAMERA ) {
- sides = cameraSides;
- } else {
- sides = axisSides;
- }
- // FIXME: precompressed cube map files
- if ( pics ) {
- memset( pics, 0, 6*sizeof(pics[0]) );
- }
- if ( timestamp ) {
- *timestamp = 0;
- }
- for ( i = 0 ; i < 6 ; i++ ) {
- idStr::snPrintf( fullName, sizeof( fullName ), "%s%s", imgName, sides[i] );
- ID_TIME_T thisTime;
- if ( !pics ) {
- // just checking timestamps
- R_LoadImageProgram( fullName, NULL, &width, &height, &thisTime );
- } else {
- R_LoadImageProgram( fullName, &pics[i], &width, &height, &thisTime );
- }
- if ( thisTime == FILE_NOT_FOUND_TIMESTAMP ) {
- break;
- }
- if ( i == 0 ) {
- size = width;
- }
- if ( width != size || height != size ) {
- common->Warning( "Mismatched sizes on cube map '%s'", imgName );
- break;
- }
- if ( timestamp ) {
- if ( thisTime > *timestamp ) {
- *timestamp = thisTime;
- }
- }
- if ( pics && extensions == CF_CAMERA ) {
- // convert from "camera" images to native cube map images
- switch( i ) {
- case 0: // forward
- R_RotatePic( pics[i], width);
- break;
- case 1: // back
- R_RotatePic( pics[i], width);
- R_HorizontalFlip( pics[i], width, height );
- R_VerticalFlip( pics[i], width, height );
- break;
- case 2: // left
- R_VerticalFlip( pics[i], width, height );
- break;
- case 3: // right
- R_HorizontalFlip( pics[i], width, height );
- break;
- case 4: // up
- R_RotatePic( pics[i], width);
- break;
- case 5: // down
- R_RotatePic( pics[i], width);
- break;
- }
- }
- }
- if ( i != 6 ) {
- // we had an error, so free everything
- if ( pics ) {
- for ( j = 0 ; j < i ; j++ ) {
- R_StaticFree( pics[j] );
- }
- }
- if ( timestamp ) {
- *timestamp = 0;
- }
- return false;
- }
- if ( outSize ) {
- *outSize = size;
- }
- return true;
- }
|