23 Dec 2007

This commit is contained in:
g-cont 2007-12-23 00:00:00 +03:00 committed by Alibek Omarov
parent 1d7fe10f27
commit 2761b9d8ec
10 changed files with 1182 additions and 39 deletions

View File

@ -2,10 +2,10 @@
#define IDI_ICON1 101
#define VER_FILEVERSION 0,45
#define VER_FILEVERSION_STR "0.45"
#define VER_PRODUCTVERSION 0,45
#define VER_PRODUCTVERSION_STR "0.45"
#define VER_FILEVERSION 0,3
#define VER_FILEVERSION_STR "0.3"
#define VER_PRODUCTVERSION 0,3
#define VER_PRODUCTVERSION_STR "0.3"
#define VER_FILEFLAGSMASK VS_FF_PRERELEASE | VS_FF_PATCHED
#define VER_FILEFLAGS VS_FF_PRERELEASE

View File

@ -98,7 +98,8 @@ extern uint image_ptr; // common moveable pointer
// image lib utilites
extern uint *d_currentpal;
bool Image_Copy8bitRGBA(const byte *in, byte *out, int pixels);
bool Image_Copy8bitRGBA(const byte *in, byte *out, int pixels); // convert indexed image to RGBA
rgbdata_t *Image_CopyRGBA8bit( rgbdata_t *pix, int numcolors ); // convert RGBA image to indexed
void Image_RoundDimensions(int *scaled_width, int *scaled_height);
byte *Image_Resample(uint *in, int inwidth, int inheight, int outwidth, int outheight, int in_type);
bool FS_AddMipmapToPack( const byte *in, int width, int height );

571
launch/common/image_save.c Normal file
View File

