Files
Jedi-Academy/tools/ModView/r_image.cpp
2013-04-04 14:32:05 -07:00

1200 lines
28 KiB
C++

// Filename:- R_Image.cpp
//
// a pile of block-paste code from other codebases for basic image file stuff
#include "stdafx.h"
#include "includes.h"
#include "R_Common.h"
#include "jpeg_interface.h"
#include "png/png.h"
//
#include "R_Image.h"
static image_t* hashTable[FILE_HASH_SIZE];
/*
========================================================================
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;
/*
=========================================================
BMP LOADING
=========================================================
*/
typedef struct
{
char id[2];
unsigned long fileSize;
unsigned long reserved0;
unsigned long bitmapDataOffset;
unsigned long bitmapHeaderSize;
unsigned long width;
unsigned long height;
unsigned short planes;
unsigned short bitsPerPixel;
unsigned long compression;
unsigned long bitmapDataSize;
unsigned long hRes;
unsigned long vRes;
unsigned long colors;
unsigned long importantColors;
unsigned char palette[256][4];
} BMPHeader_t;
static void LoadBMP( const char *name, byte **pic, int *width, int *height )
{
int columns, rows, numPixels;
byte *pixbuf;
int row, column;
byte *buf_p;
byte *buffer;
int length;
BMPHeader_t bmpHeader;
byte *bmpRGBA;
*pic = NULL;
//
// load the file
//
length = ri.FS_ReadFile( ( char * ) name, (void **)&buffer);
if (!buffer) {
return;
}
buf_p = buffer;
bmpHeader.id[0] = *buf_p++;
bmpHeader.id[1] = *buf_p++;
bmpHeader.fileSize = LittleLong( * ( long * ) buf_p );
buf_p += 4;
bmpHeader.reserved0 = LittleLong( * ( long * ) buf_p );
buf_p += 4;
bmpHeader.bitmapDataOffset = LittleLong( * ( long * ) buf_p );
buf_p += 4;
bmpHeader.bitmapHeaderSize = LittleLong( * ( long * ) buf_p );
buf_p += 4;
bmpHeader.width = LittleLong( * ( long * ) buf_p );
buf_p += 4;
bmpHeader.height = LittleLong( * ( long * ) 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( * ( long * ) buf_p );
buf_p += 4;
bmpHeader.bitmapDataSize = LittleLong( * ( long * ) buf_p );
buf_p += 4;
bmpHeader.hRes = LittleLong( * ( long * ) buf_p );
buf_p += 4;
bmpHeader.vRes = LittleLong( * ( long * ) buf_p );
buf_p += 4;
bmpHeader.colors = LittleLong( * ( long * ) buf_p );
buf_p += 4;
bmpHeader.importantColors = LittleLong( * ( long * ) buf_p );
buf_p += 4;
memcpy( bmpHeader.palette, buf_p, sizeof( bmpHeader.palette ) );
if ( bmpHeader.bitsPerPixel == 8 )
buf_p += 1024;
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 (%d vs. %d) (%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 );
}
columns = bmpHeader.width;
rows = bmpHeader.height;
if ( rows < 0 )
rows = -rows;
numPixels = columns * rows;
if ( width )
*width = columns;
if ( height )
*height = rows;
bmpRGBA = (unsigned char *)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;
default:
ri.Error( ERR_DROP, "LoadBMP: illegal pixel_size '%d' in file '%s'\n", bmpHeader.bitsPerPixel, name );
break;
}
}
}
ri.FS_FreeFile( buffer );
}
/*
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;
correct
000000 00 00 02 00 00 00 00 18 00 00 00 00 76 02 43 01 ............v.C.
000010 18 20 ED D9 C9 EC D9 C8 EC D9 C7 EA D7 C7 EA D7 . íÙÉìÙÈìÙÇê×Çê×
bad
000000 00 00 02 00 00 00 00 00 00 00 00 00 76 02 43 01 ............v.C.
000010 20 00 ED D9 C9 FF EC D9 C8 FF EC D9 C7 FF EA D7 .íÙÉÿìÙÈÿìÙÇÿê×
#pragma pack(push,1)
typedef struct
{
byte byIDFieldLength; // must be 0
byte byColourmapType; // 0 = truecolour, 1 = paletted, else bad
byte byImageType; // 1 = colour mapped (palette), uncompressed, 2 = truecolour, uncompressed, else bad
word w1stColourMapEntry; // must be 0
word wColourMapLength; // 256 for 8-bit palettes, else 0 for true-colour
byte byColourMapEntrySize; // 24 for 8-bit palettes, else 0 for true-colour
word wImageXOrigin; // ignored
word wImageYOrigin; // ignored
word wImageWidth; // in pixels
word wImageHeight; // in pixels
byte byImagePlanes; // bits per pixel (8 for paletted, else 24 for true-colour)
byte byScanLineOrder; // Image descriptor bytes
// bits 0-3 = # attr bits (alpha chan)
// bits 4-5 = pixel order/dir
// bits 6-7 scan line interleave (00b=none,01b=2way interleave,10b=4way)
} TGAHEADER, *LPTGAHEADER;
#pragma pack(pop)
*/
bool TGA_Write(LPCSTR psFullPathedFilename, byte *pPixels, int iWidth, int iHeight, int iPlanes)
{
byte *pbBuffer = NULL;
int iPixelBytes = (iPlanes == 24) ? 3:4;
int iTotalPixelBytes = iWidth * iHeight * iPixelBytes;
int iTotalBufferBytes = iTotalPixelBytes + 18;
assert(iPlanes == 24 || iPlanes == 32);
pbBuffer = (byte *) ri.Hunk_AllocateTempMemory(iTotalBufferBytes);
memset (pbBuffer, 0, 18);
pbBuffer[2] = 2; // uncompressed type
pbBuffer[12] = iWidth & 255;
pbBuffer[13] = iWidth >> 8;
pbBuffer[14] = iHeight & 255;
pbBuffer[15] = iHeight >> 8;
pbBuffer[16] = iPlanes; // pixel size
pbBuffer[17] = 0x00; // scan line order, tells TGA-legal packages that it's upside down. ID code ignore it.
memcpy(pbBuffer+18, pPixels, iTotalPixelBytes );
// turn it upside down to match TGA default, because ID loader ignores scanline attr flags... (sigh)
//
int iScanLineBytes = iWidth * iPixelBytes;
byte *pbScanLine = (byte *) ri.Hunk_AllocateTempMemory(iScanLineBytes);
byte *pbSrc = &pbBuffer[18];
byte *pbDst = &pbBuffer[(18+iTotalPixelBytes)-iScanLineBytes];
for (int iYFlip=0; iYFlip < iHeight/2; iYFlip++)
{
memcpy(pbScanLine,pbSrc,iScanLineBytes);
memcpy(pbSrc,pbDst,iScanLineBytes);
memcpy(pbDst,pbScanLine,iScanLineBytes);
pbSrc+=iScanLineBytes;
pbDst-=iScanLineBytes;
}
ri.Hunk_FreeTempMemory( pbScanLine );
// swap rgb to bgr...
//
for (int i=18 ; i<iTotalBufferBytes ; i += iPixelBytes)
{
byte r = pbBuffer[i];
pbBuffer[i] = pbBuffer[i+2];
pbBuffer[i+2] = r;
}
// ri.FS_WriteFile( psFullPathedFilename, pbBuffer, iTotalBufferBytes );
int iLength = SaveFile (psFullPathedFilename, pbBuffer, iTotalBufferBytes);
ri.Hunk_FreeTempMemory( pbBuffer );
return !!(iLength != -1);
}
/*
=============
LoadTGA
=============
*/
/*
// ID's TGA loader...
//
void LoadTGA ( const char *name, byte **pic, int *width, int *height)
{
int columns, rows, numPixels;
byte *pixbuf;
int row, column;
byte *buf_p;
byte *buffer;
TargaHeader targa_header;
byte *pRGBA;
*pic = NULL;
//
// load the file
//
ri.FS_ReadFile ( ( char * ) name, (void **)&buffer);
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 )
{
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;
if (width)
*width = columns;
if (height)
*height = rows;
pRGBA = (unsigned char *)ri.Malloc (numPixels*4);
*pic = pRGBA;
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 = pRGBA + 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 = pRGBA + 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:
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 = pRGBA + 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:
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 = pRGBA + row*columns*4;
}
}
}
}
breakOut:;
}
}
ri.FS_FreeFile (buffer);
}
*/
// My TGA loader...
//
//---------------------------------------------------
#pragma pack(push,1)
typedef struct
{
byte byIDFieldLength; // must be 0
byte byColourmapType; // 0 = truecolour, 1 = paletted, else bad
byte byImageType; // 1 = colour mapped (palette), uncompressed, 2 = truecolour, uncompressed, else bad
word w1stColourMapEntry; // must be 0
word wColourMapLength; // 256 for 8-bit palettes, else 0 for true-colour
byte byColourMapEntrySize; // 24 for 8-bit palettes, else 0 for true-colour
word wImageXOrigin; // ignored
word wImageYOrigin; // ignored
word wImageWidth; // in pixels
word wImageHeight; // in pixels
byte byImagePlanes; // bits per pixel (8 for paletted, else 24 for true-colour)
byte byScanLineOrder; // Image descriptor bytes
// bits 0-3 = # attr bits (alpha chan)
// bits 4-5 = pixel order/dir
// bits 6-7 scan line interleave (00b=none,01b=2way interleave,10b=4way)
} TGAHeader_t;
#pragma pack(pop)
// *pic == pic, else NULL for failed.
//
// returns false if found but had a format error, else true for either OK or not-found (there's a reason for this)
//
void LoadTGA ( const char *name, byte **pic, int *width, int *height)
{
char sErrorString[1024];
bool bFormatErrors = false;
// these don't need to be declared or initialised until later, but the compiler whines that 'goto' skips them.
//
byte *pRGBA = NULL;
byte *pOut = NULL;
byte *pIn = NULL;
*pic = NULL;
#define TGA_FORMAT_ERROR(blah) {sprintf(sErrorString,blah); bFormatErrors = true; goto TGADone;}
//#define TGA_FORMAT_ERROR(blah) ri.Error( ERR_DROP, blah );
//
// load the file
//
byte *pTempLoadedBuffer = 0;
ri.FS_ReadFile ( ( char * ) name, (void **)&pTempLoadedBuffer);
if (!pTempLoadedBuffer) {
return;
}
TGAHeader_t *pHeader = (TGAHeader_t *) pTempLoadedBuffer;
if (pHeader->byColourmapType!=0)
{
TGA_FORMAT_ERROR("LoadTGA: colourmaps not supported\n" );
}
if (pHeader->byImageType != 2 && pHeader->byImageType != 3 && pHeader->byImageType != 10)
{
TGA_FORMAT_ERROR("LoadTGA: Only type 2 (RGB), 3 (gray), and 10 (RLE-RGB) images supported\n");
}
if (pHeader->w1stColourMapEntry != 0)
{
TGA_FORMAT_ERROR("LoadTGA: colourmaps not supported\n" );
}
if (pHeader->wColourMapLength !=0 && pHeader->wColourMapLength != 256)
{
TGA_FORMAT_ERROR("LoadTGA: ColourMapLength must be either 0 or 256\n" );
}
if (pHeader->byColourMapEntrySize != 0 && pHeader->byColourMapEntrySize != 24)
{
TGA_FORMAT_ERROR("LoadTGA: ColourMapEntrySize must be either 0 or 24\n" );
}
if ( ( pHeader->byImagePlanes != 24 && pHeader->byImagePlanes != 32) && (pHeader->byImagePlanes != 8 && pHeader->byImageType != 3))
{
TGA_FORMAT_ERROR("LoadTGA: Only type 2 (RGB), 3 (gray), and 10 (RGB) TGA images supported\n");
}
if ((pHeader->byScanLineOrder&0x30)!=0x00 &&
(pHeader->byScanLineOrder&0x30)!=0x10 &&
(pHeader->byScanLineOrder&0x30)!=0x20 &&
(pHeader->byScanLineOrder&0x30)!=0x30
)
{
TGA_FORMAT_ERROR("LoadTGA: ScanLineOrder must be either 0x00,0x10,0x20, or 0x30\n");
}
// these last checks are so i can use ID's RLE-code. I don't dare fiddle with it or it'll probably break...
//
if ( pHeader->byImageType == 10)
{
if ((pHeader->byScanLineOrder & 0x30) != 0x00)
{
TGA_FORMAT_ERROR("LoadTGA: RLE-RGB Images (type 10) must be in bottom-to-top format\n");
}
if (pHeader->byImagePlanes != 24 && pHeader->byImagePlanes != 32) // probably won't happen, but avoids compressed greyscales?
{
TGA_FORMAT_ERROR("LoadTGA: RLE-RGB Images (type 10) must be 24 or 32 bit\n");
}
}
// now read the actual bitmap in...
//
// Image descriptor bytes
// bits 0-3 = # attr bits (alpha chan)
// bits 4-5 = pixel order/dir
// bits 6-7 scan line interleave (00b=none,01b=2way interleave,10b=4way)
//
int iYStart,iXStart,iYStep,iXStep;
switch(pHeader->byScanLineOrder & 0x30)
{
case 0x00: // left to right, bottom to top
iXStart = 0;
iXStep = 1;
iYStart = pHeader->wImageHeight-1;
iYStep = -1;
break;
case 0x10: // right to left, bottom to top
iXStart = pHeader->wImageWidth-1;
iXStep = -1;
iYStart = pHeader->wImageHeight-1;
iYStep = -1;
break;
case 0x20: // left to right, top to bottom
iXStart = 0;
iXStep = 1;
iYStart = 0;
iYStep = 1;
break;
case 0x30: // right to left, top to bottom
iXStart = pHeader->wImageWidth-1;
iXStep = -1;
iYStart = 0;
iYStep = 1;
break;
}
// feed back the results...
//
if (width)
*width = pHeader->wImageWidth;
if (height)
*height = pHeader->wImageHeight;
pRGBA = (byte *) ri.Malloc (pHeader->wImageWidth * pHeader->wImageHeight * 4);
*pic = pRGBA;
pOut = pRGBA;
pIn = pTempLoadedBuffer + sizeof(*pHeader);
// I don't know if this ID-thing here is right, since comments that I've seen are at the end of the file,
// with a zero in this field. However, may as well...
//
if (pHeader->byIDFieldLength != 0)
pIn += pHeader->byIDFieldLength; // skip TARGA image comment
byte red,green,blue,alpha;
if ( pHeader->byImageType == 2 || pHeader->byImageType == 3 ) // RGB or greyscale
{
for (int y=iYStart, iYCount=0; iYCount<pHeader->wImageHeight; y+=iYStep, iYCount++)
{
pOut = pRGBA + y * pHeader->wImageWidth *4;
for (int x=iXStart, iXCount=0; iXCount<pHeader->wImageWidth; x+=iXStep, iXCount++)
{
switch (pHeader->byImagePlanes)
{
case 8:
blue = *pIn++;
green = blue;
red = blue;
*pOut++ = red;
*pOut++ = green;
*pOut++ = blue;
*pOut++ = 255;
break;
case 24:
blue = *pIn++;
green = *pIn++;
red = *pIn++;
*pOut++ = red;
*pOut++ = green;
*pOut++ = blue;
*pOut++ = 255;
break;
case 32:
blue = *pIn++;
green = *pIn++;
red = *pIn++;
alpha = *pIn++;
*pOut++ = red;
*pOut++ = green;
*pOut++ = blue;
*pOut++ = alpha;
break;
default:
assert(0); // if we ever hit this, someone deleted a header check higher up
TGA_FORMAT_ERROR("LoadTGA: Image can only have 8, 24 or 32 planes for RGB/greyscale\n");
break;
}
}
}
}
else
if (pHeader->byImageType == 10) // RLE-RGB
{
// I've no idea if this stuff works, I normally reject RLE targas, but this is from ID's code
// so maybe I should try and support it...
//
byte packetHeader, packetSize, j;
for (int y = pHeader->wImageHeight-1; y >= 0; y--)
{
pOut = pRGBA + y * pHeader->wImageWidth *4;
for (int x=0; x<pHeader->wImageWidth;)
{
packetHeader = *pIn++;
packetSize = 1 + (packetHeader & 0x7f);
if (packetHeader & 0x80) // run-length packet
{
switch (pHeader->byImagePlanes)
{
case 24:
blue = *pIn++;
green = *pIn++;
red = *pIn++;
alpha = 255;
break;
case 32:
blue = *pIn++;
green = *pIn++;
red = *pIn++;
alpha = *pIn++;
break;
default:
assert(0); // if we ever hit this, someone deleted a header check higher up
TGA_FORMAT_ERROR("LoadTGA: RLE-RGB can only have 24 or 32 planes\n");
break;
}
for (j=0; j<packetSize; j++)
{
*pOut++ = red;
*pOut++ = green;
*pOut++ = blue;
*pOut++ = alpha;
x++;
if (x == pHeader->wImageWidth) // run spans across rows
{
x = 0;
if (y > 0)
y--;
else
goto breakOut;
pOut = pRGBA + y * pHeader->wImageWidth * 4;
}
}
}
else
{ // non run-length packet
for (j=0; j<packetSize; j++)
{
switch (pHeader->byImagePlanes)
{
case 24:
blue = *pIn++;
green = *pIn++;
red = *pIn++;
*pOut++ = red;
*pOut++ = green;
*pOut++ = blue;
*pOut++ = 255;
break;
case 32:
blue = *pIn++;
green = *pIn++;
red = *pIn++;
alpha = *pIn++;
*pOut++ = red;
*pOut++ = green;
*pOut++ = blue;
*pOut++ = alpha;
break;
default:
assert(0); // if we ever hit this, someone deleted a header check higher up
TGA_FORMAT_ERROR("LoadTGA: RLE-RGB can only have 24 or 32 planes\n");
break;
}
x++;
if (x == pHeader->wImageWidth) // pixel packet run spans across rows
{
x = 0;
if (y > 0)
y--;
else
goto breakOut;
pOut = pRGBA + y * pHeader->wImageWidth * 4;
}
}
}
}
breakOut:;
}
}
TGADone:
ri.FS_FreeFile (pTempLoadedBuffer);
if (bFormatErrors)
{
ErrorBox(va("Error parsing file \"%s\"!\n\n",name, sErrorString));
}
}
/*
==============
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;
int 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 = (unsigned char *)ri.Malloc ( (ymax+1) * (xmax+1) );
*pic = out;
pix = out;
if (palette)
{
*palette = (unsigned char *)ri.Malloc(768);
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;
}
c = (*width) * (*height);
pic32 = *pic = (unsigned char *)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);
}
/*
=================
R_LoadImage
Loads any of the supported image types into a cannonical
32 bit format.
=================
*/
char g_sImageExtension[MAX_QPATH];
void R_LoadImage2( const char *name, byte **pic, int *width, int *height ) {
int len;
*pic = NULL;
*width = 0;
*height = 0;
len = strlen(name);
if (len<5) {
return;
}
char *psExt = strstr(name,".");
strcpy(g_sImageExtension,psExt?psExt:"");
if ( !Q_stricmp( name+len-4, ".tga" ) ) {
LoadTGA( name, pic, width, height ); // try tga first
if (!*pic) { //
char altname[MAX_QPATH]; // try jpg in place of tga
strcpy( altname, name );
len = strlen( altname );
altname[len-3] = 'j';
altname[len-2] = 'p';
altname[len-1] = 'g';
LoadJPG( altname, pic, width, height );
strcpy(g_sImageExtension,".jpg");
}
} else if ( !Q_stricmp(name+len-4, ".pcx") ) {
LoadPCX32( name, pic, width, height );
} else if ( !Q_stricmp( name+len-4, ".bmp" ) ) {
LoadBMP( name, pic, width, height );
} else if ( !Q_stricmp( name+len-4, ".jpg" ) ) {
LoadJPG( name, pic, width, height );
} else if ( !Q_stricmp( name+len-4, ".png" ) ) {
LoadPNG32(name, pic , width, height, NULL);
}
}
// feeder version of above because of John's not having extensions on anymore (but it might do... sigh)
//
bool g_bReportImageLoadErrors = false;
void R_LoadImage1( const char *name, byte **pic, int *width, int *height )
{
int len;
char work[MAX_QPATH];
*pic = NULL;
*width = 0;
*height = 0;
len = strlen(name);
if(len >= MAX_QPATH)
{
return;
}
if (len < 5)
{
return;
}
if (name[1] == ':')
{
if (g_bReportImageLoadErrors)
{
g_bReportImageLoadErrors = GetYesNo(va("Filename \"%s\" has a full-path in it, paths should be local only!\n\nContinue reporting image-load errors?",name));
}
return;
}
// if the file has an extension then try it first...
//
if (name[strlen(name)-4] == '.')
{
R_LoadImage2( name, pic, width, height );
if (*pic)
return;
}
// else if not found, or no extension specified, then force some on in SOF2 order...
//
COM_StripExtension(name, work);
COM_DefaultExtension( work, sizeof( work ), ".png" );
LoadPNG32( work, pic, width, height, NULL ); // try png first
if(*pic)
{
strcpy(g_sImageExtension,".png");
return;
}
COM_StripExtension(name, work);
COM_DefaultExtension( work, sizeof( work ), ".tga" );
LoadTGA( work, pic, width, height); // try tga next
if(*pic)
{
strcpy(g_sImageExtension,".tga");
return;
}
COM_StripExtension(name, work);
COM_DefaultExtension( work, sizeof( work ), ".jpg" );
LoadJPG( work, pic, width, height ); // try jpg last
if(*pic)
{
strcpy(g_sImageExtension,".jpg");
return;
}
// Image loading failed...
//
if (g_bReportImageLoadErrors)
{
g_bReportImageLoadErrors = GetYesNo(va("R_LoadImage(): Couldn't read image:\n\n\"%s\"\n\n... continue receiving this message for this model?", name));
}
}
bool gbInImageLoader = false;
void R_LoadImage( const char *name, byte **pic, int *width, int *height )
{
// only attempt load if not the special case...
//
if (stricmp(name,"[NoMaterial]"))
{
// thanks, Jake... <sigh>
//
char sTempName[1024]={0};
if (bXMenPathHack)
strcpy(sTempName,"textures/");
strcat(sTempName,name);
gbInImageLoader = true;
{
R_LoadImage1( sTempName, pic, width, height );
}
gbInImageLoader = false;
}
else
{
// special case, so just zap whatever the last extension found was, else it gets strcat'd on return...
//
g_sImageExtension[0] = '\0';
}
}
///////////////////////// eof //////////////////////////