//======================================================================= // Copyright XashXT Group 2007 © // image.c - loading textures //======================================================================= #include "launch.h" #include "image.h" #include "mathlib.h" // global image variables int image_width, image_height; byte image_num_layers = 1; // num layers in byte image_num_mips = 0; // build mipmaps uint image_type; // main type switcher uint image_flags; // additional image flags byte image_bits_count; // bits per RGBA size_t image_size; // image rgba size uint image_ptr; byte *image_palette; // palette pointer byte *image_rgba; // image pointer (see image_type for details) jpg_t jpg_file; // jpeg read struct // cubemap variables int cubemap_width, cubemap_height; int cubemap_num_sides; // how mach sides is loaded byte *image_cubemap; // cubemap pack uint cubemap_image_type; // shared image type char *suf[6] = {"ft", "bk", "rt", "lf", "up", "dn"}; #define LUMP_NORMAL 0 #define LUMP_TRANSPARENT 1 #define LUMP_DECAL 2 #define LUMP_QFONT 3 uint d_8toD1table[256]; uint d_8toQ1table[256]; uint d_8toQ2table[256]; uint d_8to24table[256]; uint *d_currentpal; bool d1palette_init = false; bool q1palette_init = false; bool q2palette_init = false; int d_rendermode = LUMP_NORMAL; //======================================================================= // IMGLIB COMMON TOOLS //======================================================================= void Image_Init( void ) { Sys.imagepool = Mem_AllocPool( "ImageLib Pool" ); } void Image_Shutdown( void ) { Mem_FreePool( &Sys.imagepool ); } bool Image_ValidSize( char *name ) { if(image_width > 4096 || image_height > 4096 || image_width <= 0 || image_height <= 0) { MsgWarn( "Image_ValidSize: (%s) image size out of range [%dx%d]\n", name, image_width, image_height ); return false; } return true; } void Image_GetPalette( byte *pal, uint *d_table ) { int i; byte rgba[4]; // setup palette switch( d_rendermode ) { case LUMP_DECAL: for(i = 0; i < 256; i++) { rgba[3] = pal[765]; rgba[2] = pal[766]; rgba[1] = pal[767]; rgba[0] = i; d_table[i] = BuffBigLong( rgba ); } break; case LUMP_TRANSPARENT: for (i = 0; i < 256; i++) { rgba[3] = pal[i*3+0]; rgba[2] = pal[i*3+1]; rgba[1] = pal[i*3+2]; rgba[0] = pal[i] == 255 ? pal[i] : 0xFF; d_table[i] = BuffBigLong( rgba ); } break; case LUMP_QFONT: for (i = 1; i < 256; i++) { rgba[3] = pal[i*3+0]; rgba[2] = pal[i*3+1]; rgba[1] = pal[i*3+2]; rgba[0] = 0xFF; d_table[i] = BuffBigLong( rgba ); } break; case LUMP_NORMAL: for (i = 0; i < 256; i++) { rgba[3] = pal[i*3+0]; rgba[2] = pal[i*3+1]; rgba[1] = pal[i*3+2]; rgba[0] = 0xFF; d_table[i] = BuffBigLong( rgba ); } break; } } void Image_GetD1Palette( void ) { d_rendermode = LUMP_NORMAL; if(!d1palette_init) { Image_GetPalette( palette_d1, d_8toD1table ); d_8toD1table[255] = 247; // 247 is transparent d1palette_init = true; } d_currentpal = d_8toD1table; } void Image_GetQ1Palette( void ) { d_rendermode = LUMP_NORMAL; if(!q1palette_init) { Image_GetPalette( palette_q1, d_8toQ1table ); d_8toQ1table[255] = 0; // 255 is transparent q1palette_init = true; } d_currentpal = d_8toQ1table; } void Image_GetQ2Palette( void ) { d_rendermode = LUMP_NORMAL; if(!q2palette_init) { Image_GetPalette( palette_q2, d_8toQ2table ); d_8toQ2table[255] &= LittleLong(0xffffff); q2palette_init = true; } d_currentpal = d_8toQ2table; } void Image_GetPalettePCX( byte *pal ) { d_rendermode = LUMP_NORMAL; if(pal) { Image_GetPalette( pal, d_8to24table ); d_8to24table[255] &= LittleLong(0xffffff); d_currentpal = d_8to24table; } else Image_GetQ2Palette(); } void Image_GetPaletteWAD( byte *pal, int rendermode ) { d_rendermode = rendermode; if( pal ) { Image_GetPalette( pal, d_8to24table ); d_8to24table[255] &= LittleLong(0xffffff); d_currentpal = d_8to24table; } else if(rendermode == LUMP_QFONT) { // quake1 base palette and font palette have some diferences Image_GetPalette( palette_q1, d_8to24table ); d_8to24table[0] = 0; d_currentpal = d_8to24table; } else Image_GetQ1Palette(); } /* ============ Image_Copy8bitRGBA NOTE: must call Image_GetQ2Palette or Image_GetQ1Palette before used ============ */ bool Image_Copy8bitRGBA(const byte *in, byte *out, int pixels) { int *iout = (int *)out; if(!d_currentpal) { MsgDev(D_ERROR,"Image_Copy8bitRGBA: no palette set\n"); return false; } while (pixels >= 8) { iout[0] = d_currentpal[in[0]]; iout[1] = d_currentpal[in[1]]; iout[2] = d_currentpal[in[2]]; iout[3] = d_currentpal[in[3]]; iout[4] = d_currentpal[in[4]]; iout[5] = d_currentpal[in[5]]; iout[6] = d_currentpal[in[6]]; iout[7] = d_currentpal[in[7]]; in += 8; iout += 8; pixels -= 8; } if (pixels & 4) { iout[0] = d_currentpal[in[0]]; iout[1] = d_currentpal[in[1]]; iout[2] = d_currentpal[in[2]]; iout[3] = d_currentpal[in[3]]; in += 4; iout += 4; } if (pixels & 2) { iout[0] = d_currentpal[in[0]]; iout[1] = d_currentpal[in[1]]; in += 2; iout += 2; } if (pixels & 1) iout[0] = d_currentpal[in[0]]; return true; } void Image_RoundDimensions(int *scaled_width, int *scaled_height) { int width, height; for( width = 1; width < *scaled_width; width <<= 1 ); for( height = 1; height < *scaled_height; height <<= 1 ); *scaled_width = bound(1, width, 4096 ); *scaled_height = bound(1, height, 4096 ); } byte *Image_Resample(uint *in, int inwidth, int inheight, int outwidth, int outheight, int in_type ) { int i, j; uint frac, fracstep; uint *inrow1, *inrow2; byte *pix1, *pix2, *pix3, *pix4; uint *out, *buf, p1[4096], p2[4096]; // check for buffers if(!in) return NULL; // nothing to resample ? if (inwidth == outwidth && inheight == outheight) return (byte *)in; // can't resample compressed formats if(in_type != PF_RGBA_32) return NULL; // malloc new buffer out = buf = (uint *)Mem_Alloc( Sys.imagepool, outwidth * outheight * 4 ); fracstep = inwidth * 0x10000 / outwidth; frac = fracstep>>2; for( i = 0; i < outwidth; i++) { p1[i] = 4 * (frac>>16); frac += fracstep; } frac = 3 * (fracstep>>2); for( i = 0; i < outwidth; i++) { p2[i] = 4 * (frac>>16); frac += fracstep; } for (i = 0; i < outheight; i++,buf += outwidth) { inrow1 = in + inwidth * (int)((i + 0.25) * inheight / outheight); inrow2 = in + inwidth * (int)((i + 0.75) * inheight / outheight); frac = fracstep>>1; for (j = 0; j < outwidth; j++) { pix1 = (byte *)inrow1 + p1[j]; pix2 = (byte *)inrow1 + p2[j]; pix3 = (byte *)inrow2 + p1[j]; pix4 = (byte *)inrow2 + p2[j]; ((byte *)(buf+j))[0] = (pix1[0] + pix2[0] + pix3[0] + pix4[0])>>2; ((byte *)(buf+j))[1] = (pix1[1] + pix2[1] + pix3[1] + pix4[1])>>2; ((byte *)(buf+j))[2] = (pix1[2] + pix2[2] + pix3[2] + pix4[2])>>2; ((byte *)(buf+j))[3] = (pix1[3] + pix2[3] + pix3[3] + pix4[3])>>2; } } return (byte *)out; } bool Image_Processing( const char *name, rgbdata_t **pix ) { int w, h; rgbdata_t *image = *pix; byte *out; char width[4], height[4]; // check for buffers if(!image || !image->buffer) return false; w = image->width; h = image->height; if(FS_GetParmFromCmdLine("-w", width ) && FS_GetParmFromCmdLine("-h", height )) { // custom size w = bound(4, atoi(width), 1024 ); // maxwidth 1024 h = bound(4, atoi(height), 1024); // maxheight 1024 } else Image_RoundDimensions( &w, &h ); // auto detect new size out = Image_Resample((uint *)image->buffer, image->width, image->height, w, h, image->type ); if(out != image->buffer) { MsgDev(D_INFO,"Resampling %s from[%d x %d] to[%d x %d]\n",name, image->width, image->height, w, h ); Mem_Move( Sys.imagepool, &image->buffer, out, w * h * 4 );// update image->buffer image->width = w,image->height = h; *pix = image; return true; } return false; } /* ============== LoadWAL ============== */ bool LoadWAL( char *name, char *buffer, int filesize ) { wal_t wal; int pixels, ofs[4], mipsize;// FIXME, adding four mips ? int i, flags, value, contents; // wal additional parms if (filesize < (int)sizeof(wal)) { MsgWarn("LoadWAL: file (%s) have invalid size\n", name ); return false; } Mem_Copy(&wal, buffer, sizeof(wal)); flags = LittleLong(wal.flags); value = LittleLong(wal.value); contents = LittleLong(wal.contents); image_width = LittleLong(wal.width); image_height = LittleLong(wal.height); for(i = 0; i < 4; i++) ofs[i] = LittleLong(wal.offsets[i]); if(!Image_ValidSize( name )) return false; pixels = image_width * image_height; mipsize = (int)sizeof(wal) + ofs[0] + pixels; if( pixels > 256 && filesize < mipsize ) { // note: wal's with dimensions < 32 have invalid sizes. MsgWarn("LoadWAL: file (%s) have invalid size\n", name ); return false; } image_num_layers = 1; image_type = PF_RGBA_32; Image_GetQ2Palette(); // hardcoded return FS_AddMipmapToPack( buffer + ofs[0], image_width, image_height ); } /* ============ LoadLMP ============ */ bool LoadLMP( char *name, char *buffer, int filesize ) { lmp_t lmp; byte *fin, *pal; int i, pixels; int rendermode; if (filesize < (int)sizeof(lmp)) { MsgWarn("LoadLMP: file (%s) have invalid size\n", name ); return false; } fin = buffer; // great hack from id software if(!com_stricmp( name, "conchars" )) { image_width = image_height = 128; pixels = image_width * image_height; image_flags |= IMAGE_HAS_ALPHA; rendermode = LUMP_QFONT; } else { Mem_Copy(&lmp, fin, sizeof(lmp)); image_width = LittleLong(lmp.width); image_height = LittleLong(lmp.height); fin += sizeof(lmp); pixels = image_width * image_height; if (filesize < (int)sizeof(lmp) + pixels) { MsgWarn("LoadLMP: file (%s) have invalid size %d\n", name, filesize ); return false; } rendermode = LUMP_NORMAL; } if(!Image_ValidSize( name )) return false; image_size = pixels * 4; image_num_mips = 1; image_rgba = (byte *)Mem_Alloc(Sys.imagepool, image_size ); image_num_layers = 1; image_type = PF_RGBA_32; // try to find transparent pixels for(i = 0; i < pixels; i++) { if(fin[i] != 255) continue; image_flags |= IMAGE_HAS_ALPHA; rendermode = LUMP_TRANSPARENT; break; // found } // half-life 1.0.0.1 lmp version with palette if( filesize == (int)sizeof(lmp) + pixels + sizeof(short) + 768) { int numcolors; pal = fin + pixels; numcolors = BuffLittleShort( pal ), pal += 2; if( numcolors != 256 ) pal = NULL; // corrupted lump ? } else pal = NULL; Image_GetPaletteWAD( pal, rendermode ); Image_Copy8bitRGBA( fin, image_rgba, pixels ); return true; } /* ============ LoadMIP ============ */ bool LoadMIP( char *name, char *buffer, int filesize ) { mip_t mip; int i, ofs[4], mipsize; if (filesize < (int)sizeof(mip)) { MsgWarn("LoadMIP: file (%s) have invalid size\n", name ); return false; } Mem_Copy(&mip, buffer, sizeof(mip)); image_width = LittleLong(mip.width); image_height = LittleLong(mip.height); for(i = 0; i < 4; i++) ofs[i] = LittleLong(mip.offsets[i]); if(!Image_ValidSize( name )) return false; mipsize = image_width * image_height; if (filesize < (int)sizeof(mip) + mipsize) { MsgWarn("LoadMIP: file (%s) have invalid size\n", name ); return false; } image_num_layers = 1; image_type = PF_RGBA_32; Image_GetQ1Palette();// hardcoded return FS_AddMipmapToPack( buffer + ofs[0], image_width, image_height ); } /* ============ LoadPIC ============ */ bool LoadPIC( char *name, char *buffer, int filesize ) { qpic_t pic; byte *pal, *fin; int numcolors; int pixels; if(filesize < (int)sizeof(pic)) { MsgWarn("LoadPIC: file (%s) have invalid size\n", name ); return false; } Mem_Copy(&pic, buffer, sizeof(pic)); image_width = LittleShort(pic.width); image_height = LittleShort(pic.height); if(!Image_ValidSize( name )) return false; pixels = image_width * image_height; fin = buffer + sizeof(pic) - 4; image_num_layers = 1; image_type = PF_RGBA_32; // half-life qlmpy version with palette if( filesize == (int)sizeof(pic) - 2 + pixels + sizeof(short) + 768) { pal = fin + pixels; numcolors = BuffLittleShort(pal); if(numcolors != 256) { MsgWarn("LoadPIC: file (%s) have invalid palette size %d\n", name, numcolors ); return false; } pal += 2; // skip colorsize } else pal = NULL; // quake1 pic if(fin[0] == 255) image_flags |= IMAGE_HAS_ALPHA; Image_GetPaletteWAD( pal, LUMP_NORMAL ); return FS_AddMipmapToPack( fin, image_width, image_height ); } bool LoadTEST( char *name, char *buffer, int filesize ) { return false; } /* ============ LoadFNT ============ */ bool LoadFNT( char *name, char *buffer, int filesize ) { qfont_t font; byte *pal, *fin; int i, pixels, fullsize; int numcolors; if(filesize < (int)sizeof(font)) { MsgWarn("LoadFNT: file (%s) have invalid size\n", name ); return false; } Mem_Copy(&font, buffer, sizeof(font)); // swap lumps font.width = LittleShort(font.width); font.height = LittleShort(font.height); font.rowcount = LittleShort(font.rowcount); font.rowheight = LittleShort(font.rowheight); for(i = 0; i < 256; i++) { font.fontinfo[i].startoffset = LittleShort( font.fontinfo[i].startoffset ); font.fontinfo[i].charwidth = LittleShort( font.fontinfo[i].charwidth ); } // last sixty four bytes - what the hell ???? fullsize = sizeof(qfont_t)-4+(128 * font.width * QCHAR_WIDTH) + sizeof(short) + 768 + 64; Msg("width %d, height %d\n", font.width, font.height ); if( fullsize != filesize ) { // probably it's "creditsfont" or "conchars" MsgWarn("LoadFNT: (%s) it's not a qfont_t structure\n", name ); return LoadTEST( name, buffer, filesize ); } // setup font dimensions image_width = font.width * QCHAR_WIDTH; image_height = font.height; image_num_layers = 1; image_type = PF_RGBA_32; if(!Image_ValidSize( name )) return false; pixels = ( image_width * image_height ); fin = buffer + sizeof(font) - 4, pal = fin + pixels; numcolors = BuffLittleShort( pal ), pal += 2; image_flags |= IMAGE_HAS_ALPHA; // fonts always have transparency if(numcolors != 768) { MsgWarn("LoadFNT: file (%s) have invalid palette size %d\n", name, numcolors ); return false; } // FIXME: convert this to quake-style conchars ? Image_GetPaletteWAD( pal, LUMP_QFONT ); return FS_AddMipmapToPack( fin, image_width, image_height ); } /* ============ LoadWAD ============ */ bool LoadWAD( char *name, char *buffer, int filesize ) { mip_t mip; byte *pal; int ofs[4], rendermode; int i, numcolors; if (filesize < (int)sizeof(mip)) { MsgWarn("LoadWAD_3: file (%s) have invalid size\n", name ); return false; } Mem_Copy(&mip, buffer, sizeof(mip)); image_width = LittleLong(mip.width); image_height = LittleLong(mip.height); for(i = 0; i < 4; i++) ofs[i] = LittleLong(mip.offsets[i]); if(!Image_ValidSize( name )) return false; pal = buffer + mip.offsets[0] + (((image_width * image_height) * 85)>>6); numcolors = BuffLittleShort( pal ), pal += 2; // skip colorsize if(numcolors != 256) { MsgWarn("LoadWAD_3: file (%s) have invalid palette size %d\n", name, numcolors ); return false; } image_num_layers = 1; image_type = PF_RGBA_32; // always exctracted to 32-bit buffer // detect rendermode if( name[0] == '{' ) { // note: i trying determine transparent miptex by last color in palette // e.g. valve used in their textures blue color (0,0,255) // other cases for red (255,0,0) ang green (0,255,0) colors, // otherwise - it will use decal palette with ugly results. ughgrrr.. if(pal[255*3+0] == 0 && pal[255*3+1] == 0 && pal[255*3+2] == 255 && pal[255*3+3] == 0) rendermode = LUMP_TRANSPARENT; else if(pal[255*3+0] == 0 && pal[255*3+1] == 255 && pal[255*3+2] == 0 && pal[255*3+3] == 0) rendermode = LUMP_TRANSPARENT; else if(pal[255*3+0] == 255 && pal[255*3+1] == 0 && pal[255*3+2] == 0 && pal[255*3+3] == 0) rendermode = LUMP_TRANSPARENT; else rendermode = LUMP_DECAL; image_flags |= IMAGE_HAS_ALPHA; } else rendermode = LUMP_NORMAL; Image_GetPaletteWAD( pal, rendermode ); return FS_AddMipmapToPack( buffer + mip.offsets[0], image_width, image_height ); } /* ============ LoadPCX ============ */ bool LoadPCX( char *name, char *buffer, int filesize ) { pcx_t pcx; uint *trans; int s, i, p, x, y, x2, dataByte; byte *pix, *pbuf, *palette, *fin, *enddata; fin = buffer; Mem_Copy(&pcx, fin, sizeof(pcx)); fin += sizeof(pcx); // probably it's not pcx file if (pcx.manufacturer != 0x0a || pcx.version != 5 || pcx.encoding != 1 ) return false; if (filesize < (int)sizeof(pcx) + 768) { MsgWarn("LoadPCX: file (%s) have invalid size\n", name ); return false; } pcx.xmax = LittleShort (pcx.xmax); pcx.xmin = LittleShort (pcx.xmin); pcx.ymax = LittleShort (pcx.ymax); pcx.ymin = LittleShort (pcx.ymin); pcx.hres = LittleShort (pcx.hres); pcx.vres = LittleShort (pcx.vres); pcx.bytes_per_line = LittleShort (pcx.bytes_per_line); pcx.palette_type = LittleShort (pcx.palette_type); image_width = pcx.xmax + 1 - pcx.xmin; image_height = pcx.ymax + 1 - pcx.ymin; if( pcx.bits_per_pixel != 8 || pcx.manufacturer != 0x0a || pcx.version != 5 || pcx.encoding != 1) { MsgWarn("LoadPCX: (%s) have illegal pixel size '%d'\n", name, pcx.bits_per_pixel ); return false; } if(!Image_ValidSize( name )) return false; palette = buffer + filesize - 768; image_num_layers = 1; image_num_mips = 1; image_type = PF_RGBA_32; if(palette) { image_palette = Mem_Alloc( Sys.imagepool, 768 ); Mem_Copy( image_palette, palette, 768 ); } s = image_width * image_height; image_size = image_width * image_height * 4; pbuf = (byte *)Mem_Alloc( Sys.imagepool, s ); image_rgba = (byte *)Mem_Alloc( Sys.imagepool, image_size ); trans = (uint *)image_rgba; enddata = palette; for (y = 0; y < image_height && fin < enddata; y++) { pix = pbuf + y * image_width; for (x = 0; x < image_width && fin < enddata;) { dataByte = *fin++; if(dataByte >= 0xC0) { if (fin >= enddata) break; x2 = x + (dataByte & 0x3F); dataByte = *fin++; if (x2 > image_width) x2 = image_width; // technically an error while(x < x2) pix[x++] = dataByte; } else pix[x++] = dataByte; } // the number of bytes per line is always forced to an even number fin += pcx.bytes_per_line - image_width; while(x < image_width) pix[x++] = 0; } Image_GetPalettePCX( palette ); // convert to rgba for (i = 0; i < s; i++) { p = pbuf[i]; if (p == 255) { image_flags |= IMAGE_HAS_ALPHA; // found alpha channel ((byte *)&trans[i])[0] = ((byte *)&d_currentpal[0])[0]; ((byte *)&trans[i])[1] = ((byte *)&d_currentpal[0])[1]; ((byte *)&trans[i])[2] = ((byte *)&d_currentpal[0])[2]; ((byte *)&trans[i])[3] = ((byte *)&d_currentpal[p])[3]; } else trans[i] = d_currentpal[p]; } Mem_Free( pbuf ); // free compressed image return true; } /* ============= LoadTGA ============= */ bool LoadTGA( char *name, char *buffer, int filesize ) { int x, y, pix_inc, row_inc; int red, green, blue, alpha; int runlen, alphabits; byte *pixbuf, *p; const byte *fin, *enddata; tga_t targa_header; byte palette[256*4]; if (filesize < 19) return false; fin = buffer; enddata = fin + filesize; targa_header.id_length = *fin++; targa_header.colormap_type = *fin++; targa_header.image_type = *fin++; targa_header.colormap_index = BuffLittleShort( fin ); fin += 2; targa_header.colormap_length = BuffLittleShort( fin ); fin += 2; targa_header.colormap_size = *fin++; targa_header.x_origin = BuffLittleShort( fin ); fin += 2; targa_header.y_origin = BuffLittleShort( fin ); fin += 2; targa_header.width = image_width = BuffLittleShort( fin ); fin += 2; targa_header.height = image_height = BuffLittleShort( fin );fin += 2; if(!Image_ValidSize( name )) return false; image_num_layers = 1; image_num_mips = 1; image_type = PF_RGBA_32; //always exctracted to 32-bit buffer targa_header.pixel_size = *fin++; targa_header.attributes = *fin++; // end of header // skip TARGA image comment (usually 0 bytes) fin += targa_header.id_length; // read/skip the colormap if present (note: according to the TARGA spec it // can be present even on truecolor or greyscale images, just not used by // the image data) if (targa_header.colormap_type) { if (targa_header.colormap_length > 256) { MsgWarn("LoadTGA: (%s) have unsupported colormap type ( more than 256 bytes)\n", name ); return false; } if (targa_header.colormap_index) { MsgWarn("LoadTGA: (%s) have unspported indexed colormap\n", name ); return false; } image_palette = Mem_Alloc(Sys.imagepool, 256*4 ); if (targa_header.colormap_size == 24) { for (x = 0;x < targa_header.colormap_length;x++) { palette[x*4+2] = *fin++; palette[x*4+1] = *fin++; palette[x*4+0] = *fin++; palette[x*4+3] = 0xff; } } else if (targa_header.colormap_size == 32) { for (x = 0;x < targa_header.colormap_length;x++) { palette[x*4+2] = *fin++; palette[x*4+1] = *fin++; palette[x*4+0] = *fin++; palette[x*4+3] = *fin++; } } else { MsgWarn("LoadTGA: (%s) have unsupported colormap size (valid is 32 or 24 bit)\n", name ); return false; } Mem_Copy(image_palette, palette, 256 * 4 ); //copy palette } // check our pixel_size restrictions according to image_type switch (targa_header.image_type & ~8) { case 2: if (targa_header.pixel_size != 24 && targa_header.pixel_size != 32) { MsgWarn("LoadTGA: (%s) have unsupported pixel size '%d', for type '%d'\n", name, targa_header.pixel_size, targa_header.image_type ); return false; } break; case 3: // set up a palette to make the loader easier for (x = 0; x < 256; x++) { palette[x*4+2] = x; palette[x*4+1] = x; palette[x*4+0] = x; palette[x*4+3] = 255; } // fall through to colormap case case 1: if (targa_header.pixel_size != 8) { MsgWarn("LoadTGA: (%s) have unsupported pixel size '%d', for type '%d'\n", name, targa_header.pixel_size, targa_header.image_type ); return false; } break; default: MsgWarn("LoadTGA: (%s) is unsupported image type '%i'\n", name, targa_header.image_type); return false; } if (targa_header.attributes & 0x10) { MsgWarn("LoadTGA: (%s): top right and bottom right origin are not supported\n", name ); return false; } // number of attribute bits per pixel, we only support 0 or 8 alphabits = targa_header.attributes & 0x0F; if (alphabits != 8 && alphabits != 0) { MsgWarn("LoadTGA: (%s) have invalid attributes '%i'\n", name, alphabits ); return false; } image_flags |= alphabits ? IMAGE_HAS_ALPHA : 0; image_size = image_width * image_height * 4; image_rgba = Mem_Alloc( Sys.imagepool, image_size ); // If bit 5 of attributes isn't set, the image has been stored from bottom to top if ((targa_header.attributes & 0x20) == 0) { pixbuf = image_rgba + (image_height - 1)*image_width*4; row_inc = -image_width*4*2; } else { pixbuf = image_rgba; row_inc = 0; } x = y = 0; red = green = blue = alpha = 255; pix_inc = 1; if ((targa_header.image_type & ~8) == 2) pix_inc = targa_header.pixel_size / 8; switch (targa_header.image_type) { case 1: // colormapped, uncompressed case 3: // greyscale, uncompressed if (fin + image_width * image_height * pix_inc > enddata) break; for (y = 0;y < image_height;y++, pixbuf += row_inc) { for (x = 0;x < image_width;x++) { p = palette + *fin++ * 4; *pixbuf++ = p[0]; *pixbuf++ = p[1]; *pixbuf++ = p[2]; *pixbuf++ = p[3]; } } break; case 2: // BGR or BGRA, uncompressed if (fin + image_width * image_height * pix_inc > enddata) break; if (targa_header.pixel_size == 32 && alphabits) { for (y = 0;y < image_height;y++, pixbuf += row_inc) { for (x = 0;x < image_width;x++, fin += pix_inc) { *pixbuf++ = fin[2]; *pixbuf++ = fin[1]; *pixbuf++ = fin[0]; *pixbuf++ = fin[3]; } } } else //24 bits { for (y = 0;y < image_height; y++, pixbuf += row_inc) { for (x = 0;x < image_width; x++, fin += pix_inc) { *pixbuf++ = fin[2]; *pixbuf++ = fin[1]; *pixbuf++ = fin[0]; *pixbuf++ = 255; } } } break; case 9: // colormapped, RLE case 11: // greyscale, RLE for (y = 0; y < image_height; y++, pixbuf += row_inc) { for (x = 0;x < image_width;) { if (fin >= enddata) break; // error - truncated file runlen = *fin++; if (runlen & 0x80) { // RLE - all pixels the same color runlen += 1 - 0x80; if (fin + pix_inc > enddata) break; // error - truncated file if (x + runlen > image_width) break; // error - line exceeds width p = palette + *fin++ * 4; red = p[0]; green = p[1]; blue = p[2]; alpha = p[3]; for (;runlen--; x++) { *pixbuf++ = red; *pixbuf++ = green; *pixbuf++ = blue; *pixbuf++ = alpha; } } else { // uncompressed - all pixels different color runlen++; if (fin + pix_inc * runlen > enddata) break; // error - truncated file if (x + runlen > image_width) break; // error - line exceeds width for (;runlen--; x++) { p = palette + *fin++ * 4; *pixbuf++ = p[0]; *pixbuf++ = p[1]; *pixbuf++ = p[2]; *pixbuf++ = p[3]; } } } } break; case 10: // BGR or BGRA, RLE if (targa_header.pixel_size == 32 && alphabits) { for (y = 0;y < image_height;y++, pixbuf += row_inc) { for (x = 0;x < image_width;) { if (fin >= enddata) break; // error - truncated file runlen = *fin++; if (runlen & 0x80) { // RLE - all pixels the same color runlen += 1 - 0x80; if (fin + pix_inc > enddata) break; // error - truncated file if (x + runlen > image_width) break; // error - line exceeds width red = fin[2]; green = fin[1]; blue = fin[0]; alpha = fin[3]; fin += pix_inc; for (;runlen--;x++) { *pixbuf++ = red; *pixbuf++ = green; *pixbuf++ = blue; *pixbuf++ = alpha; } } else { // uncompressed - all pixels different color runlen++; if (fin + pix_inc * runlen > enddata) break; // error - truncated file if (x + runlen > image_width) break; // error - line exceeds width for (;runlen--;x++, fin += pix_inc) { *pixbuf++ = fin[2]; *pixbuf++ = fin[1]; *pixbuf++ = fin[0]; *pixbuf++ = fin[3]; } } } } } else { for (y = 0;y < image_height;y++, pixbuf += row_inc) { for (x = 0;x < image_width;) { if (fin >= enddata) break; // error - truncated file runlen = *fin++; if (runlen & 0x80) { // RLE - all pixels the same color runlen += 1 - 0x80; if (fin + pix_inc > enddata) break; // error - truncated file if (x + runlen > image_width) break; // error - line exceeds width red = fin[2]; green = fin[1]; blue = fin[0]; alpha = 255; fin += pix_inc; for (;runlen--;x++) { *pixbuf++ = red; *pixbuf++ = green; *pixbuf++ = blue; *pixbuf++ = alpha; } } else { // uncompressed - all pixels different color runlen++; if (fin + pix_inc * runlen > enddata) break; // error - truncated file if (x + runlen > image_width) break; // error - line exceeds width for (;runlen--;x++, fin += pix_inc) { *pixbuf++ = fin[2]; *pixbuf++ = fin[1]; *pixbuf++ = fin[0]; *pixbuf++ = 255; } } } } } break; default: break; // unknown image_type } return true; } /* ============= LoadDDS ============= */ uint dds_get_linear_size( int width, int height, int depth, int rgbcount ) { uint i, BlockSize = 0; int block, bpp; // right calcualte blocksize for(i = 0; i < PF_TOTALCOUNT; i++) { if(image_type == PFDesc[i].format) { block = PFDesc[i].block; bpp = PFDesc[i].bpp; break; } } if(i != PF_TOTALCOUNT) //make sure what match found { if(block == 0) BlockSize = width * height * bpp; else if(block > 0) BlockSize = ((width + 3)/4) * ((height + 3)/4) * depth * block; else if(block < 0 && rgbcount > 0) BlockSize = width * height * depth * rgbcount; else BlockSize = width * height * abs(block); } return BlockSize; } void dds_get_pixelformat( dds_t *hdr ) { uint bits = hdr->dsPixelFormat.dwRGBBitCount; // All volume textures I've seem so far didn't have the DDS_COMPLEX flag set, // even though this is normally required. But because noone does set it, // also read images without it (TODO: check file size for 3d texture?) if (!(hdr->dsCaps.dwCaps2 & DDS_VOLUME)) hdr->dwDepth = 1; if(hdr->dsPixelFormat.dwFlags & DDS_ALPHA) image_flags |= IMAGE_HAS_ALPHA; if (hdr->dsPixelFormat.dwFlags & DDS_FOURCC) { switch (hdr->dsPixelFormat.dwFourCC) { case TYPE_DXT1: image_type = PF_DXT1; break; case TYPE_DXT2: image_type = PF_DXT2; break; case TYPE_DXT3: image_type = PF_DXT3; break; case TYPE_DXT4: image_type = PF_DXT4; break; case TYPE_DXT5: image_type = PF_DXT5; break; case TYPE_ATI1: image_type = PF_ATI1N; break; case TYPE_ATI2: image_type = PF_ATI2N; break; case TYPE_RXGB: image_type = PF_RXGB; break; case TYPE_$: image_type = PF_ABGR_64; break; default: image_type = PF_UNKNOWN; break; } } else { // This dds texture isn't compressed so write out ARGB or luminance format if (hdr->dsPixelFormat.dwFlags & DDS_LUMINANCE) { if (hdr->dsPixelFormat.dwFlags & DDS_ALPHAPIXELS) image_type = PF_LUMINANCE_ALPHA; else if(hdr->dsPixelFormat.dwRGBBitCount == 16 && hdr->dsPixelFormat.dwRBitMask == 0xFFFF) image_type = PF_LUMINANCE_16; else image_type = PF_LUMINANCE; } else { if( bits == 32) image_type = PF_ABGR_64; else image_type = PF_ARGB_32; } } // setup additional flags if( hdr->dsCaps.dwCaps1 & DDS_COMPLEX && hdr->dsCaps.dwCaps2 & DDS_CUBEMAP) { image_flags |= IMAGE_CUBEMAP | IMAGE_CUBEMAP_FLIP; } if(hdr->dsPixelFormat.dwFlags & DDS_ALPHAPIXELS) { image_flags |= IMAGE_HAS_ALPHA; } if(image_type == TYPE_DXT2 || image_type == TYPE_DXT4) image_flags |= IMAGE_PREMULT; if(hdr->dwFlags & DDS_MIPMAPCOUNT) image_num_mips = hdr->dwMipMapCount; else image_num_mips = 1; if(image_type == PF_ARGB_32 || image_type == PF_LUMINANCE || image_type == PF_LUMINANCE_16 || image_type == PF_LUMINANCE_ALPHA) { //store RGBA mask into one block, and get palette pointer byte *tmp = image_palette = Mem_Alloc( Sys.imagepool, sizeof(uint) * 4 ); Mem_Copy( tmp, &hdr->dsPixelFormat.dwRBitMask, sizeof(uint)); tmp += 4; Mem_Copy( tmp, &hdr->dsPixelFormat.dwGBitMask, sizeof(uint)); tmp += 4; Mem_Copy( tmp, &hdr->dsPixelFormat.dwBBitMask, sizeof(uint)); tmp += 4; Mem_Copy( tmp, &hdr->dsPixelFormat.dwABitMask, sizeof(uint)); tmp += 4; } } void dds_addjust_volume_texture( dds_t *hdr ) { uint bits; if (hdr->dwDepth <= 1) return; bits = hdr->dsPixelFormat.dwRGBBitCount / 8; hdr->dwFlags |= DDS_LINEARSIZE; hdr->dwLinearSize = dds_get_linear_size( hdr->dwWidth, hdr->dwHeight, hdr->dwDepth, bits ); } uint dds_calc_mipmap_size( dds_t *hdr ) { uint buffsize = 0; int w = hdr->dwWidth; int h = hdr->dwHeight; int d = hdr->dwDepth; int i, mipsize = 0; int bits = hdr->dsPixelFormat.dwRGBBitCount / 8; // now correct buffer size for( i = 0; i < image_num_mips; i++, buffsize += mipsize ) { mipsize = dds_get_linear_size( w, h, d, bits ); w = (w+1)>>1, h = (h+1)>>1, d = (d+1)>>1; } return buffsize; } uint dds_calc_size( char *name, dds_t *hdr, uint filesize ) { uint buffsize = 0; int w = image_width; int h = image_height; int d = image_num_layers; int bits = hdr->dsPixelFormat.dwRGBBitCount / 8; if(hdr->dsCaps.dwCaps2 & DDS_CUBEMAP) { // cubemap w*h always match for all sides buffsize = dds_calc_mipmap_size( hdr ) * 6; } else if(hdr->dwFlags & DDS_MIPMAPCOUNT) { // if mipcount > 1 buffsize = dds_calc_mipmap_size( hdr ); } else if(hdr->dwFlags & (DDS_LINEARSIZE | DDS_PITCH)) { // just in case (no need, really) buffsize = hdr->dwLinearSize; } else { // pretty solution for microsoft bug buffsize = dds_calc_mipmap_size( hdr ); } if(filesize != buffsize) // main check { MsgWarn("LoadDDS: (%s) probably corrupted(%i should be %i)\n", name, buffsize, filesize ); return false; } return buffsize; } bool LoadDDS( char *name, char *buffer, int filesize ) { dds_t header; byte *fin; uint i; fin = buffer; // swap header header.dwIdent = BuffLittleLong(fin); fin += 4; header.dwSize = BuffLittleLong(fin); fin += 4; header.dwFlags = BuffLittleLong(fin); fin += 4; header.dwHeight = BuffLittleLong(fin); fin += 4; header.dwWidth = BuffLittleLong(fin); fin += 4; header.dwLinearSize = BuffLittleLong(fin); fin += 4; header.dwDepth = BuffLittleLong(fin); fin += 4; header.dwMipMapCount = BuffLittleLong(fin); fin += 4; header.dwAlphaBitDepth = BuffLittleLong(fin); fin += 4; // skip unused stuff for (i = 0; i < 10; i++) { header.dwReserved1[i] = BuffLittleLong(fin); fin += 4; } // pixel format header.dsPixelFormat.dwSize = BuffLittleLong(fin); fin += 4; header.dsPixelFormat.dwFlags = BuffLittleLong(fin); fin += 4; header.dsPixelFormat.dwFourCC = BuffLittleLong(fin); fin += 4; header.dsPixelFormat.dwRGBBitCount = BuffLittleLong(fin); fin += 4; header.dsPixelFormat.dwRBitMask = BuffLittleLong(fin); fin += 4; header.dsPixelFormat.dwGBitMask = BuffLittleLong(fin); fin += 4; header.dsPixelFormat.dwBBitMask = BuffLittleLong(fin); fin += 4; header.dsPixelFormat.dwABitMask = BuffLittleLong(fin); fin += 4; // caps header.dsCaps.dwCaps1 = BuffLittleLong(fin); fin += 4; header.dsCaps.dwCaps2 = BuffLittleLong(fin); fin += 4; header.dsCaps.dwCaps3 = BuffLittleLong(fin); fin += 4; header.dsCaps.dwCaps4 = BuffLittleLong(fin); fin += 4; header.dwTextureStage = BuffLittleLong(fin); fin += 4; if(header.dwIdent != DDSHEADER) return false; // it's not a dds file, just skip it if(header.dwSize != sizeof(dds_t) - 4 ) // size of the structure (minus MagicNum) { MsgWarn("LoadDDS: (%s) have corrupt header\n", name ); return false; } if(header.dsPixelFormat.dwSize != sizeof(dds_pixf_t)) // size of the structure { MsgWarn("LoadDDS: (%s) have corrupt pixelformat header\n", name ); return false; } image_width = header.dwWidth; image_height = header.dwHeight; image_bits_count = header.dsPixelFormat.dwRGBBitCount; if(header.dwFlags & DDS_DEPTH) image_num_layers = header.dwDepth; if(!Image_ValidSize( name )) return false; dds_get_pixelformat( &header );// and image type too :) dds_addjust_volume_texture( &header ); if (image_type == PF_UNKNOWN) { MsgWarn("LoadDDS: (%s) have unsupported compression type\n", name ); return false; //unknown type } image_size = dds_calc_size( name, &header, filesize - 128 ); if(image_size == 0) return false; // just in case // dds files will be uncompressed on a render. requires minimal of info for set this image_rgba = Mem_Alloc( Sys.imagepool, image_size ); Mem_Copy( image_rgba, fin, image_size ); return true; } /* ============= LoadJPG ============= */ int jpeg_read_byte( void ) { // read byte jpg_file.curbyte = *jpg_file.buffer++; jpg_file.curbit = 0; return jpg_file.curbyte; } int jpeg_read_word( void ) { // read word word i = BuffLittleShort( jpg_file.buffer); i = ((i << 8) & 0xFF00) + ((i >> 8) & 0x00FF); jpg_file.buffer += 2; return i; } int jpeg_read_bit( void ) { // read bit register int i; if(jpg_file.curbit == 0) { jpeg_read_byte(); if(jpg_file.curbyte == 0xFF) { while(jpg_file.curbyte == 0xFF) jpeg_read_byte(); if(jpg_file.curbyte >= 0xD0 && jpg_file.curbyte <= 0xD7) memset(jpg_file.dc, 0, sizeof(int) * 3); if(jpg_file.curbyte == 0) jpg_file.curbyte = 0xFF; else jpeg_read_byte(); } } i = (jpg_file.curbyte >> (7 - jpg_file.curbit++)) & 0x01; if(jpg_file.curbit == 8) jpg_file.curbit = 0; return i; } int jpeg_read_bits( int num ) { // read num bit register int i, j; for(i = 0, j = 0; i < num; i++) { j <<= 1; j |= jpeg_read_bit(); } return j; } int jpeg_bit2int( int bit, int i ) { // convert bit code to int if((i & (1 << (bit - 1))) > 0) return i; return -(i ^ ((1 << bit) - 1)); } int jpeg_huffmancode( huffman_table_t *table ) { // get Huffman code register int i,size,code; for(size = 1, code = 0, i = 0; size < 17; size++) { code <<= 1; code |= jpeg_read_bit(); while(table->size[i] <= size) { if(table->code[i] == code) return table->hval[i]; i++; } } return code; } void jpeg_idct( float *data ) { // aa&n algorithm inverse DCT float t0, t1, t2, t3, t4, t5, t6, t7; float t10, t11, t12, t13; float z5, z10, z11, z12, z13; float *dataptr; int i; dataptr = data; for(i = 0; i < 8; i++) { t0 = dataptr[8 * 0]; t1 = dataptr[8 * 2]; t2 = dataptr[8 * 4]; t3 = dataptr[8 * 6]; t10 = t0 + t2; t11 = t0 - t2; t13 = t1 + t3; t12 = - t13 + (t1 - t3) * 1.414213562;//?? t0 = t10 + t13; t3 = t10 - t13; t1 = t11 + t12; t2 = t11 - t12; t4 = dataptr[8 * 1]; t5 = dataptr[8 * 3]; t6 = dataptr[8 * 5]; t7 = dataptr[8 * 7]; z13 = t6 + t5; z10 = t6 - t5; z11 = t4 + t7; z12 = t4 - t7; t7 = z11 + z13; t11 = (z11 - z13) * 1.414213562; z5 = (z10 + z12) * 1.847759065; t10 = - z5 + z12 * 1.082392200; t12 = z5 - z10 * 2.613125930; t6 = t12 - t7; t5 = t11 - t6; t4 = t10 + t5; dataptr[8 * 0] = t0 + t7; dataptr[8 * 7] = t0 - t7; dataptr[8 * 1] = t1 + t6; dataptr[8 * 6] = t1 - t6; dataptr[8 * 2] = t2 + t5; dataptr[8 * 5] = t2 - t5; dataptr[8 * 4] = t3 + t4; dataptr[8 * 3] = t3 - t4; dataptr++; } dataptr = data; for(i = 0; i < 8; i++) { t10 = dataptr[0] + dataptr[4]; t11 = dataptr[0] - dataptr[4]; t13 = dataptr[2] + dataptr[6]; t12 = - t13 + (dataptr[2] - dataptr[6]) * 1.414213562;//?? t0 = t10 + t13; t3 = t10 - t13; t1 = t11 + t12; t2 = t11 - t12; z13 = dataptr[5] + dataptr[3]; z10 = dataptr[5] - dataptr[3]; z11 = dataptr[1] + dataptr[7]; z12 = dataptr[1] - dataptr[7]; t7 = z11 + z13; t11 = (z11 - z13) * 1.414213562; z5 = (z10 + z12) * 1.847759065; t10 = - z5 + z12 * 1.082392200; t12 = z5 - z10 * 2.613125930; t6 = t12 - t7; t5 = t11 - t6; t4 = t10 + t5; dataptr[0] = t0 + t7; dataptr[7] = t0 - t7; dataptr[1] = t1 + t6; dataptr[6] = t1 - t6; dataptr[2] = t2 + t5; dataptr[5] = t2 - t5; dataptr[4] = t3 + t4; dataptr[3] = t3 - t4; dataptr += 8;//move ptr } } int jpeg_readmarkers( void ) { // read jpeg markers int marker, length, i, j, k, l, m; huffman_table_t *hptr; while( 1 ) { marker = jpeg_read_byte(); if(marker != 0xFF) return 0; marker = jpeg_read_byte(); if(marker != 0xD8) { length = jpeg_read_word(); length -= 2; switch(marker) { case 0xC0: // Baseline jpg_file.data_precision = jpeg_read_byte(); jpg_file.height = jpeg_read_word(); jpg_file.width = jpeg_read_word(); jpg_file.num_components = jpeg_read_byte(); if(length - 6 != jpg_file.num_components * 3) return 0; for(i = 0; i < jpg_file.num_components; i++) { jpg_file.component_info[i].id = jpeg_read_byte(); j = jpeg_read_byte(); jpg_file.component_info[i].h = (j >> 4) & 0x0F; jpg_file.component_info[i].v = j & 0x0F; jpg_file.component_info[i].t = jpeg_read_byte(); } break; case 0xC1: // Extended sequetial, Huffman case 0xC2: // Progressive, Huffman case 0xC3: // Lossless, Huffman case 0xC5: // Differential sequential, Huffman case 0xC6: // Differential progressive, Huffman case 0xC7: // Differential lossless, Huffman case 0xC8: // Reserved for JPEG extensions case 0xC9: // Extended sequential, arithmetic case 0xCA: // Progressive, arithmetic case 0xCB: // Lossless, arithmetic case 0xCD: // Differential sequential, arithmetic case 0xCE: // Differential progressive, arithmetic case 0xCF: // Differential lossless, arithmetic return 0; case 0xC4: // Huffman table while(length > 0) { k = jpeg_read_byte(); if(k & 0x10) hptr = &jpg_file.hac[k & 0x0F]; else hptr = &jpg_file.hdc[k & 0x0F]; for(i = 0, j = 0; i < 16; i++) { hptr->bits[i] = jpeg_read_byte(); j += hptr->bits[i]; } length -= 17; for(i = 0; i < j; i++) hptr->hval[i] = jpeg_read_byte(); length -= j; for(i = 0, k = 0, l = 0; i < 16; i++) { for(j = 0; j < hptr->bits[i]; j++, k++) { hptr->size[k] = i + 1; hptr->code[k] = l++; } l <<= 1; } } break; case 0xDB: // Quantization table while(length > 0) { j = jpeg_read_byte(); k = (j >> 4) & 0x0F; for(i = 0; i < 64; i++) { if( k )jpg_file.qtable[j][i] = jpeg_read_word(); else jpg_file.qtable[j][i] = jpeg_read_byte(); } length -= 65; if( k )length -= 64; } break; case 0xD9: // End of image (EOI) return 0; case 0xDA: // Start of scan (SOS) j = jpeg_read_byte(); for(i = 0; i < j; i++) { k = jpeg_read_byte(); m = jpeg_read_byte(); for(l = 0; l < jpg_file.num_components; l++) { if(jpg_file.component_info[l].id == k) { jpg_file.component_info[l].td = (m >> 4) & 0x0F; jpg_file.component_info[l].ta = m & 0x0F; } } } jpg_file.scan.ss = jpeg_read_byte(); jpg_file.scan.se = jpeg_read_byte(); k = jpeg_read_byte(); jpg_file.scan.ah = (k >> 4) & 0x0F; jpg_file.scan.al = k & 0x0F; return 1; case 0xDD: // Restart interval jpg_file.restart_interval = jpeg_read_word(); break; default: jpg_file.buffer += length; //move ptr break; } } } } void jpeg_decompress( void ) { // decompress jpeg file (Baseline algorithm) register int x, y, i, j, k, l, c; int X, Y, H, V, plane, scaleh[3], scalev[3]; static float vector[64], dct[64]; static const int jpeg_zigzag[64] = { 0, 1, 5, 6, 14, 15, 27, 28, 2, 4, 7, 13, 16, 26, 29, 42, 3, 8, 12, 17, 25, 30, 41, 43, 9, 11, 18, 24, 31, 40, 44, 53, 10, 19, 23, 32, 39, 45, 52, 54, 20, 22, 33, 38, 46, 51, 55, 60, 21, 34, 37, 47, 50, 56, 59, 61, 35, 36, 48, 49, 57, 58, 62, 63 }; // 1.0, k = 0; cos(k * PI / 16) * sqrt(2), k = 1...7 static const float aanscale[8] = { 1.0, 1.387039845, 1.306562965, 1.175875602, 1.0, 0.785694958, 0.541196100, 0.275899379 }; scaleh[0] = 1; scalev[0] = 1; if(jpg_file.num_components == 3) { scaleh[1] = jpg_file.component_info[0].h / jpg_file.component_info[1].h; scalev[1] = jpg_file.component_info[0].v / jpg_file.component_info[1].v; scaleh[2] = jpg_file.component_info[0].h / jpg_file.component_info[2].h; scalev[2] = jpg_file.component_info[0].v / jpg_file.component_info[2].v; } memset(jpg_file.dc,0,sizeof(int) * 3); for(Y = 0; Y < jpg_file.height; Y += jpg_file.component_info[0].v << 3) { if(jpg_file.restart_interval > 0) jpg_file.curbit = 0; for(X = 0; X < jpg_file.width; X += jpg_file.component_info[0].h << 3) { for(plane = 0; plane < jpg_file.num_components; plane++) { for(V = 0; V < jpg_file.component_info[plane].v; V++) { for(H = 0; H < jpg_file.component_info[plane].h; H++) { i = jpeg_huffmancode(&jpg_file.hdc[jpg_file.component_info[plane].td]); i &= 0x0F; vector[0] = jpg_file.dc[plane] + jpeg_bit2int(i,jpeg_read_bits(i)); jpg_file.dc[plane] = vector[0]; i = 1; while(i < 64) { j = jpeg_huffmancode(&jpg_file.hac[jpg_file.component_info[plane].ta]); if(j == 0) while(i < 64) vector[i++] = 0; else { k = i + ((j >> 4) & 0x0F); while(i < k) vector[i++] = 0; j &= 0x0F; vector[i++] = jpeg_bit2int(j,jpeg_read_bits(j)); } } k = jpg_file.component_info[plane].t; for(y = 0, i = 0; y < 8; y++) { for(x = 0; x < 8; x++, i++) { j = jpeg_zigzag[i]; dct[i] = vector[j] * jpg_file.qtable[k][j] * aanscale[x] * aanscale[y]; } } jpeg_idct(dct); for(y = 0; y < 8; y++) { for(x = 0; x < 8; x++) { c = ((int)dct[(y << 3) + x] >> 3) + 128; if(c < 0) c = 0; else if(c > 255) c = 255; if(scaleh[plane] == 1 && scalev[plane] == 1) { i = X + x + (H << 3); j = Y + y + (V << 3); if(i < jpg_file.width && j < jpg_file.height) jpg_file.data[((j * jpg_file.width + i) << 2) + plane] = c; } else for(l = 0; l < scalev[plane]; l++)//else for, heh... { for(k = 0; k < scaleh[plane]; k++) { i = X + (x + (H << 3)) * scaleh[plane] + k; j = Y + (y + (V << 3)) * scalev[plane] + l; if(i < jpg_file.width && j < jpg_file.height) jpg_file.data[((j * jpg_file.width + i) << 2) + plane] = c; } } } } } } } } } } void jpeg_ycbcr2rgba( void ) { int i, Y, Cb, Cr, R, G, B; // convert YCbCr image to RGBA for(i = 0; i < jpg_file.width * jpg_file.height << 2; i += 4) { Y = jpg_file.data[i]; Cb = jpg_file.data[i + 1] - 128; Cr = jpg_file.data[i + 2] - 128; R = Y + 1.40200 * Cr; G = Y - 0.34414 * Cb - 0.71414 * Cr; B = Y + 1.77200 * Cb; // bound colors if(R < 0) R = 0; else if(R > 255) R = 255; if(G < 0) G = 0; else if(G > 255) G = 255; if(B < 0) B = 0; else if(B > 255) B = 255; jpg_file.data[i + 0] = R; jpg_file.data[i + 1] = G; jpg_file.data[i + 2] = B; jpg_file.data[i + 3] = 0xff;//alpha channel } } void jpeg_gray2rgba( void ) { int i, j; // grayscale image to RGBA for(i = 0; i < jpg_file.width * jpg_file.height << 2; i += 4) { j = jpg_file.data[i]; jpg_file.data[i + 0] = j; jpg_file.data[i + 1] = j; jpg_file.data[i + 2] = j; jpg_file.data[i + 3] = 0xff; } } bool LoadJPG(char *name, char *buffer, int filesize ) { memset(&jpg_file, 0, sizeof(jpg_file)); jpg_file.buffer = buffer; if(!jpeg_readmarkers()) return false; // it's not a jpg file, just skip it image_width = jpg_file.width; image_height = jpg_file.height; image_type = PF_RGBA_32; if(!Image_ValidSize( name )) return false; image_size = jpg_file.width * jpg_file.height * 4; jpg_file.data = Mem_Alloc( Sys.imagepool, image_size ); jpeg_decompress(); if(jpg_file.num_components == 1) jpeg_gray2rgba(); if(jpg_file.num_components == 3) jpeg_ycbcr2rgba(); image_num_layers = 1; image_num_mips = 1; image_rgba = jpg_file.data; return true; } typedef struct loadformat_s { char *formatstring; char *ext; bool (*loadfunc)(char *name, char *buffer, int filesize); } loadformat_t; loadformat_t load_formats[] = { {"textures/%s%s.%s", "dds", LoadDDS}, {"textures/%s%s.%s", "tga", LoadTGA}, {"textures/%s%s.%s", "jpg", LoadJPG}, {"textures/%s%s.%s", "wad3",LoadWAD}, {"textures/%s%s.%s", "qpic",LoadPIC}, {"textures/%s%s.%s", "pcx", LoadPCX}, {"textures/%s%s.%s", "wal", LoadWAL}, {"textures/%s%s.%s", "lmp", LoadLMP}, {"textures/%s%s.%s", "mip", LoadMIP}, {"textures/%s%s.%s", "qfnt",LoadFNT}, {"%s%s.%s", "dds", LoadDDS}, {"%s%s.%s", "tga", LoadTGA}, {"%s%s.%s", "jpg", LoadJPG}, {"%s%s.%s", "wad3",LoadWAD}, {"%s%s.%s", "qpic",LoadPIC}, {"%s%s.%s", "pcx", LoadPCX}, {"%s%s.%s", "wal", LoadWAL}, {"%s%s.%s", "lmp", LoadLMP}, {"%s%s.%s", "mip", LoadMIP}, {"%s%s.%s", "qfnt",LoadFNT}, {NULL, NULL} }; rgbdata_t *ImagePack( void ) { rgbdata_t *pack = Mem_Alloc( Sys.imagepool, sizeof(rgbdata_t)); if(image_cubemap && cubemap_num_sides != 6) { // this neved be happens, just in case MsgWarn("ImagePack: inconsistent cubemap pack %d\n", cubemap_num_sides ); FS_FreeImage( pack ); return NULL; } if(image_cubemap) { image_flags |= IMAGE_CUBEMAP; pack->buffer = image_cubemap; pack->width = cubemap_width; pack->height = cubemap_height; pack->type = cubemap_image_type; pack->size = image_size * cubemap_num_sides; } else { pack->buffer = image_rgba; pack->width = image_width; pack->height = image_height; pack->type = image_type; pack->size = image_size; } pack->numLayers = image_num_layers; pack->numMips = image_num_mips; pack->bitsCount = image_bits_count; pack->flags = image_flags; pack->palette = image_palette; return pack; } bool FS_AddImageToPack( const char *name ) { byte *resampled; // first image have suffix "ft" and set average size for all cubemap sides! if(!image_cubemap) { cubemap_width = image_width; cubemap_height = image_height; cubemap_image_type = image_type; } image_size = cubemap_width * cubemap_height * 4; // keep constant size, render.dll expecting it // mixing dds format with any existing ? if(image_type != cubemap_image_type) return false; // resampling image if needed resampled = Image_Resample((uint *)image_rgba, image_width, image_height, cubemap_width, cubemap_height, cubemap_image_type ); if(!resampled) return false; // try to reasmple dxt? if(resampled != image_rgba) { MsgDev(D_NOTE, "FS_AddImageToPack: resample %s from [%dx%d] to [%dx%d]\n", name, image_width, image_height, cubemap_width, cubemap_height ); Mem_Move( Sys.imagepool, &image_rgba, resampled, image_size );// update buffer } image_cubemap = Mem_Realloc( Sys.imagepool, image_cubemap, image_ptr + image_size ); Mem_Copy(image_cubemap + image_ptr, image_rgba, image_size ); Mem_Free( image_rgba ); // memmove aren't help us image_ptr += image_size; // move to next cubemap_num_sides++; // sides counter return true; } bool FS_AddMipmapToPack( const byte *in, int width, int height ) { int mipsize = width * height; int outsize = width * height * 4; // reallocate image buffer image_rgba = Mem_Realloc( Sys.imagepool, image_rgba, image_size + outsize ); if(!Image_Copy8bitRGBA( in, image_rgba + image_size, mipsize )) return false; // pallette not installed image_size += outsize; image_num_mips++; return true; } /* ================ FS_LoadImage loading and unpack to rgba any known image ================ */ rgbdata_t *FS_LoadImage(const char *filename, char *buffer, int buffsize ) { loadformat_t *format; const char *ext = FS_FileExtension( filename ); char path[128], loadname[128], texname[128]; bool anyformat = !stricmp(ext, "") ? true : false; int i, filesize = 0; byte *f; #if 0 // don't try to be very clever if(!buffer || !buffsize) buffer = (char *)florr1_2_jpg, buffsize = sizeof(florr1_2_jpg); #endif com_strncpy( loadname, filename, sizeof(loadname)-1); FS_StripExtension( loadname ); //remove extension if needed // developer warning if(!anyformat) MsgDev(D_NOTE, "Note: %s will be loading only with ext .%s\n", loadname, ext ); // now try all the formats in the selected list for (format = load_formats; format->formatstring; format++) { if(anyformat || !stricmp(ext, format->ext )) { com_sprintf (path, format->formatstring, loadname, "", format->ext ); f = FS_LoadFile( path, &filesize ); if(f && filesize > 0) { // this name will be used only for tell user about problems FS_FileBase( path, texname ); if( format->loadfunc(texname, f, filesize )) return ImagePack(); //loaded } } } // maybe it skybox or cubemap ? for(i = 0; i < 6; i++) { for (format = load_formats; format->formatstring; format++) { if(anyformat || !stricmp(ext, format->ext )) { com_sprintf (path, format->formatstring, loadname, suf[i], format->ext ); f = FS_LoadFile( path, &filesize ); if(f && filesize > 0) { // this name will be used only for tell user about problems FS_FileBase( path, texname ); if( format->loadfunc(texname, f, filesize )) { if(FS_AddImageToPack(va("%s%s.%s", loadname, suf[i], format->ext))) break; // loaded } } } } if(cubemap_num_sides != i + 1) //check side { // first side not found, probably it's not cubemap // it contain info about image_type and dimensions, don't generate black cubemaps if(!image_cubemap) break; MsgDev(D_ERROR, "FS_LoadImage: couldn't load (%s%s.%s), create balck image\n",loadname,suf[i],ext ); // Mem_Alloc already filled memblock with 0x00, no need to do it again image_cubemap = Mem_Realloc( Sys.imagepool, image_cubemap, image_ptr + image_size ); image_ptr += image_size; // move to next cubemap_num_sides++; // merge counter } } if(image_cubemap) return ImagePack(); // now it's cubemap pack // try to load image from const buffer (e.g. const byte blank_frame ) com_strncpy( texname, filename, sizeof(texname) - 1); for (format = load_formats; format->formatstring; format++) { if(anyformat || !stricmp(ext, format->ext )) { if(buffer && buffsize > 0) { // this name will be used only for tell user about problems FS_FileBase( loadname, texname ); if( format->loadfunc(texname, buffer, buffsize )) return ImagePack();// loaded } } } MsgDev(D_WARN, "couldn't load %s\n", texname ); return NULL; } /* ================ FS_FreeImage free RGBA buffer ================ */ void FS_FreeImage( rgbdata_t *pack ) { if( pack ) { if( pack->buffer ) Mem_Free( pack->buffer ); if( pack->palette ) Mem_Free( pack->palette ); Mem_Free( pack ); } // reset global variables image_width = image_height = 0; cubemap_width = cubemap_height = 0; image_bits_count = image_flags = 0; cubemap_num_sides = 0; image_num_layers = 1; image_num_mips = 0; image_type = PF_UNKNOWN; image_palette = NULL; image_rgba = NULL; image_cubemap = NULL; image_ptr = 0; image_size = 0; } /* ============= SaveTGA ============= */ bool SaveTGA( const char *filename, byte *data, int width, int height, bool alpha, int imagetype ) { int y, outsize, pixel_size; const byte *bufend, *in; byte *buffer, *out; const char *comment = "Generated by Xash ImageLib\0"; if(alpha) outsize = width * height * 4 + 18 + com_strlen(comment); else outsize = width * height * 3 + 18 + com_strlen(comment); buffer = (byte *)Malloc( outsize ); memset (buffer, 0, 18); // prepare header buffer[0] = com_strlen(comment); // tga comment length buffer[2] = 2; // uncompressed type buffer[12] = (width >> 0) & 0xFF; buffer[13] = (width >> 8) & 0xFF; buffer[14] = (height >> 0) & 0xFF; buffer[15] = (height >> 8) & 0xFF; buffer[16] = alpha ? 32 : 24; buffer[17] = alpha ? 8 : 0; // 8 bits of alpha com_strncpy(buffer + 18, comment, com_strlen(comment)); out = buffer + 18 + com_strlen(comment); // get image description switch( imagetype ) { case PF_RGB_24_FLIP: case PF_RGB_24: pixel_size = 3; break; case PF_RGBA_32: pixel_size = 4; break; default: MsgWarn("SaveTGA: unsupported image type %s\n", PFDesc[image_type].name ); return false; } // flip buffer switch( imagetype ) { case PF_RGB_24_FLIP: // glReadPixels rotating image at 180 degrees, flip it for (in = data; in < data + width * height * pixel_size; in += pixel_size) { *out++ = in[2]; *out++ = in[1]; *out++ = in[0]; } break; case PF_RGB_24: case PF_RGBA_32: // swap rgba to bgra and flip upside down for (y = height - 1; y >= 0; y--) { in = data + y * width * pixel_size; bufend = in + width * pixel_size; for ( ;in < bufend; in += pixel_size) { *out++ = in[2]; *out++ = in[1]; *out++ = in[0]; if(alpha) *out++ = in[3]; } } } MsgDev(D_NOTE, "Writing %s[%d]\n", filename, alpha ? 32 : 24 ); FS_WriteFile (filename, buffer, outsize ); Mem_Free( buffer ); return true; } typedef struct saveformat_s { char *formatstring; char *ext; bool (*savefunc)(char *filename, byte *data, int width, int height, bool alpha, int imagetype ); } saveformat_t; saveformat_t save_formats[] = { {"%s%s.%s", "tga", SaveTGA}, {NULL, NULL} }; /* ================ FS_SaveImage writes image as tga RGBA format ================ */ void FS_SaveImage(const char *filename, rgbdata_t *pix ) { bool has_alpha = false; int i, numsides = 1; byte *data; char savename[256]; if(!pix || !pix->buffer) return; data = pix->buffer; FS_StripExtension( (char *)filename ); if(pix->flags & IMAGE_HAS_ALPHA) has_alpha = true; if(pix->flags & IMAGE_CUBEMAP) numsides = 6; for(i = 0; i < numsides; i++) { if(numsides > 1) com_sprintf(savename, "%s%s.tga", filename, suf[i] ); else com_sprintf(savename, "%s.tga", filename ); SaveTGA( savename, data, pix->width, pix->height, has_alpha, pix->type ); data += pix->width * pix->height * PFDesc[pix->type].bpp; } }