@ -0,0 +1,571 @@
//=======================================================================
// Copyright XashXT Group 2007 ©
// image_save.c - saving textures
//=======================================================================
#include "launch.h"
#include "image.h"
#include "mathlib.h"
#define Sum(c) ((c)->r + (c)->g + (c)->b)
bool dds_write_header( vfile_t *f, rgbdata_t *pix, uint cubemap_flags )
{
uint dwFourCC, dwFlags1 = 0, dwFlags2 = 0, dwCaps1 = 0;
uint dwLinearSize, dwBlockSize, dwCaps2 = 0;
uint dwIdent = DDSHEADER, dwSize = 124, dwSize2 = 32;
if(!pix || pix->buffer )
return false;
// setup flags
dwFlags1 |= DDS_LINEARSIZE | DDS_MIPMAPCOUNT | DDS_WIDTH | DDS_HEIGHT | DDS_CAPS | DDS_PIXELFORMAT;
dwFlags2 |= DDS_FOURCC;
if( pix->numLayers > 1) dwFlags1 |= DDS_DEPTH;
switch( pix->type )
{
case PF_DXT1:
dwFourCC = TYPE_DXT1;
break;
case PF_DXT2:
dwFourCC = TYPE_DXT2;
break;
case PF_DXT3:
dwFourCC = TYPE_DXT3;
break;
case PF_DXT4:
dwFourCC = TYPE_DXT4;
break;
case PF_DXT5:
dwFourCC = TYPE_DXT5;
break;
case PF_ATI1N:
dwFourCC = TYPE_ATI1;
break;
case PF_ATI2N:
dwFourCC = TYPE_ATI2;
break;
case PF_RXGB:
dwFourCC = TYPE_RXGB;
break;
default:
MsgDev( D_ERROR, "dds_write_header: unsupported type %s\n", PFDesc[pix->type].name );
return false;
}
VFS_Write(f, &dwIdent, sizeof(uint));
VFS_Write(f, &dwSize, sizeof(uint));
VFS_Write(f, &dwFlags1, sizeof(uint));
VFS_Write(f, &pix->height, sizeof(uint));
VFS_Write(f, &pix->width, sizeof(uint));
dwBlockSize = PFDesc[pix->type].block;
dwLinearSize = (((pix->width + 3)/4) * ((pix->height + 3)/4)) * dwBlockSize * pix->numLayers;
VFS_Write(f, &dwLinearSize, sizeof(uint)); // TODO: use dds_get_linear_size
if (pix->numLayers > 1)
{
VFS_Write(f, &pix->numLayers, sizeof(uint));
dwCaps2 |= DDS_VOLUME;
}
else VFS_Write(f, 0, sizeof(uint));
VFS_Write(f, &pix->numMips, sizeof(uint));
VFS_Write(f, 0, sizeof(uint));
VFS_Write(f, pix->color, sizeof(vec3_t));
VFS_Write(f, &pix->bump_scale, sizeof(float));
VFS_Write(f, 0, sizeof(uint) * 6 ); // reserved fields
VFS_Write(f, &dwSize2, sizeof(uint));
VFS_Write(f, &dwFlags2, sizeof(uint));
VFS_Write(f, &dwFourCC, sizeof(uint));
VFS_Write(f, 0, sizeof(uint) * 5 ); // bit masks
dwCaps1 |= DDS_TEXTURE;
if( pix->numMips > 1 ) dwCaps1 |= DDS_MIPMAP | DDS_COMPLEX;
if( cubemap_flags )
{
dwCaps1 |= DDS_COMPLEX;
dwCaps2 |= cubemap_flags;
}
VFS_Write(f, &dwCaps1, sizeof(uint));
VFS_Write(f, &dwCaps2, sizeof(uint));
VFS_Write(f, 0, sizeof(uint) * 3 ); // other caps and TextureStage
return true;
}
void ShortToColor565(word Pixel, color16 *Colour)
{
Colour->r = (Pixel & 0xF800) >> 11;
Colour->g = (Pixel & 0x07E0) >> 5;
Colour->b = (Pixel & 0x001F);
return;
}
void ShortToColor888(word Pixel, color24 *Colour)
{
Colour->r = ((Pixel & 0xF800) >> 11) << 3;
Colour->g = ((Pixel & 0x07E0) >> 5) << 2;
Colour->b = ((Pixel & 0x001F)) << 3;
return;
}
word Color565ToShort(color16 *Colour)
{
return (Colour->r << 11) | (Colour->g << 5) | (Colour->b);
}
word Color888ToShort(color24 *Colour)
{
return ((Colour->r >> 3) << 11) | ((Colour->g >> 2) << 5) | (Colour->b >> 3);
}
void ChooseEndpoints(word *Block, word *ex0, word *ex1)
{
uint i;
color24 Colours[16];
int Lowest=0, Highest=0;
for (i = 0; i < 16; i++)
{
ShortToColor888(Block[i], &Colours[i]);
if(Sum(&Colours[i]) < Sum(&Colours[Lowest])) Lowest = i;
if(Sum(&Colours[i]) > Sum(&Colours[Highest])) Highest = i;
}
*ex0 = Block[Highest];
*ex1 = Block[Lowest];
}
void ChooseAlphaEndpoints( byte *Block, byte *a0, byte *a1)
{
uint i, Lowest = 0xFF, Highest = 0;
for (i = 0; i < 16; i++)
{
if( Block[i] < Lowest ) Lowest = Block[i];
if( Block[i] > Highest) Highest = Block[i];
}
*a0 = Lowest;
*a1 = Highest;
}
bool Get3DcBlock( byte *Block, byte *Data, rgbdata_t *pix, uint XPos, uint YPos, int channel )
{
uint x, y, i = 0, Offset = 2*(YPos * pix->width + XPos) + channel;
for (y = 0; y < 4; y++)
{
for (x = 0; x < 4; x++)
{
if(x < pix->width && y < pix->height)
Block[i++] = Data[Offset + 2*x];
else Block[i++] = Data[Offset];
}
Offset += 2 * pix->width;
}
return true;
}
word *dds_compress_565( rgbdata_t *pix )
{
word *Data;
uint i, j;
Data = (word*)Mem_Alloc( Sys.imagepool, pix->width * pix->height * 2 * pix->numLayers );
switch ( pix->type )
{
case PF_RGB_24:
for (i = 0, j = 0; i < pix->size; i += 3, j++)
{
Data[j] = (pix->buffer[i+0] >> 3) << 11;
Data[j] |= (pix->buffer[i+1] >> 2) << 5;
Data[j] |= pix->buffer[i+2] >> 3;
}
break;
case PF_RGBA_32:
for (i = 0, j = 0; i < pix->size; i += 4, j++)
{
Data[j] = (pix->buffer[i+0] >> 3) << 11;
Data[j] |= (pix->buffer[i+1] >> 2) << 5;
Data[j] |= pix->buffer[i+2] >> 3;
}
break;
case PF_RGB_24_FLIP:
for (i = 0, j = 0; i < pix->size; i += 3, j++)
{
Data[j] = (pix->buffer[i+2] >> 3) << 11;
Data[j] |= (pix->buffer[i+1] >> 2) << 5;
Data[j] |= pix->buffer[i+0] >> 3;
}
break;
case PF_LUMINANCE:
for (i = 0, j = 0; i < pix->size; i++, j++)
{
Data[j] = (pix->buffer[i] >> 3) << 11;
Data[j] |= (pix->buffer[i] >> 2) << 5;
Data[j] |= pix->buffer[i] >> 3;
}
break;
case PF_LUMINANCE_ALPHA:
for (i = 0, j = 0; i < pix->size; i += 2, j++)
{
Data[j] = (pix->buffer[i] >> 3) << 11;
Data[j] |= (pix->buffer[i] >> 2) << 5;
Data[j] |= pix->buffer[i] >> 3;
}
break;
}
return Data;
}
byte *dds_compress_88( rgbdata_t *pix )
{
byte *Data;
uint i, j;
Data = (byte*)Mem_Alloc(Sys.imagepool, pix->width * pix->height * 2 * pix->numLayers );
switch( pix->type )
{
case PF_RGB_24:
for (i = 0, j = 0; i < pix->size; i += 3, j += 2)
{
Data[j+0] = pix->buffer[i+1];
Data[j+1] = pix->buffer[i+0];
}
break;
case PF_RGBA_32:
for (i = 0, j = 0; i < pix->size; i += 4, j += 2)
{
Data[j+0] = pix->buffer[i+1];
Data[j+1] = pix->buffer[i+0];
}
break;
case PF_RGB_24_FLIP:
for (i = 0, j = 0; i < pix->size; i += 3, j += 2)
{
Data[j ] = pix->buffer[i+1];
Data[j+1] = pix->buffer[i+2];
}
break;
case PF_LUMINANCE:
case PF_LUMINANCE_ALPHA:
for (i = 0, j = 0; i < pix->size; i++, j += 2)
{
Data[j] = Data[j+1] = 0; //??? Luminance is no normal map format...
}
break;
}
return Data;
}
void dds_compress_RXGB(rgbdata_t *pix, word **xgb, byte **r )
{
uint i, j;
word *Data;
byte *Alpha;
*xgb = NULL;
*r = NULL;
*xgb = (word*)Mem_Alloc( Sys.imagepool, pix->width * pix->height * 2 * pix->numLayers );
*r = Mem_Alloc( Sys.imagepool, pix->width * pix->height * pix->numLayers);
//alias pointers to be able to use copy'n'pasted code :)
Data = *xgb;
Alpha = *r;
switch( pix->type )
{
case PF_RGB_24:
for (i = 0, j = 0; i < pix->size; i += 3, j++)
{
Alpha[j] = pix->buffer[i+0];
Data[j] = (pix->buffer[i+1] >> 2) << 5;
Data[j] |= pix->buffer[i+2] >> 3;
}
break;
case PF_RGBA_32:
for (i = 0, j = 0; i < pix->size; i += 4, j++)
{
Alpha[j] = pix->buffer[i+0];
Data[j] = (pix->buffer[i+1] >> 2) << 5;
Data[j] |= pix->buffer[i+2] >> 3;
}
break;
case PF_RGB_24_FLIP:
for (i = 0, j = 0; i < pix->size; i += 3, j++)
{
Alpha[j] = pix->buffer[i+2];
Data[j] = (pix->buffer[i+1] >> 2) << 5;
Data[j] |= pix->buffer[i+0] >> 3;
}
break;
case PF_LUMINANCE:
for (i = 0, j = 0; i < pix->size; i++, j++)
{
Alpha[j] = pix->buffer[i];
Data[j] = (pix->buffer[i] >> 2) << 5;
Data[j] |= pix->buffer[i] >> 3;
}
break;
case PF_LUMINANCE_ALPHA:
for (i = 0, j = 0; i < pix->size; i += 2, j++)
{
Alpha[j] = pix->buffer[i];
Data[j] = (pix->buffer[i] >> 2) << 5;
Data[j] |= pix->buffer[i] >> 3;
}
break;
}
}
uint dds_compress_dxt( vfile_t *f, int saveformat, rgbdata_t *pix )
{
word *Data, Block[16], ex0, ex1, *Runner16, t0, t1;
byte *Alpha, AlphaBlock[16], AlphaBitMask[6], a0, a1;
uint x, y, z, i, BitMask;
byte *Data3Dc, *Runner8;
bool HasAlpha;
uint Count = 0;
if(!pix || pix->buffer )
return 0;
if( saveformat == PF_ATI2N)
{
Data3Dc = dds_compress_88( pix );
if(!Data3Dc) return 0;
Runner8 = Data3Dc;
for (z = 0; z < pix->numLayers; z++)
{
for (y = 0; y < pix->height; y += 4)
{
for (x = 0; x < pix->width; x += 4)
{
Get3DcBlock( AlphaBlock, Runner8, pix, x, y, 0);
ChooseAlphaEndpoints(AlphaBlock, &a0, &a1);
GenAlphaBitMask(a0, a1, AlphaBlock, AlphaBitMask, NULL);
VFS_Write(f, &a0, sizeof(byte));
VFS_Write(f, &a1, sizeof(byte));
VFS_Write(f, &AlphaBitMask, sizeof(byte) * 6 );
Get3DcBlock(AlphaBlock, Runner8, pix, x, y, 1);
ChooseAlphaEndpoints(AlphaBlock, &a0, &a1);
GenAlphaBitMask(a0, a1, AlphaBlock, AlphaBitMask, NULL);
VFS_Write(f, &a0, sizeof(byte));
VFS_Write(f, &a1, sizeof(byte));
VFS_Write(f, &AlphaBitMask, sizeof(byte) * 6 );
Count += 16;
}
}
Runner8 += pix->width * pix->height * 2;
}
Mem_Free( Data3Dc );
}
else if( saveformat == IL_ATI1N )
{
rgbdata_t *lum = NULL;
if (PFDesc[pix->type].bpp != 1)
{
//FIXME: lum = Image_Convert( pix, PF_LUMINANCE );
if(!lum) return 0;
}
else lum = pix;
Runner8 = lum->buffer;
for (z = 0; z < pix->numLayers; z++)
{
for (y = 0; y < pix->height; y += 4)
{
for (x = 0; x < pix->width; x += 4)
{
GetAlphaBlock(AlphaBlock, Runner8, pix, x, y);
ChooseAlphaEndpoints(AlphaBlock, &a0, &a1);
GenAlphaBitMask(a0, a1, AlphaBlock, AlphaBitMask, NULL);
VFS_Write(f, &a0, sizeof(byte));
VFS_Write(f, &a1, sizeof(byte));
VFS_Write(f, &AlphaBitMask, sizeof(byte) * 6 );
Count += 8;
}
}
Runner8 += pix->width * pix->height;
}
if(lum != pix) FS_FreeImage( lum );
}
else
{
if( saveformat != PF_RXGB )
{
Data = dds_compress_565( pix );
if(!Data) return 0;
Alpha = ilGetAlpha(IL_UNSIGNED_BYTE);
if(!Alpha)
{
Mem_Free(Data);
return 0;
}
}
else
{
dds_compress_RXGB(pix, &Data, &Alpha);
if (!Data || !Alpha)
{
if( Data ) Mem_Free( Data );
if( Alpha) Mem_Free( Alpha);
return 0;
}
}
Runner8 = Alpha;
Runner16 = Data;
switch( saveformat )
{
case IL_DXT1:
for (z = 0; z < pix->numLayers; z++) {
for (y = 0; y < Image->Height; y += 4) {
for (x = 0; x < Image->Width; x += 4) {
GetAlphaBlock(AlphaBlock, Runner8, Image, x, y);
HasAlpha = IL_FALSE;
for (i = 0 ; i < 16; i++) {
if (AlphaBlock[i] < 128) {
HasAlpha = IL_TRUE;
break;
}
}
GetBlock(Block, Runner16, Image, x, y);
ChooseEndpoints(Block, &ex0, &ex1);
CorrectEndDXT1(&ex0, &ex1, HasAlpha);
SaveLittleUShort(ex0);
SaveLittleUShort(ex1);
if (HasAlpha)
BitMask = GenBitMask(ex0, ex1, 3, Block, AlphaBlock, NULL);
else
BitMask = GenBitMask(ex0, ex1, 4, Block, NULL, NULL);
SaveLittleUInt(BitMask);
Count += 8;
}
}
Runner16 += Image->Width * Image->Height;
Runner8 += Image->Width * Image->Height;
}
break;
/*case IL_DXT2:
for (y = 0; y < Image->Height; y += 4) {
for (x = 0; x < Image->Width; x += 4) {
GetAlphaBlock(AlphaBlock, Alpha, Image, x, y);
for (i = 0; i < 16; i += 2) {
iputc((ILubyte)(((AlphaBlock[i] >> 4) << 4) | (AlphaBlock[i+1] >> 4)));
}
GetBlock(Block, Data, Image, x, y);
PreMult(Block, AlphaBlock);
ChooseEndpoints(Block, &ex0, &ex1);
SaveLittleUShort(ex0);
SaveLittleUShort(ex1);
BitMask = GenBitMask(ex0, ex1, 4, Block, NULL, NULL);
SaveLittleUInt(BitMask);
}
}
break;*/
case IL_DXT3:
for (z = 0; z < Image->Depth; z++) {
for (y = 0; y < Image->Height; y += 4) {
for (x = 0; x < Image->Width; x += 4) {
GetAlphaBlock(AlphaBlock, Runner8, Image, x, y);
for (i = 0; i < 16; i += 2) {
iputc((ILubyte)(((AlphaBlock[i+1] >> 4) << 4) | (AlphaBlock[i] >> 4)));
}
GetBlock(Block, Runner16, Image, x, y);
ChooseEndpoints(Block, &t0, &t1);
ex0 = IL_MAX(t0, t1);
ex1 = IL_MIN(t0, t1);
CorrectEndDXT1(&ex0, &ex1, 0);
SaveLittleUShort(ex0);
SaveLittleUShort(ex1);
BitMask = GenBitMask(ex0, ex1, 4, Block, NULL, NULL);
SaveLittleUInt(BitMask);
Count += 16;
}
}
Runner16 += Image->Width * Image->Height;
Runner8 += Image->Width * Image->Height;
}
break;
case IL_RXGB:
case IL_DXT5:
for (z = 0; z < Image->Depth; z++) {
for (y = 0; y < Image->Height; y += 4) {
for (x = 0; x < Image->Width; x += 4) {
GetAlphaBlock(AlphaBlock, Runner8, Image, x, y);
ChooseAlphaEndpoints(AlphaBlock, &a0, &a1);
GenAlphaBitMask(a0, a1, AlphaBlock, AlphaBitMask, NULL/*AlphaOut*/);
/*Rms2 = RMSAlpha(AlphaBlock, AlphaOut);
GenAlphaBitMask(a0, a1, 8, AlphaBlock, AlphaBitMask, AlphaOut);
Rms1 = RMSAlpha(AlphaBlock, AlphaOut);
if (Rms2 <= Rms1) { // Yeah, we have to regenerate...
GenAlphaBitMask(a0, a1, 6, AlphaBlock, AlphaBitMask, AlphaOut);
Rms2 = a1; // Just reuse Rms2 as a temporary variable...
a1 = a0;
a0 = Rms2;
}*/
iputc(a0);
iputc(a1);
iwrite(AlphaBitMask, 1, 6);
GetBlock(Block, Runner16, Image, x, y);
ChooseEndpoints(Block, &t0, &t1);
ex0 = IL_MAX(t0, t1);
ex1 = IL_MIN(t0, t1);
CorrectEndDXT1(&ex0, &ex1, 0);
SaveLittleUShort(ex0);
SaveLittleUShort(ex1);
BitMask = GenBitMask(ex0, ex1, 4, Block, NULL, NULL);
SaveLittleUInt(BitMask);
Count += 16;
}
}
Runner16 += Image->Width * Image->Height;
Runner8 += Image->Width * Image->Height;
}
break;
}
ifree(Data);
ifree(Alpha);
} //else no 3dc
return Count;
}

