#include "tr_local.h" #include "ref_import.h" static void* q3_stbi_malloc(size_t size) { return ri.Malloc((int)size); } static void q3_stbi_free(void* p) { ri.Free(p); } static void* q3_stbi_realloc(void* p, size_t old_size, size_t new_size) { if (p == NULL) return q3_stbi_malloc(new_size); void* p_new; if (old_size < new_size) { p_new = q3_stbi_malloc(new_size); memcpy(p_new, p, old_size); q3_stbi_free(p); } else { p_new = p; } return p_new; } #define STBI_MALLOC q3_stbi_malloc #define STBI_FREE q3_stbi_free #define STBI_REALLOC_SIZED q3_stbi_realloc #define STB_IMAGE_IMPLEMENTATION #include "stb_image.h" static void LoadTGA( const char* name, unsigned char** pic, uint32_t* width, uint32_t* height); static void LoadBMP( const char* name, unsigned char** pic, uint32_t* width, uint32_t* height); static void LoadJPG( const char* name, unsigned char** pic, uint32_t* width, uint32_t* height); static void LoadPCX32 ( const char* filename, unsigned char** pic, uint32_t* width, uint32_t* height); /* Loads any of the supported image types into a cannonical 32 bit format. */ void R_LoadImage2(const char *name, unsigned char **pic, uint32_t* width, uint32_t* height) { const int len = (int)strlen(name); if (len<5) { ri.Printf( PRINT_WARNING, "R_LoadImage2: try to loading %s ? \n", name ); return; } // point to '.', .jped are assume not exist const char* const pPnt = (char*)name + len - 4; if(pPnt[0] == '.') { // have a extension if( ( (pPnt[1] == 't') && (pPnt[2] == 'g') && (pPnt[3] == 'a') ) || ( (pPnt[1] == 'T') && (pPnt[2] == 'G') && (pPnt[3] == 'A') ) ) { LoadTGA( name, pic, width, height ); if (NULL == *pic) { // try jpg in place of tga char altname[128] = {0}; strncpy( altname, name, 128 ); char* pt = altname + len - 4; pt[1] = 'j'; pt[2] = 'p'; pt[3] = 'g'; LoadJPG( altname, pic, width, height ); } } else if ( ( (pPnt[1] == 'j') && (pPnt[2] == 'p') && (pPnt[3] == 'g') ) || ( (pPnt[1] == 'J') && (pPnt[2] == 'P') && (pPnt[3] == 'G') ) ) { LoadJPG( name, pic, width, height ); } else if ( ( (pPnt[1] == 'b') && (pPnt[2] == 'm') && (pPnt[3] == 'p') ) || ( (pPnt[1] == 'B') && (pPnt[2] == 'M') && (pPnt[3] == 'P') ) ) { LoadBMP( name, pic, width, height ); } else if ( ( (pPnt[1] == 'p') && (pPnt[2] == 'c') && (pPnt[3] == 'x') ) || ( (pPnt[1] == 'P') && (pPnt[2] == 'C') && (pPnt[3] == 'X') ) ) { LoadPCX32( name, pic, width, height ); } } else { // without a extension // Try and find a suitable match using all the image formats supported char altname[128] = {0}; strcpy( altname, name ); char* pt = altname + len; pt[0] = '.'; pt[1] = 't'; pt[2] = 'g'; pt[3] = 'a'; pt[4] = '\0'; LoadTGA( altname, pic, width, height ); if (NULL == *pic) { // try jpg in place of tga pt[0] = '.'; pt[1] = 'j'; pt[2] = 'p'; pt[3] = 'g'; pt[4] = '\0'; LoadJPG( altname, pic, width, height ); } if( *pic == NULL ) { ri.Printf( PRINT_WARNING, "%s not present.\n", name); } // else // ri.Printf( PRINT_ALL, "%s without a extension, using %s instead. \n", name, altname); } } // ====================================== // // ====================================== // 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; static void LoadTGA( const char* name, unsigned char** pic, uint32_t* width, uint32_t* height) { int columns, rows, numPixels; unsigned char* pixbuf; int row, column; char* buffer; TargaHeader targa_header; *pic = NULL; // // load the file // ri.FS_ReadFile(name, (void**)&buffer); if (!buffer) { return; } char* 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; unsigned char* targa_rgba = (unsigned char*) ri.Malloc (numPixels*4); *pic = targa_rgba; if (targa_header.id_length != 0) buf_p += targa_header.id_length; // skip TARGA image comment if ( targa_header.image_type==2 || 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=0; row--) { pixbuf = targa_rgba + row*columns*4; for(column=0; column0) row--; else goto breakOut; pixbuf = targa_rgba + row*columns*4; } } } else { // non run-length packet for(j=0;j0) row--; else goto breakOut; pixbuf = targa_rgba + row*columns*4; } } } } breakOut:; } } #if 0 // TTimo: this is the chunk of code to ensure a behavior that meets TGA specs // bk0101024 - fix from Leonardo // 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); } ri.FS_FreeFile (buffer); } static void LoadJPG( const char* name, unsigned char** pic, uint32_t* width, uint32_t* height) { char* fbuffer; int len = ri.FS_ReadFile(name, (void**)&fbuffer); if (!fbuffer) { return; } int components; *pic = stbi_load_from_memory((unsigned char*)fbuffer, len, (int*)width, (int*)height, &components, STBI_rgb_alpha); if (*pic == NULL) { ri.FS_FreeFile(fbuffer); return; } // clear all the alphas to 255 { unsigned int i; unsigned char* buf = *pic; unsigned int nBytes = 4 * (*width) * (*height); for (i = 3; i < nBytes; i += 4) { buf[i] = 255; } } ri.FS_FreeFile(fbuffer); } /* ========================================================= 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, unsigned char **pic, uint32_t *width, uint32_t *height ) { int columns, rows, numPixels; unsigned char *pixbuf; int row, column; char* buf_p; char* buffer; int length; BMPHeader_t bmpHeader; unsigned char *bmpRGBA; *pic = NULL; // // load the file // length = ri.FS_ReadFile(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 (%ld 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 { 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]; char data; // unbounded } pcx_t; static void LoadPCX ( const char *filename, unsigned char **pic, unsigned char **palette, uint32_t *width, uint32_t *height) { char* raw; pcx_t *pcx; int x, y; int len; int dataByte, runLength; unsigned char *out, *pix; int xmax, ymax; *pic = NULL; *palette = NULL; // // load the file // len = ri.FS_ReadFile(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, (unsigned char *)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 - (char *)pcx > len) { ri.Printf (PRINT_DEVELOPER, "PCX file %s was malformed", filename); ri.Free (*pic); *pic = NULL; } ri.FS_FreeFile (pcx); } static void LoadPCX32 ( const char *filename, unsigned char **pic, uint32_t *width, uint32_t *height) { unsigned char *palette; unsigned char *pic8; int i, c, p; unsigned char *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); }