void R_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 int row_stride; /* physical row width in output buffer */ unsigned int pixelcount, memcount; unsigned int sindex, dindex; byte *out; int len; union { byte *b; void *v; } 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. */ len = ri.FS_ReadFile ( ( char * ) filename, &fbuffer.v); if (!fbuffer.b || len < 0) { 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); cinfo.err->error_exit = R_JPGErrorExit; cinfo.err->output_message = R_JPGOutputMessage; /* Now we can initialize the JPEG decompression object. */ jpeg_create_decompress(&cinfo); /* Step 2: specify data source (eg, a file) */ jpeg_mem_src(&cinfo, fbuffer.b, len); /* 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 */ /* * Make sure it always converts images to RGB color space. This will * automatically convert 8-bit greyscale images to RGB as well. */ cinfo.out_color_space = JCS_RGB; /* 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 != 3 ) { // Free the memory to make sure we don't leak memory ri.FS_FreeFile (fbuffer.v); jpeg_destroy_decompress(&cinfo); ri.Error(ERR_DROP, "LoadJPG: %s has an invalid image format: %dx%d*4=%d, components: %d", 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; // Expand from RGB to RGBA sindex = pixelcount * cinfo.output_components; dindex = memcount; do { buf[--dindex] = 255; buf[--dindex] = buf[--sindex]; buf[--dindex] = buf[--sindex]; buf[--dindex] = buf[--sindex]; } while(sindex); *pic = out; /* Step 7: Finish decompression */ 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.v); /* 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! */ }
void R_LoadPCX ( const char *filename, byte **pic, int *width, int *height) { union { byte *b; void *v; } raw; byte *end; pcx_t *pcx; int len; unsigned char dataByte = 0, runLength = 0; byte *out, *pix; unsigned short w, h; byte *pic8; byte *palette; int i; unsigned size = 0; if (width) *width = 0; if (height) *height = 0; *pic = NULL; // // load the file // len = ri.FS_ReadFile( ( char * ) filename, &raw.v); if (!raw.b || len < 0) { return; } if((unsigned)len < sizeof(pcx_t)) { ri.Printf (PRINT_ALL, "PCX truncated: %s\n", filename); ri.FS_FreeFile (raw.v); return; } // // parse the PCX file // pcx = (pcx_t *)raw.b; end = raw.b+len; w = LittleShort(pcx->xmax)+1; h = LittleShort(pcx->ymax)+1; size = w*h; if (pcx->manufacturer != 0x0a || pcx->version != 5 || pcx->encoding != 1 || pcx->color_planes != 1 || pcx->bits_per_pixel != 8 || w >= 1024 || h >= 1024) { ri.Printf (PRINT_ALL, "Bad or unsupported pcx file %s (%dx%d@%d)\n", filename, w, h, pcx->bits_per_pixel); return; } pix = pic8 = ri.Malloc ( size ); raw.b = pcx->data; // FIXME: should use bytes_per_line but original q3 didn't do that either while(pix < pic8+size) { if(runLength > 0) { *pix++ = dataByte; --runLength; continue; } if(raw.b+1 > end) break; dataByte = *raw.b++; if((dataByte & 0xC0) == 0xC0) { if(raw.b+1 > end) break; runLength = dataByte & 0x3F; dataByte = *raw.b++; } else runLength = 1; } if(pix < pic8+size) { ri.Printf (PRINT_ALL, "PCX file truncated: %s\n", filename); ri.FS_FreeFile (pcx); ri.Free (pic8); } if (raw.b-(byte*)pcx >= end - (byte*)769 || end[-769] != 0x0c) { ri.Printf (PRINT_ALL, "PCX missing palette: %s\n", filename); ri.FS_FreeFile (pcx); ri.Free (pic8); return; } palette = end-768; pix = out = ri.Malloc(4 * size ); for (i = 0 ; i < size ; i++) { unsigned char p = pic8[i]; pix[0] = palette[p*3]; pix[1] = palette[p*3 + 1]; pix[2] = palette[p*3 + 2]; pix[3] = 255; pix += 4; } if (width) *width = w; if (height) *height = h; *pic = out; ri.FS_FreeFile (pcx); ri.Free (pic8); }
void R_LoadTGA ( const char *name, byte **pic, int *width, int *height) { unsigned columns, rows, numPixels; byte *pixbuf; int row, column; byte *buf_p; byte *end; union { byte *b; void *v; } buffer; 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, &buffer.v); if (!buffer.b || length < 0) { return; } if(length < 18) { ri.Error( ERR_DROP, "LoadTGA: header too short (%s)", name ); } buf_p = buffer.b; end = buffer.b + 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"); } if ( targa_header.colormap_type != 0 ) { ri.Error( ERR_DROP, "LoadTGA: colormaps not supported" ); } 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)"); } 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", 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)", 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)", 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'", 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)", 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)", 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'", 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)", 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'", 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.v); }
void R_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; union { byte *b; void *v; } buffer; 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, &buffer.v); if (!buffer.b || length < 0) { return; } if (length < 54) { ri.Error( ERR_DROP, "LoadBMP: header too short (%s)", name ); } buf_p = buffer.b; end = buffer.b + 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)", name ); Com_Memcpy( bmpHeader.palette, buf_p, sizeof( bmpHeader.palette ) ); buf_p += sizeof(bmpHeader.palette); } if (buffer.b + bmpHeader.bitmapDataOffset > end) { ri.Error( ERR_DROP, "LoadBMP: invalid offset value in header (%s)", name ); } buf_p = buffer.b + bmpHeader.bitmapDataOffset; if ( bmpHeader.id[0] != 'B' && bmpHeader.id[1] != 'M' ) { ri.Error( ERR_DROP, "LoadBMP: only Windows-style BMP files supported (%s)", name ); } if ( bmpHeader.fileSize != length ) { ri.Error( ERR_DROP, "LoadBMP: header size does not match file size (%u vs. %u) (%s)", bmpHeader.fileSize, length, name ); } if ( bmpHeader.compression != 0 ) { ri.Error( ERR_DROP, "LoadBMP: only uncompressed BMP files supported (%s)", name ); } if ( bmpHeader.bitsPerPixel < 8 ) { ri.Error( ERR_DROP, "LoadBMP: monochrome and 4-bit BMP files not supported (%s)", 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'", 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", name); } if(buf_p + numPixels*bmpHeader.bitsPerPixel/8 > end) { ri.Error (ERR_DROP, "LoadBMP: file truncated (%s)", 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.v ); }