View File

@ -1266,9 +1266,17 @@ bool LoadDDS( char *name, char *buffer, int filesize )
header.dwMipMapCount = BuffLittleLong(fin); fin += 4;
header.dwAlphaBitDepth = BuffLittleLong(fin); fin += 4;
// skip unused stuff
for (i = 0; i < 10; i++)
for(i = 0; i < 3; i++)
{
header.fReflectivity[i] = BuffLittleFloat(fin);
fin += 4;
}
header.fBumpScale = BuffLittleFloat(fin); fin += 4;
for (i = 0; i < 6; i++)
{
// skip unused stuff
header.dwReserved1[i] = BuffLittleLong(fin);
fin += 4;
}
@ -2072,12 +2080,102 @@ void FS_FreeImage( rgbdata_t *pack )
image_size = 0;
}
bool SaveBMP( const char *filename, byte *data, int width, int height, bool alpha, int imagetype, byte *palette )
{
file_t *pfile = NULL;
BITMAPFILEHEADER bmfh;
BITMAPINFOHEADER bmih;
RGBQUAD rgrgbPalette[256];
dword cbBmpBits;
byte* pbBmpBits;
byte *pb, *pbPal = NULL;
dword cbPalBytes;
dword biTrueWidth;
int i, rc = 0;
// bogus parameter check
if(!palette || !data ) return false;
pfile = FS_Open( filename, "wb");
if(!pfile) return false;
switch( imagetype )
{
case PF_INDEXED_24:
case PF_INDEXED_32:
break;
default:
MsgWarn("SaveBMP: unsupported image type %s\n", PFDesc[imagetype].name );
return false;
}
biTrueWidth = ((width + 3) & ~3);
cbBmpBits = biTrueWidth * height;
cbPalBytes = 256 * sizeof( RGBQUAD );
// Bogus file header check
bmfh.bfType = MAKEWORD( 'B', 'M' );
bmfh.bfSize = sizeof(bmfh) + sizeof(bmih) + cbBmpBits + cbPalBytes;
bmfh.bfReserved1 = 0;
bmfh.bfReserved2 = 0;
bmfh.bfOffBits = sizeof(bmfh) + sizeof(bmih) + cbPalBytes;
// write header
FS_Write( pfile, &bmfh, sizeof(bmfh));
// size of structure
bmih.biSize = sizeof bmih;
bmih.biWidth = biTrueWidth;
bmih.biHeight = height;
bmih.biPlanes = 1;
bmih.biBitCount = 8;
bmih.biCompression = BI_RGB;
bmih.biSizeImage = 0;
bmih.biXPelsPerMeter = 0;
bmih.biYPelsPerMeter = 0;
bmih.biClrUsed = 256;
bmih.biClrImportant = 0;
// Write info header
FS_Write( pfile, &bmih, sizeof(bmih));
pb = palette;
// copy over used entries
for (i = 0; i < (int)bmih.biClrUsed; i++)
{
rgrgbPalette[i].rgbRed = *pb++;
rgrgbPalette[i].rgbGreen = *pb++;
rgrgbPalette[i].rgbBlue = *pb++;
rgrgbPalette[i].rgbReserved = 0;
}
// write palette( bmih.biClrUsed entries )
cbPalBytes = bmih.biClrUsed * sizeof( RGBQUAD );
FS_Write( pfile, rgrgbPalette, cbPalBytes );
pbBmpBits = Mem_Alloc( Sys.imagepool, cbBmpBits );
pb = data;
pb += (height - 1) * width;
for(i = 0; i < bmih.biHeight; i++)
{
memmove(&pbBmpBits[biTrueWidth * i], pb, width);
pb -= width;
}
// write bitmap bits (remainder of file)
FS_Write( pfile, pbBmpBits, cbBmpBits );
Mem_Free( pbBmpBits );
return true;
}
/*
=============
SaveTGA
=============
*/
bool SaveTGA( const char *filename, byte *data, int width, int height, bool alpha, int imagetype )
bool SaveTGA( const char *filename, byte *data, int width, int height, bool alpha, int imagetype, byte *palette )
{
int y, outsize, pixel_size;
const byte *bufend, *in;
@ -2113,7 +2211,7 @@ bool SaveTGA( const char *filename, byte *data, int width, int height, bool alph
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 );
MsgWarn("SaveTGA: unsupported image type %s\n", PFDesc[imagetype].name );
return false;
}
@ -2157,12 +2255,13 @@ typedef struct saveformat_s
{
char *formatstring;
char *ext;
bool (*savefunc)(char *filename, byte *data, int width, int height, bool alpha, int imagetype );
bool (*savefunc)(char *filename, byte *data, int width, int height, bool alpha, int imagetype, byte *pal );
} saveformat_t;
saveformat_t save_formats[] =
{
{"%s%s.%s", "tga", SaveTGA},
{"%s%s.%s", "bmp", SaveBMP},
{NULL, NULL}
};
@ -2173,26 +2272,43 @@ FS_SaveImage
writes image as tga RGBA format
================
*/
void FS_SaveImage(const char *filename, rgbdata_t *pix )
void FS_SaveImage( const char *filename, rgbdata_t *pix )
{
bool has_alpha = false;
int i, numsides = 1;
saveformat_t *format;
const char *ext = FS_FileExtension( filename );
char path[128], savename[128];
bool anyformat = !stricmp(ext, "") ? true : false;
int i, filesize = 0;
byte *data;
char savename[256];
bool has_alpha = false;
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;
data = pix->buffer;
for(i = 0; i < numsides; i++)
com_strncpy( savename, filename, sizeof(savename) - 1);
FS_StripExtension( savename ); // remove extension if needed
// developer warning
if(!anyformat) MsgDev(D_NOTE, "Note: %s will be saving only with ext .%s\n", savename, ext );
// now try all the formats in the selected list
for (format = save_formats; format->formatstring; format++)
{
if( anyformat || !com_stricmp( ext, format->ext ))
{
com_sprintf( path, format->formatstring, savename, "", format->ext );
if( format->savefunc( path, data, pix->width, pix->height, has_alpha, pix->type, pix->palette ))
return; // saved
}
}
/*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 );
SaveTGA( savename, data, pix->width, pix->height, has_alpha, pix->type, pix->palette );
data += pix->width * pix->height * PFDesc[pix->type].bpp;
}
}*/
}

445
launch/common/pallib.c Normal file
View File

@ -0,0 +1,445 @@
//=======================================================================
// Copyright XashXT Group 2007 ©
// pallib.c - create palette images
//=======================================================================
#include "launch.h"
#include "image.h"
#include "mathlib.h"
#define PAL_MAXCOLORS 256
#define RED 0
#define GREEN 1
#define BLUE 2
typedef struct pixel_box_s
{
int r0; // min value, exclusive
int r1; // max value, inclusive
int g0;
int g1;
int b0;
int b1;
int vol;
} pixel_box_t;
// Histogram is in elements 1..HISTSIZE along each axis,
// element 0 is for base or marginal value
// NB: these must start out 0!
float gm2[33][33][33];
int wt[33][33][33];
int mr[33][33][33];
int mg[33][33][33];
int mb[33][33][33];
uint size; // image size
int K; // colour look-up table size
word *Qadd;
int WindW, WindH, WindD;
int i;
byte *buffer;
static int Width, Height, Depth, Comp;
uint n2(int s)
{
int i;
int res = 1;
for (i = 0; i < s; i++)
res = res*2;
return res;
}
// build 3-D color histogram of counts, r/g/b, c^2
void Hist3d( byte *Ir, byte *Ig, byte *Ib, int *vwt, int *vmr, int *vmg, int *vmb, float *m2 )
{
int ind, r, g, b;
int inr, ing, inb, table[2560];
uint i;
for (i = 0; i < 256; i++) table[i] = i * i;
Qadd = (word*)Mem_Alloc( Sys.imagepool, sizeof(word) * size );
for (i = 0; i < size; i++)
{
r = Ir[i]; g = Ig[i]; b = Ib[i];
inr = (r>>3) + 1;
ing = (g>>3) + 1;
inb = (b>>3) + 1;
Qadd[i] = ind = (inr<<10) + (inr<<6) + inr + (ing<<5) + ing + inb;
//[inr][ing][inb]
vwt[ind]++;
vmr[ind] += r;
vmg[ind] += g;
vmb[ind] += b;
m2[ind] += (float)(table[r] + table[g] + table[b]);
}
}
// At conclusion of the histogram step, we can interpret
// wt[r][g][b] = sum over voxel of P(c)
// mr[r][g][b] = sum over voxel of r*P(c), similarly for mg, mb
// m2[r][g][b] = sum over voxel of c^2*P(c)
// Actually each of these should be divided by 'size' to give the usual
// interpretation of P() as ranging from 0 to 1, but we needn't do that here.
// We now convert histogram into moments so that we can rapidly calculate
// the sums of the above quantities over any desired Box.
// compute cumulative moments
void M3d( int *vwt, int *vmr, int *vmg, int *vmb, float *m2 )
{
word ind1, ind2;
byte i, r, g, b;
int line, line_r, line_g, line_b, area[33], area_r[33], area_g[33], area_b[33];
float line2, area2[33];
for (r = 1; r <= 32; r++)
{
for (i = 0; i <= 32; i++)
{
area2[i] = 0.0f;
area[i] = area_r[i] = area_g[i] = area_b[i] = 0;
}
for (g = 1; g <= 32; g++)
{
line2 = 0.0f;
line = line_r = line_g = line_b = 0;
for (b = 1; b <= 32; b++)
{
ind1 = (r<<10) + (r<<6) + r + (g<<5) + g + b; // [r][g][b]
line += vwt[ind1];
line_r += vmr[ind1];
line_g += vmg[ind1];
line_b += vmb[ind1];
line2 += m2[ind1];
area[b] += line;
area_r[b] += line_r;
area_g[b] += line_g;
area_b[b] += line_b;
area2[b] += line2;
ind2 = ind1 - 1089; // [r-1][g][b]
vwt[ind1] = vwt[ind2] + area[b];
vmr[ind1] = vmr[ind2] + area_r[b];
vmg[ind1] = vmg[ind2] + area_g[b];
vmb[ind1] = vmb[ind2] + area_b[b];
m2[ind1] = m2[ind2] + area2[b];
}
}
}
}
// compute sum over a Box of any given statistic
int Vol(pixel_box_t *cube, int mmt[33][33][33])
{
return( mmt[cube->r1][cube->g1][cube->b1] -mmt[cube->r1][cube->g1][cube->b0] -mmt[cube->r1][cube->g0][cube->b1] +mmt[cube->r1][cube->g0][cube->b0] -mmt[cube->r0][cube->g1][cube->b1] +mmt[cube->r0][cube->g1][cube->b0] +mmt[cube->r0][cube->g0][cube->b1] -mmt[cube->r0][cube->g0][cube->b0] );
}
// the next two routines allow a slightly more efficient calculation
// of Vol() for a proposed subBox of a given Box. The sum of Top()
// and Bottom() is the Vol() of a subBox split in the given direction
// and with the specified new upper bound.
// compute part of Vol(cube, mmt) that doesn't depend on r1, g1, or b1
// (depending on dir)
int Bottom(pixel_box_t *cube, byte dir, int mmt[33][33][33])
{
switch(dir)
{
case RED: return( -mmt[cube->r0][cube->g1][cube->b1] +mmt[cube->r0][cube->g1][cube->b0] +mmt[cube->r0][cube->g0][cube->b1] -mmt[cube->r0][cube->g0][cube->b0] );
case GREEN: return( -mmt[cube->r1][cube->g0][cube->b1] +mmt[cube->r1][cube->g0][cube->b0] +mmt[cube->r0][cube->g0][cube->b1] -mmt[cube->r0][cube->g0][cube->b0] );
case BLUE: return( -mmt[cube->r1][cube->g1][cube->b0] +mmt[cube->r1][cube->g0][cube->b0] +mmt[cube->r0][cube->g1][cube->b0] -mmt[cube->r0][cube->g0][cube->b0] );
}
return 0;
}
// compute remainder of Vol(cube, mmt), substituting pos for
// r1, g1, or b1 (depending on dir)
int Top(pixel_box_t *cube, byte dir, int pos, int mmt[33][33][33])
{
switch (dir)
{
case RED: return( mmt[pos][cube->g1][cube->b1] -mmt[pos][cube->g1][cube->b0] -mmt[pos][cube->g0][cube->b1] +mmt[pos][cube->g0][cube->b0] );
case GREEN: return( mmt[cube->r1][pos][cube->b1] -mmt[cube->r1][pos][cube->b0] -mmt[cube->r0][pos][cube->b1] +mmt[cube->r0][pos][cube->b0] );
case BLUE: return( mmt[cube->r1][cube->g1][pos] -mmt[cube->r1][cube->g0][pos] -mmt[cube->r0][cube->g1][pos] +mmt[cube->r0][cube->g0][pos] );
}
return 0;
}
// compute the weighted variance of a Box
// NB: as with the raw statistics, this is really the variance * size
float Var(pixel_box_t *cube)
{
float dr, dg, db, xx;
dr = (float)Vol(cube, mr);
dg = (float)Vol(cube, mg);
db = (float)Vol(cube, mb);
xx = gm2[cube->r1][cube->g1][cube->b1] -gm2[cube->r1][cube->g1][cube->b0] -gm2[cube->r1][cube->g0][cube->b1] +gm2[cube->r1][cube->g0][cube->b0] -gm2[cube->r0][cube->g1][cube->b1] +gm2[cube->r0][cube->g1][cube->b0] +gm2[cube->r0][cube->g0][cube->b1] -gm2[cube->r0][cube->g0][cube->b0];
return xx - (dr*dr+dg*dg+db*db) / (float)Vol(cube, wt);
}
// We want to minimize the sum of the variances of two subBoxes.
// The sum(c^2) terms can be ignored since their sum over both subBoxes
// is the same (the sum for the whole Box) no matter where we split.
// The remaining terms have a minus sign in the variance formula,
// so we drop the minus sign and MAXIMIZE the sum of the two terms.
float Maximize( pixel_box_t *cube, byte dir, int first, int last, int *cut, int whole_r, int whole_g, int whole_b, int whole_w )
{
int half_r, half_g, half_b, half_w;
int base_r, base_g, base_b, base_w;
int i;
float temp, max = 0.0f;
base_r = Bottom(cube, dir, mr);
base_g = Bottom(cube, dir, mg);
base_b = Bottom(cube, dir, mb);
base_w = Bottom(cube, dir, wt);
*cut = -1;
for (i = first; i < last; ++i)
{
half_r = base_r + Top(cube, dir, i, mr);
half_g = base_g + Top(cube, dir, i, mg);
half_b = base_b + Top(cube, dir, i, mb);
half_w = base_w + Top(cube, dir, i, wt);
// now half_x is sum over lower half of pixel_box_t, if split at i
if (half_w == 0) continue; // never split into an empty pixel_box_t
else temp = ((float)half_r*half_r + (float)half_g * half_g + (float)half_b*half_b) / half_w;
half_r = whole_r - half_r;
half_g = whole_g - half_g;
half_b = whole_b - half_b;
half_w = whole_w - half_w;
if (half_w == 0) continue; // never split into an empty pixel_box_t
else temp += ((float)half_r*half_r + (float)half_g * half_g + (float)half_b*half_b) / half_w;
if (temp > max)
{
max = temp;
*cut = i;
}
}
return max;
}
int Cut( pixel_box_t *set1, pixel_box_t *set2 )
{
byte dir;
int cutr, cutg, cutb;
float maxr, maxg, maxb;
int whole_r, whole_g, whole_b, whole_w;
whole_r = Vol(set1, mr);
whole_g = Vol(set1, mg);
whole_b = Vol(set1, mb);
whole_w = Vol(set1, wt);
maxr = Maximize( set1, RED, set1->r0+1, set1->r1, &cutr, whole_r, whole_g, whole_b, whole_w );
maxg = Maximize( set1, GREEN, set1->g0+1, set1->g1, &cutg, whole_r, whole_g, whole_b, whole_w );
maxb = Maximize( set1, BLUE, set1->b0+1, set1->b1, &cutb, whole_r, whole_g, whole_b, whole_w );
if((maxr >= maxg) && (maxr >= maxb))
{
dir = RED;
if(cutr < 0)return 0; // can't split the pixel_box_t
}
else if ((maxg >= maxr) && (maxg >= maxb))
dir = GREEN;
else dir = BLUE;
set2->r1 = set1->r1;
set2->g1 = set1->g1;
set2->b1 = set1->b1;
switch (dir)
{
case RED:
set2->r0 = set1->r1 = cutr;
set2->g0 = set1->g0;
set2->b0 = set1->b0;
break;
case GREEN:
set2->g0 = set1->g1 = cutg;
set2->r0 = set1->r0;
set2->b0 = set1->b0;
break;
case BLUE:
set2->b0 = set1->b1 = cutb;
set2->r0 = set1->r0;
set2->g0 = set1->g0;
break;
}
set1->vol = (set1->r1-set1->r0) * (set1->g1-set1->g0) * (set1->b1-set1->b0);
set2->vol = (set2->r1-set2->r0) * (set2->g1-set2->g0) * (set2->b1-set2->b0);
return 1;
}
void Mark( pixel_box_t *cube, int label, byte *tag )
{
int r, g, b;
for (r = cube->r0 + 1; r <= cube->r1; r++)
{
for (g = cube->g0 + 1; g <= cube->g1; g++)
{
for (b = cube->b0 + 1; b <= cube->b1; b++)
{
tag[(r<<10) + (r<<6) + r + (g<<5) + g + b] = label;
}
}
}
}
rgbdata_t *Image_CopyRGBA8bit( rgbdata_t *pix, int numcolors )
{
pixel_box_t cube[PAL_MAXCOLORS];
byte lut_r[PAL_MAXCOLORS];
byte lut_g[PAL_MAXCOLORS];
byte lut_b[PAL_MAXCOLORS];
byte *tag = NULL;
int next = 0;
int weight;
uint k;
float vv[PAL_MAXCOLORS], temp;
byte *NewData = NULL, *Palette = NULL;
byte *Ir = NULL, *Ig = NULL, *Ib = NULL;
rgbdata_t *newpix = NULL;
int num_alloced_colors;
if(!pix) return NULL;
switch( pix->type )
{
case PF_RGBA_32: break;
default:
MsgDev(D_ERROR, "Image_ConvertTo8bit: unsupported pixelformat %s\n", PFDesc[pix->type].name );
return NULL;
}
num_alloced_colors = bound( 2, numcolors, 256 );
buffer = pix->buffer;
WindW = Width = pix->width;
WindH = Height = pix->height;
WindD = Depth = pix->numLayers;
Comp = PFDesc[pix->type].bpp;
Qadd = NULL;
NewData = (byte *)Mem_Alloc( Sys.imagepool, Width * Height * Depth );
Palette = (byte *)Mem_Alloc( Sys.imagepool, 3 * num_alloced_colors ); // 768 bytes
Ir = Mem_Alloc( Sys.imagepool, Width * Height * Depth);
Ig = Mem_Alloc( Sys.imagepool, Width * Height * Depth);
Ib = Mem_Alloc( Sys.imagepool, Width * Height * Depth);
size = Width * Height * Depth;
for (k = 0; k < size; k++)
{
Ir[k] = pix->buffer[k*3+2];
Ig[k] = pix->buffer[k*3+1];
Ib[k] = pix->buffer[k*3+0];
}
// set new colors number
K = numcolors;
// begin Wu's color quantization algorithm
// may have "leftovers" from a previous run.
memset(gm2, 0, 33 * 33 * 33 * sizeof(float));
memset(wt, 0, 33 * 33 * 33 * sizeof(int));
memset(mr, 0, 33 * 33 * 33 * sizeof(int));
memset(mg, 0, 33 * 33 * 33 * sizeof(int));
memset(mb, 0, 33 * 33 * 33 * sizeof(int));
// build 3d color histogramm
Hist3d(Ir, Ig, Ib, (int*)wt, (int*)mr, (int*)mg, (int*)mb, (float*)gm2);
M3d((int*)wt, (int*)mr, (int*)mg, (int*)mb, (float*)gm2);
cube[0].r0 = cube[0].g0 = cube[0].b0 = 0;
cube[0].r1 = cube[0].g1 = cube[0].b1 = 32;
for (i = 1; i < K; i++)
{
if(Cut(&cube[next], &cube[i]))
{
// volume test ensures we won't try to cut one-cell box
vv[next] = (cube[next].vol > 1) ? Var(&cube[next]) : 0.0f;
vv[i] = (cube[i].vol>1) ? Var(&cube[i]) : 0.0f;
}
else
{
vv[next] = 0.0; // don't try to split this Box again
i--; // didn't create Box i
}
next = 0;
temp = vv[0];
for (k = 1; (int)k <= i; ++k)
{
if (vv[k] > temp)
temp = vv[k];
next = k;
}
if (temp <= 0.0)
{
// only got K boxes
K = i + 1;
break;
}
}
tag = (byte*)Mem_Alloc( Sys.imagepool, 33 * 33 * 33 * sizeof(byte));
for (k = 0; (int)k < K; k++)
{
Mark(&cube[k], k, tag);
weight = Vol(&cube[k], wt);
if (weight)
{
lut_r[k] = (byte)(Vol(&cube[k], mr) / weight);
lut_g[k] = (byte)(Vol(&cube[k], mg) / weight);
lut_b[k] = (byte)(Vol(&cube[k], mb) / weight);
}
else
{
// bogus box
lut_r[k] = lut_g[k] = lut_b[k] = 0;
}
}
for (i = 0; i < (int)size; i++) NewData[i] = tag[Qadd[i]];
Mem_Free( tag );
Mem_Free( Qadd);
// fill palette
for (k = 0; k < numcolors; k++)
{
Palette[k * 3] = lut_b[k];
Palette[k * 3 + 1] = lut_g[k];
Palette[k * 3 + 2] = lut_r[k];
}
Mem_Free( Ig );
Mem_Free( Ib );
Mem_Free( Ir );
newpix = (rgbdata_t *)Mem_Alloc( Sys.imagepool, sizeof(rgbdata_t));
newpix->width = pix->width;
newpix->height = pix->height;
newpix->numLayers = pix->numLayers;
newpix->numMips = pix->numMips;
newpix->type = PF_INDEXED_24; // FIXME: do alpha-channel
newpix->flags = pix->flags;
newpix->palette = Palette;
newpix->buffer = NewData;
newpix->size = pix->width * pix->height * pix->numLayers;
//FS_FreeImage( pix ); // free old image
return newpix;
}

View File

@ -2,10 +2,10 @@
#define IDI_ICON1 101
#define VER_FILEVERSION 0,45
#define VER_FILEVERSION_STR "0.45"
#define VER_PRODUCTVERSION 0,45
#define VER_PRODUCTVERSION_STR "0.45"
#define VER_FILEVERSION 0,48
#define VER_FILEVERSION_STR "0.48"
#define VER_PRODUCTVERSION 0,48
#define VER_PRODUCTVERSION_STR "0.48"
#define VER_FILEFLAGSMASK VS_FF_PRERELEASE | VS_FF_PATCHED
#define VER_FILEFLAGS VS_FF_PRERELEASE

View File

@ -148,6 +148,10 @@ SOURCE=.\common\memlib.c
# End Source File
# Begin Source File
SOURCE=.\common\pallib.c
# End Source File
# Begin Source File
SOURCE=.\common\parselib.c
# End Source File
# Begin Source File

View File

@ -2,10 +2,10 @@
#define IDI_ICON1 101
#define VER_FILEVERSION 0,45
#define VER_FILEVERSION_STR "0.45"
#define VER_PRODUCTVERSION 0,45
#define VER_PRODUCTVERSION_STR "0.45"
#define VER_FILEVERSION 0,29
#define VER_FILEVERSION_STR "0.29"
#define VER_PRODUCTVERSION 0,29
#define VER_PRODUCTVERSION_STR "0.29"
#define VER_FILEFLAGSMASK VS_FF_PRERELEASE | VS_FF_PATCHED
#define VER_FILEFLAGS VS_FF_PRERELEASE

View File

@ -2,10 +2,10 @@
#define IDI_ICON1 101
#define VER_FILEVERSION 0,45
#define VER_FILEVERSION_STR "0.45"
#define VER_PRODUCTVERSION 0,45
#define VER_PRODUCTVERSION_STR "0.45"
#define VER_FILEVERSION 0,48
#define VER_FILEVERSION_STR "0.48"
#define VER_PRODUCTVERSION 0,48
#define VER_PRODUCTVERSION_STR "0.48"
#define VER_FILEFLAGSMASK VS_FF_PRERELEASE | VS_FF_PATCHED
#define VER_FILEFLAGS VS_FF_PRERELEASE

View File

@ -1699,17 +1699,19 @@ typedef struct tga_s
#define DDS_PITCH 0x00000008L
#define DDS_COMPLEX 0x00000008L
#define DDS_CUBEMAP 0x00000200L
#define DDS_TEXTURE 0x00001000L
#define DDS_MIPMAPCOUNT 0x00020000L
#define DDS_LINEARSIZE 0x00080000L
#define DDS_VOLUME 0x00200000L
#define DDS_MIPMAP 0x00400000L
#define DDS_DEPTH 0x00800000L
#define DDS_CUBEMAP_POSITIVEX 0x00000400L
#define DDS_CUBEMAP_NEGATIVEX 0x00000800L
#define DDS_CUBEMAP_POSITIVEY 0x00001000L
#define DDS_CUBEMAP_NEGATIVEY 0x00002000L
#define DDS_CUBEMAP_POSITIVEZ 0x00004000L
#define DDS_CUBEMAP_NEGATIVEZ 0x00008000L
#define DDS_MIPMAPCOUNT 0x00020000L
#define DDS_LINEARSIZE 0x00080000L
#define DDS_VOLUME 0x00200000L
#define DDS_MIPMAP 0x00400000L
#define DDS_DEPTH 0x00800000L
typedef struct dds_pf_s
{
@ -1743,7 +1745,9 @@ typedef struct
uint dwDepth; // depth if a volume texture
uint dwMipMapCount; // number of mip-map levels requested
uint dwAlphaBitDepth; // depth of alpha buffer requested
uint dwReserved1[10]; // reserved for future expansions
float fReflectivity[3]; // average reflectivity value
float fBumpScale; // bumpmapping scale factor
uint dwReserved1[6]; // reserved for future expansions
dds_pixf_t dsPixelFormat;
dds_caps_t dsCaps;
uint dwTextureStage;
@ -1893,6 +1897,8 @@ typedef struct rgbdata_s
uint flags; // misc image flags
byte *palette; // palette if present
byte *buffer; // image buffer
vec3_t color; // radiocity reflectivity
float bump_scale; // internal bumpscale
uint size; // for bounds checking
} rgbdata_t;