avp/src/win95/d3_image.cpp
2019-08-20 02:09:04 +02:00

2491 lines
52 KiB
C++

#include <stdlib.h>
#include <string.h>
#include "d3_image.hpp"
#include "platform.h"
// define = 1 if you dont have pre-quantized images to load into palettized D3D texture surfaces
#define QUANTISE_ON_LOAD 0
#if debug
#define DEBUG_TRANSPARENCY 1
#else
#define DEBUG_TRANSPARENCY 0
#endif
// image loader stuff
#if OUTPUT_LOG
#include "debuglog.hpp"
LogFile CL_LogFile("DB_IMLD.LOG");
#endif
#if TIME_LOADS
unsigned int OpenTime;
unsigned int CloseTime;
unsigned int ReadTime;
#endif
static inline unsigned long read_le_dword(D3I_FILE * f)
{
unsigned char d1,d2,d3,d4;
d3i_fread(&d1,1,1,f);
d3i_fread(&d2,1,1,f);
d3i_fread(&d3,1,1,f);
d3i_fread(&d4,1,1,f);
return (unsigned long)d1 | (unsigned long)d2<<8 | (unsigned long)d3<<16 | (unsigned long)d4<<24;
}
static inline unsigned short read_le_word(D3I_FILE * f)
{
unsigned char d1,d2;
d3i_fread(&d1,1,1,f);
d3i_fread(&d2,1,1,f);
return (unsigned short)d1 | (unsigned short)((unsigned short)d2<<8);
}
// some default globals
CL_ImageMode CL_Image::imode = CLM_GLOBALPALETTE;
CL_ImageMode CL_Image::imode_d3d = CLM_ATTACHEDPALETTE;
CL_ImageMode CL_Image::imode_ddraw = CLM_GLOBALPALETTE;
LPDDSURFACEDESC CL_Image::format = 0;
unsigned int CL_Image::bitsperpixel;
unsigned int CL_Image::bitsperpixel_d3d;
unsigned int CL_Image::bitsperpixel_ddraw;
CL_LoadMode CL_Image::lmode = CLL_DDSURFACE;
CL_DX_Format CL_Pixel_32::f;
CL_DX_Format CL_Pixel_32::f_d3d;
CL_DX_Format CL_Pixel_32::f_ddraw;
CL_DX_Format CL_Pixel_16::f;
CL_DX_Format CL_Pixel_16::f_d3d;
CL_DX_Format CL_Pixel_16::f_ddraw;
void CL_Image::DeleteNotMips(void)
{
if (im24)
{
delete[] *im24;
delete[] im24;
im24 = 0;
}
if (im16)
{
delete[] *im16;
delete[] im16;
im16 = 0;
}
if (im8)
{
delete[] *im8;
delete[] im8;
im8 = 0;
}
if (palette)
{
delete[] palette;
palette = 0;
palette_size = 0;
}
flags.loaded = 0;
flags.raw16bit = 0;
}
void CL_Image::Delete(void)
{
DeleteNotMips();
while (mipmaps.size())
{
delete mipmaps.last_entry();
mipmaps.delete_last_entry();
}
}
void CL_Image::Copy(CL_Image const & i2)
{
if (i2.im24)
{
im24 = new CL_Pixel_24 * [height];
*im24 = new CL_Pixel_24 [size];
CL_Pixel_24 const * sptr = *i2.im24;
CL_Pixel_24 * dptr = *im24;
for (int i=size; i; --i)
{
*dptr++ = *sptr++;
}
CL_Pixel_24 const * * dptrptr = (CL_Pixel_24 const * *) im24;
CL_Pixel_24 const * aptr = *im24;
for (i=height; i; --i, aptr+=width)
{
*dptrptr++ = aptr;
}
}
if (i2.im16)
{
im16 = new CL_Pixel_16 * [height];
*im16 = new CL_Pixel_16 [size];
CL_Pixel_16 const * sptr = *i2.im16;
CL_Pixel_16 * dptr = *im16;
for (int i=size; i; --i)
{
*dptr++ = *sptr++;
}
CL_Pixel_16 const * * dptrptr = (CL_Pixel_16 const * *) im16;
CL_Pixel_16 const * aptr = *im16;
for (i=height; i; --i, aptr+=width)
{
*dptrptr++ = aptr;
}
}
if (i2.im8)
{
im8 = new unsigned char * [height];
*im8 = new unsigned char [size];
unsigned char const * sptr = *i2.im8;
unsigned char * dptr = *im8;
for (int i=size; i; --i)
{
*dptr++ = *sptr++;
}
unsigned char const * * dptrptr = (unsigned char const * *) im8;
unsigned char const * aptr = *im8;
for (i=height; i; --i, aptr+=width)
{
*dptrptr++ = aptr;
}
}
if (i2.palette)
{
palette = new CL_Pixel_24 [palette_size];
CL_Pixel_24 const * sptr = i2.palette;
CL_Pixel_24 * dptr = palette;
for (int i=palette_size; i; --i)
{
*dptr++ = *sptr;
}
}
if (i2.name)
{
name = new char [strlen(i2.name)+1];
strcpy(name,i2.name);
}
if (i2.fname)
{
fname = new char [strlen(i2.fname)+1];
strcpy(fname,i2.fname);
}
for (CLIF<CL_Image *> i_mip(&i2.mipmaps); !i_mip.done(); i_mip.next())
{
mipmaps.add_entry_end(new CL_Image(*i_mip()));
}
}
CL_Image::~CL_Image()
{
Delete();
if (fname) delete[] fname;
if (name) delete[] name;
}
CL_Image::CL_Image(CL_Image const & i2)
: width(i2.width)
, height(i2.height)
, size(i2.size)
, im24(0)
, im16(0)
, im8(0)
, palette(0)
, palette_size(i2.palette_size)
, fname(0)
, name(0)
, flags(i2.flags)
{
Copy(i2);
}
CL_Image & CL_Image::operator = (CL_Image const & i2)
{
if (&i2 != this)
{
Delete();
if (fname) delete[] fname;
if (name) delete[] name;
width = i2.width;
height = i2.height;
size = i2.size;
palette_size = i2.palette_size;
fname = 0;
name = 0;
flags = i2.flags;
Copy(i2);
}
return *this;
}
CL_Error CL_Image::Make(int const _width, int const _height)
{
Delete();
width = _width;
height = _height;
palette_size = 0;
size = width*height;
switch (imode)
{
case CLM_ATTACHEDPALETTE:
{
palette_size = 256;
palette = new CL_Pixel_24 [palette_size];
}
case CLM_GLOBALPALETTE:
case CLM_TLTPALETTE:
{
im8 = new unsigned char * [height];
*im8 = new unsigned char [size];
unsigned char const * * dptrptr = (unsigned char const * *) im8;
unsigned char * dptr = *im8;
for (int i=height; i; --i, dptr+=width)
{
*dptrptr++ = dptr;
}
break;
}
case CLM_24BIT:
{
im24 = new CL_Pixel_24 * [height];
*im24 = new CL_Pixel_24 [size];
CL_Pixel_24 const * * dptrptr = (CL_Pixel_24 const * *) im24;
CL_Pixel_24 const * aptr = *im24;
for (int i=height; i; --i, aptr+=width)
{
*dptrptr++ = aptr;
}
break;
}
case CLM_16BIT:
{
im16 = new CL_Pixel_16 * [height];
*im16 = new CL_Pixel_16 [size];
CL_Pixel_16 const * * dptrptr = (CL_Pixel_16 const * *) im16;
CL_Pixel_16 const * aptr = *im16;
for (int i=height; i; --i, aptr+=width)
{
*dptrptr++ = aptr;
}
break;
}
case CLM_32BIT:
{
im32 = new CL_Pixel_32 * [height];
*im32 = new CL_Pixel_32 [size];
CL_Pixel_32 const * * dptrptr = (CL_Pixel_32 const * *) im32;
CL_Pixel_32 const * aptr = *im32;
for (int i=height; i; --i, aptr+=width)
{
*dptrptr++ = aptr;
}
break;
}
default:
return CLE_INVALIDDXMODE;
}
flags.loaded = 1;
return CLE_OK;
}
CL_Error CL_Image::MakeRandom(int const _width, int const _height, int const seed)
{
Delete();
if (-1!=seed) srand((unsigned int)seed);
width = _width;
height = _height;
size = width*height;
palette_size = 0;
switch (imode)
{
case CLM_ATTACHEDPALETTE:
{
palette_size = 256;
palette = new CL_Pixel_24 [palette_size];
CL_Pixel_24 * palP = palette;
for (int i=palette_size; i; --i, ++palP)
{
*palP = CL_Pixel_24(rand()&255,rand()&255,rand()&255);
}
}
case CLM_GLOBALPALETTE:
case CLM_TLTPALETTE:
{
im8 = new unsigned char * [height];
*im8 = new unsigned char [size];
unsigned char const * * dptrptr = (unsigned char const * *) im8;
unsigned char * dptr = *im8;
for (int i=height; i; --i)
{
*dptrptr++ = dptr;
for (int j=width; j; --j, ++dptr)
*dptr = (unsigned char)(rand()&(palette_size-1));
}
break;
}
case CLM_24BIT:
{
im24 = new CL_Pixel_24 * [height];
*im24 = new CL_Pixel_24 [size];
CL_Pixel_24 const * * dptrptr = (CL_Pixel_24 const * *) im24;
CL_Pixel_24 * aptr = *im24;
for (int i=height; i; --i)
{
*dptrptr++ = aptr;
for (int j=width; j; --j, ++aptr)
*aptr = CL_Pixel_24(rand()&255,rand()&255,rand()&255);
}
break;
}
case CLM_16BIT:
{
im16 = new CL_Pixel_16 * [height];
*im16 = new CL_Pixel_16 [size];
CL_Pixel_16 const * * dptrptr = (CL_Pixel_16 const * *) im16;
CL_Pixel_16 * aptr = *im16;
for (int i=height; i; --i)
{
*dptrptr++ = aptr;
for (int j=width; j; --j, ++aptr)
*aptr = CL_Pixel_24(rand()&255,rand()&255,rand()&255);
}
break;
}
case CLM_32BIT:
{
im32 = new CL_Pixel_32 * [height];
*im32 = new CL_Pixel_32 [size];
CL_Pixel_32 const * * dptrptr = (CL_Pixel_32 const * *) im32;
CL_Pixel_32 * aptr = *im32;
for (int i=height; i; --i)
{
*dptrptr++ = aptr;
for (int j=width; j; --j, ++aptr)
*aptr = CL_Pixel_24(rand()&255,rand()&255,rand()&255);
}
break;
}
default:
return CLE_INVALIDDXMODE;
}
flags.loaded = 1;
return CLE_OK;
}
CL_Error CL_Image::Load_BMP(D3I_FILE * f)
{
if (!f) return CLE_OPENERROR;
DeleteNotMips();
switch (imode)
{
case CLM_ATTACHEDPALETTE:
case CLM_24BIT:
case CLM_16BIT:
case CLM_32BIT:
break;
default:
return CLE_INVALIDDXMODE;
}
d3i_fseek(f,0,SEEK_SET);
unsigned short magic;
d3i_fread(&magic,2,1,f);
if (magic != *(unsigned short const *)"BM") return CLE_LOADERROR;
size_t filesize = read_le_dword(f);
d3i_fseek(f,4,SEEK_CUR);
size_t offset = read_le_dword(f);
size_t headsize = read_le_dword(f);
unsigned short bitdepth;
if (12 == headsize) // OS/2 1.x
{
width = read_le_word(f);
height = read_le_word(f);
size = width * height;
if (!width || !height) return CLE_LOADERROR;
unsigned short planes = read_le_word(f);
if (1 != planes) return CLE_LOADERROR;
bitdepth = read_le_word(f);
if (bitdepth != 24)
{
palette_size = 1<<bitdepth;
palette = new CL_Pixel_24 [palette_size];
CL_Pixel_24 * rptr = palette;
for (int i=palette_size; i; --i)
{
rptr++->Read(f,CLF_BGR);
}
}
}
else if (40 == headsize || 64 == headsize) // Windows 3.x || OS/2 2.x
{
width = read_le_dword(f);
height = read_le_dword(f);
unsigned short planes = read_le_word(f);
if (1 != planes) return CLE_LOADERROR;
bitdepth = read_le_word(f);
if (read_le_dword(f)) return CLE_LOADERROR; // compressed bmps not supported
size = read_le_dword(f);
d3i_fseek(f,8,SEEK_CUR);
palette_size = read_le_dword(f);
if (!palette_size && bitdepth != 24) palette_size = 1<<bitdepth;
if (palette_size)
{
d3i_fseek(f,headsize+14,SEEK_SET);
palette = new CL_Pixel_24 [palette_size];
CL_Pixel_24 * rptr = palette;
for (int i=palette_size; i; --i)
{
rptr++->Read(f,CLF_BGRX);
}
}
}
else return CLE_LOADERROR;
d3i_fseek(f,offset,SEEK_SET);
if (palette_size)
{
if (bitdepth < 4)
{
delete[] palette;
palette_size = 0;
return CLE_LOADERROR;
}
im8 = new unsigned char * [height];
*im8 = new unsigned char [size];
unsigned char const * * dptrptr = (unsigned char const * *) im8;
unsigned char * dptr = *im8;
for (int i=height; i; --i, dptr+=width)
{
*dptrptr++ = dptr;
}
#if DEBUG_TRANSPARENCY
int num_tps=0;
#endif
for (i=height-1; i>=0; --i)
{
dptr = im8[i];
if (4==bitdepth)
{
for (int i=width>>1; i; --i)
{
unsigned char byte;
d3i_fread(&byte,1,1,f);
#if DEBUG_TRANSPARENCY
*dptr = (unsigned char)(byte >> 4 & 0xf);
if (!*dptr) num_tps++;
*++dptr = (unsigned char)(byte & 0xf);
if (!*dptr) num_tps++;
++dptr;
#else
*dptr++ = (unsigned char)(byte >> 4 & 0xf);
*dptr++ = (unsigned char)(byte & 0xf);
#endif
}
if (width & 1)
{
d3i_fread(dptr,1,1,f);
*dptr &= 0xf;
#if DEBUG_TRANSPARENCY
if (!*dptr) num_tps++;
#endif
}
d3i_fseek(f,(~(width-1) & 7)>>1,SEEK_CUR);
}
else
{
for (int i=width; i; --i)
{
#if DEBUG_TRANSPARENCY
d3i_fread(dptr,1,1,f);
if (!*dptr) num_tps++;
dptr++;
#else
d3i_fread(dptr++,1,1,f);
#endif
}
d3i_fseek(f,~(width-1) & 3,SEEK_CUR);
}
}
#if DEBUG_TRANSPARENCY
if (num_tps) CL_LogFile.lprintf("-- %d TRANSPARENT PIXELS FOUND\n",num_tps);
#endif
}
else if (CLM_16BIT == imode)
{
im16 = new CL_Pixel_16 * [height];
*im16 = new CL_Pixel_16 [size];
CL_Pixel_16 const * * dptrptr = (CL_Pixel_16 const * *) im16;
CL_Pixel_16 const * aptr = *im16;
for (int i=height; i; --i, aptr+=width)
{
*dptrptr++ = aptr;
}
for (i=height-1; i>=0; --i)
{
CL_Pixel_16 * dptr = im16[i];
for (int i=width; i; --i)
{
dptr++->Read(f,CLF_BGR);
}
d3i_fseek(f,~(width*3-1) & 3,SEEK_CUR);
}
}
else if (CLM_32BIT == imode)
{
im32 = new CL_Pixel_32 * [height];
*im32 = new CL_Pixel_32 [size];
CL_Pixel_32 const * * dptrptr = (CL_Pixel_32 const * *) im32;
CL_Pixel_32 const * aptr = *im32;
for (int i=height; i; --i, aptr+=width)
{
*dptrptr++ = aptr;
}
for (i=height-1; i>=0; --i)
{
CL_Pixel_32 * dptr = im32[i];
for (int i=width; i; --i)
{
dptr++->Read(f,CLF_BGR);
}
d3i_fseek(f,~(width*3-1) & 3,SEEK_CUR);
}
}
else if (CLM_24BIT == imode)
{
im24 = new CL_Pixel_24 * [height];
*im24 = new CL_Pixel_24 [size];
CL_Pixel_24 const * * dptrptr = (CL_Pixel_24 const * *) im24;
CL_Pixel_24 const * aptr = *im24;
for (int i=height; i; --i, aptr+=width)
{
*dptrptr++ = aptr;
}
for (i=height-1; i>=0; --i)
{
CL_Pixel_24 * dptr = im24[i];
for (int i=width; i; --i)
{
dptr++->Read(f,CLF_BGR);
}
d3i_fseek(f,~(width*3-1) & 3,SEEK_CUR);
}
}
else return CLE_INVALIDDXMODE;
if (palette_size && CLM_ATTACHEDPALETTE != imode)
{
if (CLM_16BIT == imode)
{
im16 = new CL_Pixel_16 * [height];
*im16 = new CL_Pixel_16 [size];
unsigned char const * sptr = *im8;
CL_Pixel_16 * dptr = *im16;
for (int i=size; i; --i, ++dptr,++sptr)
{
if (*sptr && !palette[*sptr])
*dptr = dptr->f.dx_black;
else
*dptr = palette[*sptr];
}
CL_Pixel_16 const * * dptrptr = (CL_Pixel_16 const * *) im16;
CL_Pixel_16 const * aptr = *im16;
for (i=height; i; --i, aptr+=width)
{
*dptrptr++ = aptr;
}
}
else if (CLM_32BIT == imode)
{
im32 = new CL_Pixel_32 * [height];
*im32 = new CL_Pixel_32 [size];
unsigned char const * sptr = *im8;
CL_Pixel_32 * dptr = *im32;
for (int i=size; i; --i, ++dptr,++sptr)
{
if (*sptr && !palette[*sptr])
*dptr = dptr->f.dx_black;
else
*dptr = palette[*sptr];
}
CL_Pixel_32 const * * dptrptr = (CL_Pixel_32 const * *) im32;
CL_Pixel_32 const * aptr = *im32;
for (i=height; i; --i, aptr+=width)
{
*dptrptr++ = aptr;
}
}
else
{
im24 = new CL_Pixel_24 * [height];
*im24 = new CL_Pixel_24 [size];
unsigned char const * sptr = *im8;
CL_Pixel_24 * dptr = *im24;
for (int i=size; i; --i, ++dptr,++sptr)
{
if (*sptr && !palette[*sptr])
*dptr = CL_Pixel_24(0,1,0);
else
*dptr = palette[*sptr];
}
CL_Pixel_24 const * * dptrptr = (CL_Pixel_24 const * *) im24;
CL_Pixel_24 const * aptr = *im24;
for (i=height; i; --i, aptr+=width)
{
*dptrptr++ = aptr;
}
}
delete[] palette;
palette = 0;
palette_size = 0;
delete[] *im8;
delete[] im8;
im8 = 0;
}
if (d3i_ferror(f) || d3i_feof(f))
{
DeleteNotMips();
return CLE_LOADERROR;
}
flags.loaded = 1;
return CLE_OK;
}
CL_Error CL_Image::Load_PPM(D3I_FILE * f)
{
if (!f) return CLE_OPENERROR;
DeleteNotMips();
switch (imode)
{
case CLM_24BIT:
case CLM_16BIT:
case CLM_32BIT:
break;
default:
return CLE_INVALIDDXMODE;
}
d3i_fseek(f,0,SEEK_SET);
unsigned short magic;
d3i_fread(&magic,2,1,f);
if (magic != *(unsigned short const *)"P6") return CLE_LOADERROR;
d3i_fseek(f,1,SEEK_CUR);
char buf[256];
char * bufptr = buf;
unsigned int fields[5];
unsigned int fields_read = 0;
while (fields_read < 3 && bufptr)
{
do bufptr = d3i_fgets(buf, sizeof buf, f);
while ('#'==buf[0] && bufptr);
int num_fields_read = sscanf(buf,"%u %u %u",&fields[fields_read],&fields[fields_read+1],&fields[fields_read+2]);
if (EOF != num_fields_read) fields_read += num_fields_read;
}
if (fields_read < 3) return CLE_LOADERROR;
width = fields[0];
height = fields[1];
unsigned int maxval = fields[2];
if (maxval > 255) return CLE_LOADERROR;
size = width * height;
if (CLM_16BIT == imode)
{
im16 = new CL_Pixel_16 * [height];
*im16 = new CL_Pixel_16 [size];
CL_Pixel_16 * rptr = *im16;
for (int i=size; i; --i)
{
rptr++->Read(f,CLF_RGB,maxval);
}
CL_Pixel_16 const * * dptrptr = (CL_Pixel_16 const * *) im16;
CL_Pixel_16 const * aptr = *im16;
for (i=height; i; --i, aptr+=width)
{
*dptrptr++ = aptr;
}
}
else if (CLM_32BIT == imode)
{
im32 = new CL_Pixel_32 * [height];
*im32 = new CL_Pixel_32 [size];
CL_Pixel_32 * rptr = *im32;
for (int i=size; i; --i)
{
rptr++->Read(f,CLF_RGB,maxval);
}
CL_Pixel_32 const * * dptrptr = (CL_Pixel_32 const * *) im32;
CL_Pixel_32 const * aptr = *im32;
for (i=height; i; --i, aptr+=width)
{
*dptrptr++ = aptr;
}
}
else
{
im24 = new CL_Pixel_24 * [height];
*im24 = new CL_Pixel_24 [size];
CL_Pixel_24 * rptr = *im24;
for (int i=size; i; --i)
{
rptr++->Read(f,CLF_RGB,maxval);
}
CL_Pixel_24 const * * dptrptr = (CL_Pixel_24 const * *) im24;
CL_Pixel_24 const * aptr = *im24;
for (i=height; i; --i, aptr+=width)
{
*dptrptr++ = aptr;
}
}
if (d3i_ferror(f) || d3i_feof(f))
{
DeleteNotMips();
return CLE_LOADERROR;
}
flags.loaded = 1;
return CLE_OK;
}
CL_Error CL_Image::Load_PGM(D3I_FILE * f)
{
if (!f) return CLE_OPENERROR;
DeleteNotMips();
switch (imode)
{
case CLM_GLOBALPALETTE:
case CLM_TLTPALETTE:
break;
default:
return CLE_INVALIDDXMODE;
}
d3i_fseek(f,0,SEEK_SET);
unsigned short magic;
d3i_fread(&magic,2,1,f);
if (magic != *(unsigned short const *)"P5") return CLE_LOADERROR;
d3i_fseek(f,1,SEEK_CUR);
char buf[256];
char * bufptr = buf;
unsigned int fields[5];
unsigned int fields_read = 0;
while (fields_read < 3 && bufptr)
{
do bufptr = d3i_fgets(buf, sizeof buf, f);
while ('#'==buf[0] && bufptr);
int num_fields_read = sscanf(buf,"%u %u %u",&fields[fields_read],&fields[fields_read+1],&fields[fields_read+2]);
if (EOF != num_fields_read) fields_read += num_fields_read;
}
if (fields_read < 3) return CLE_LOADERROR;
width = fields[0];
height = fields[1];
unsigned int maxval = fields[2];
if (maxval > 255) return CLE_LOADERROR;
size = width * height;
im8 = new unsigned char * [height];
*im8 = new unsigned char [size];
d3i_fread(*im8,1,size,f);
unsigned char const * * dptrptr = (unsigned char const * *) im8;
unsigned char const * aptr = *im8;
for (int i=height; i; --i, aptr+=width)
{
*dptrptr++ = aptr;
}
if (d3i_ferror(f) || d3i_feof(f))
{
DeleteNotMips();
return CLE_LOADERROR;
}
flags.loaded = 1;
return CLE_OK;
}
CL_Error CL_Image::Load_PWM(D3I_FILE * f)
{
if (!f) return CLE_OPENERROR;
DeleteNotMips();
switch (imode)
{
case CLM_TLTPALETTE:
break;
default:
return CLE_INVALIDDXMODE;
}
d3i_fseek(f,0,SEEK_SET);
unsigned short magic;
d3i_fread(&magic,2,1,f);
if (magic != *(unsigned short const *)"P8") return CLE_LOADERROR;
d3i_fseek(f,1,SEEK_CUR);
char buf[256];
char * bufptr = buf;
unsigned int fields[5];
unsigned int fields_read = 0;
while (fields_read < 3 && bufptr)
{
do bufptr = d3i_fgets(buf, sizeof buf, f);
while ('#'==buf[0] && bufptr);
int num_fields_read = sscanf(buf,"%u %u %u",&fields[fields_read],&fields[fields_read+1],&fields[fields_read+2]);
if (EOF != num_fields_read) fields_read += num_fields_read;
}
if (fields_read < 3) return CLE_LOADERROR;
width = fields[0];
height = fields[1];
unsigned int maxval = fields[2];
if (maxval > 65535) return CLE_LOADERROR;
size = width * height;
im16raw = new unsigned short * [height];
*im16raw = new unsigned short [size];
d3i_fread(*im16raw,2,size,f);
unsigned short const * * dptrptr = (unsigned short const * *) im16raw;
unsigned short const * aptr = *im16raw;
for (int i=height; i; --i, aptr+=width)
{
*dptrptr++ = aptr;
}
if (d3i_ferror(f) || d3i_feof(f))
{
DeleteNotMips();
return CLE_LOADERROR;
}
flags.loaded = 1;
flags.raw16bit = 1;
return CLE_OK;
}
CL_Error CL_Image::GetBitsPerPixel(unsigned int* bpp)
{
*bpp = bitsperpixel;
return CLE_OK;
}
CL_Error CL_Image::Load_Image(D3I_FILE * f)
{
if (!f) return CLE_OPENERROR;
CL_Error retval = CLE_LOADERROR;
START_TIMER
d3i_fseek(f,0,SEEK_SET);
unsigned short magic;
d3i_fread(&magic,2,1,f);
if ( *(unsigned short const *)"BM" == magic)
retval = Load_BMP(f);
else if ( *(unsigned short const *)"P6" == magic)
retval = Load_PPM(f);
else if ( *(unsigned short const *)"P5" == magic)
retval = Load_PGM(f);
else if ( *(unsigned short const *)"P8" == magic)
retval = Load_PWM(f);
#if OUTPUT_LOG
else CL_LogFile.lputs("** ERROR: Not a recognized file format\n");
#endif
END_TIMER(ReadTime)
#if OUTPUT_LOG
#if TIME_LOADS
CL_LogFile.lprintf("-- Timer Stats (ms): Open %d Read %d Close %d\n",OpenTime,ReadTime,CloseTime);
#endif
#endif
return retval;
}
void CL_Image::PadTo(int const width_unit,int const height_unit)
{
if (!flags.loaded) return;
unsigned int const owidth = width;
unsigned int const oheight = height;
width += width_unit-1;
width &= ~(width_unit-1);
height += height_unit-1;
height &= ~(height_unit-1);
if (width == owidth && height == oheight) return; // already in spec
size = width * height;
CL_Pixel_32 * * const oim32 = im32;
CL_Pixel_16 * * const oim16 = im16;
unsigned char * * const oim8 = im8;
if (oim32)
{
im32 = new CL_Pixel_32 * [height];
*im32 = new CL_Pixel_32 [size];
CL_Pixel_32 const * * dptrptr = (CL_Pixel_32 const * *) im32;
CL_Pixel_32 const * aptr = *im32;
for (unsigned int i=height; i; --i, aptr+=width)
{
*dptrptr++ = aptr;
}
for (unsigned int y = 0; y < oheight; ++y)
{
CL_Pixel_32 const * sptr = oim32[y];
CL_Pixel_32 * dptr = im32[y];
for (unsigned int x = 0; x < owidth; ++x, ++dptr, ++sptr)
*dptr = *sptr;
for (; x < width; ++x, ++dptr)
*dptr = CL_Pixel_24(0,0,0);
}
for (; y < height; ++y)
{
CL_Pixel_32 * dptr = im32[y];
for (unsigned int x = 0; x < width; ++x, ++dptr)
*dptr = CL_Pixel_24(0,0,0);
}
delete[] *oim32;
delete[] oim32;
}
if (oim16)
{
im16 = new CL_Pixel_16 * [height];
*im16 = new CL_Pixel_16 [size];
CL_Pixel_16 const * * dptrptr = (CL_Pixel_16 const * *) im16;
CL_Pixel_16 const * aptr = *im16;
for (unsigned int i=height; i; --i, aptr+=width)
{
*dptrptr++ = aptr;
}
for (unsigned int y = 0; y < oheight; ++y)
{
CL_Pixel_16 const * sptr = oim16[y];
CL_Pixel_16 * dptr = im16[y];
for (unsigned int x = 0; x < owidth; ++x, ++dptr, ++sptr)
*dptr = *sptr;
for (; x < width; ++x, ++dptr)
*dptr = CL_Pixel_24(0,0,0);
}
for (; y < height; ++y)
{
CL_Pixel_16 * dptr = im16[y];
for (unsigned int x = 0; x < width; ++x, ++dptr)
*dptr = CL_Pixel_24(0,0,0);
}
delete[] *oim16;
delete[] oim16;
}
if (oim8)
{
im8 = new unsigned char * [height];
*im8 = new unsigned char [size];
unsigned char const * * dptrptr = (unsigned char const * *) im8;
unsigned char const * aptr = *im8;
for (unsigned int i=height; i; --i, aptr+=width)
{
*dptrptr++ = aptr;
}
for (unsigned int y = 0; y < oheight; ++y)
{
unsigned char const * sptr = oim8[y];
unsigned char * dptr = im8[y];
for (unsigned int x = 0; x < owidth; ++x, ++dptr, ++sptr)
*dptr = *sptr;
for (; x < width; ++x, ++dptr)
*dptr = 0;
}
for (; y < height; ++y)
{
unsigned char * dptr = im8[y];
for (unsigned int x = 0; x < width; ++x, ++dptr)
*dptr = 0;
}
delete[] *oim8;
delete[] oim8;
}
}
// implementation of the 'popularity algorithm' for reducing palette
unsigned int CL_Pixel_24::FVD_Distance(CL_Pixel_24 const & p2) const
{
unsigned int dg = g > p2.g ? g - p2.g : p2.g - g;
unsigned int db = b > p2.b ? b - p2.b : p2.b - b;
unsigned int dgb = dg + db + ((dg > db ? dg : db)<<1);
unsigned int dr = r > p2.r ? r - p2.r : p2.r - r;
dr += dr<<1; // *= 3
return dr + dgb + ((dr > dgb ? dr : dgb)<<1);
}
struct _CL_palpixcnt
{
unsigned int cnt;
unsigned int palpos;
};
int _CL_cmp_palpixcnt(void const * e1, void const * e2)
{
return ((_CL_palpixcnt *)e2)->cnt - ((_CL_palpixcnt *)e1)->cnt;
}
CL_Error CL_Image::ReducePalette(unsigned int const num_colours)
{
if (!flags.loaded) return CLE_LOADERROR;
if (!palette) return CLE_INVALIDDXMODE;
if (!im8) return CLE_INVALIDDXMODE;
if (palette_size <= num_colours) return CLE_OK;
_CL_palpixcnt * const sortarray = new _CL_palpixcnt[palette_size];
for (unsigned int i=0; i<palette_size; ++i)
{
sortarray[i].cnt = 0;
sortarray[i].palpos = i;
}
for (unsigned int y=0; y<height; ++y)
{
unsigned char const * sptr = im8[y];
for (unsigned int x=width; x; --x, ++sptr)
++ sortarray[*sptr].cnt;
}
unsigned int const palstart = CL_Pixel_24(0,0,0) == palette[0] ? 1 : 0;
qsort(sortarray + palstart, palette_size - palstart, sizeof(_CL_palpixcnt), _CL_cmp_palpixcnt);
CL_Pixel_24 * const opalette = palette;
unsigned int const opalette_size = palette_size;
palette = new CL_Pixel_24[num_colours];
palette_size = num_colours;
for (i=0; i<palette_size; ++i)
palette[i] = opalette[sortarray[i].palpos];
delete[] sortarray;
// umm - if theres black thats not transparent, make sure it doesnt become colour 0 in palette
for (unsigned int pswap=1; pswap<palette_size && 0==palstart && CL_Pixel_24(0,0,0)==palette[0]; ++pswap)
{
palette[0]=palette[pswap];
palette[pswap]=CL_Pixel_24(0,0,0);
}
// emergency?
if (0==palstart && CL_Pixel_24(0,0,0)==palette[0])
palette[0] = CL_Pixel_24(1,1,1);
unsigned int * const palmaps = new unsigned int [opalette_size];
palmaps[0] = 0;
for (i=palstart; i<opalette_size; ++i)
{
unsigned int closest = palstart;
unsigned int distance = opalette[i].FVD_Distance(palette[palstart]);
for (unsigned int j = palstart+1; j<palette_size; ++j)
{
unsigned int thisdist = opalette[i].FVD_Distance(palette[j]);
if (thisdist < distance)
{
distance = thisdist;
closest = j;
}
}
palmaps[i] = closest;
}
delete[] opalette;
for (y=0; y<height; ++y)
{
unsigned char * sptr = im8[y];
for (unsigned int x=width; x; --x, ++sptr)
*sptr = (unsigned char)palmaps[*sptr];
}
delete[] palmaps;
return CLE_OK;
}
// useful filename handling functions
// returns pointer into string pointing to filename without dirname
static char const * strip_path(char const * n)
{
char const * rm = strrchr(n,':');
if (rm) n = rm+1;
rm = strrchr(n,'/');
if (rm) n = rm+1;
rm = strrchr(n,'\\');
if (rm) n = rm+1;
return n;
}
#if 0
static char * strip_path(char * n)
{
char * rm = strrchr(n,':');
if (rm) n = rm+1;
rm = strrchr(n,'/');
if (rm) n = rm+1;
rm = strrchr(n,'\\');
if (rm) n = rm+1;
return n;
}
#endif
// removes any .extension from filename by inserting null character
static void strip_file_extension(char * n)
{
char * dotpos = strrchr(n,'.');
if (dotpos) *dotpos = 0;
}
static char * strip_file_extension(char const * n)
{
char * nn = new char[strlen(n)+1];
strcpy(nn,n);
strip_file_extension(nn);
return nn;
}
// CL_Image functions
CL_Error CL_Image::Locate(char const * iname, int const /* enum_id */)
{
if (!iname) return CLE_RIFFERROR;
if (name)
delete[] name;
name = strip_file_extension(strip_path(iname));
if (fname)
delete[] fname;
fname = new char[strlen(iname)+1];
strcpy(fname,iname);
flags.located = 1;
return CLE_OK;
}
CL_Error CL_Image::Load(char const * const iname, int const enum_id)
{
if (iname || CL_EID_INVALID != enum_id || !flags.located)
{
CL_Error locate_err = Locate(iname,enum_id);
if (locate_err != CLE_OK)
{
EXITONLOCATEFAIL(locate_err,iname,enum_id)
return locate_err;
}
}
#if OUTPUT_LOG
char const * texformat;
unsigned int redbits = 8;
unsigned int greenbits = 8;
unsigned int bluebits = 8;
switch (imode)
{
case CLM_GLOBALPALETTE:
texformat = "Palettized Display";
break;
case CLM_TLTPALETTE:
texformat = "Palettized Display with Abstract TLT Palette";
break;
case CLM_ATTACHEDPALETTE:
texformat = "Palettized Textures";
break;
case CLM_32BIT:
texformat = "32BIT DX";
redbits += CL_Pixel_32::f.red_bits_gt8;
redbits -= CL_Pixel_32::f.red_bits_lt8;
greenbits += CL_Pixel_32::f.green_bits_gt8;
greenbits -= CL_Pixel_32::f.green_bits_lt8;
bluebits += CL_Pixel_32::f.blue_bits_gt8;
bluebits -= CL_Pixel_32::f.blue_bits_lt8;
break;
case CLM_24BIT:
texformat = "24-bit for runtime conversion";
break;
case CLM_16BIT:
texformat = "16BIT DX";
redbits += CL_Pixel_16::f.red_bits_gt8;
redbits -= CL_Pixel_16::f.red_bits_lt8;
greenbits += CL_Pixel_16::f.green_bits_gt8;
greenbits -= CL_Pixel_16::f.green_bits_lt8;
bluebits += CL_Pixel_16::f.blue_bits_gt8;
bluebits -= CL_Pixel_16::f.blue_bits_lt8;
break;
}
CL_LogFile.lprintf("--%s %s (%u-bit, %u-%u-%u)\n",fname,texformat,bitsperpixel,redbits,greenbits,bluebits);
#endif
START_TIMER
D3I_FILE * fp = d3i_fopen(fname,"rb");
END_TIMER(OpenTime)
if (fp)
{
CL_Error retval = Load_Image(fp);
START_TIMER
d3i_fclose(fp);
END_TIMER(CloseTime)
if (CLE_OK != retval)
{
#if OUTPUT_LOG
CL_LogFile.lputs("** ERROR: unable to read\n");
#endif
EXITONREADFAIL(fname)
}
return retval;
}
#if OUTPUT_LOG
CL_LogFile.lputs("** ERROR: unable to open\n");
#endif
EXITONLOADFAIL(fname)
return CLE_OPENERROR;
}
CL_Error CL_Image::PreLoad(char const * const iname, int const enum_id)
{
CL_Error locate_err = Locate(iname,enum_id);
if (locate_err != CLE_OK)
{
return locate_err;
}
return CLE_OK;
}
CL_Error CL_Image::LoadMipMaps(int const n_mips) // assumes locating has been done (ie. PreLoad or Load has been called)
{
if (!flags.located) return CLE_FINDERROR;
while (mipmaps.size())
{
delete mipmaps.last_entry();
mipmaps.delete_last_entry();
}
for (int mip_idx = 1; mip_idx < n_mips; ++mip_idx)
{
CL_Image * mip_n = new CL_Image;
mip_n->name = new char[strlen(name)+1];
mip_n->fname = new char[strlen(fname)+1];
strcpy(mip_n->name,name);
strcpy(mip_n->fname,fname);
char * dotpos = strrchr(mip_n->fname,'.');
if (!dotpos)
{
delete mip_n;
break;
}
sprintf(dotpos+3,"%1d",mip_idx);
mip_n->flags.located = 1;
if (CLE_OK!=mip_n->Load())
{
delete mip_n;
break;
}
if (mip_n->width << mip_idx < width || mip_n->height << mip_idx < height)
{
delete mip_n;
#if OUTPUT_LOG
CL_LogFile.lputs("** Warning: less than half size\n");
#endif
break;
}
mipmaps.add_entry_end(mip_n);
}
return CLE_OK;
}
void CL_Select_Mode(CL_LoadMode const lmode)
{
switch (lmode)
{
case CLL_D3DTEXTURE:
CL_Image::imode = CL_Image::imode_d3d;
CL_Image::bitsperpixel = CL_Image::bitsperpixel_d3d;
CL_Pixel_16::f = CL_Pixel_16::f_d3d;
CL_Pixel_32::f = CL_Pixel_32::f_d3d;
break;
case CLL_DDSURFACE:
CL_Image::imode = CL_Image::imode_ddraw;
CL_Image::bitsperpixel = CL_Image::bitsperpixel_ddraw;
CL_Pixel_16::f = CL_Pixel_16::f_ddraw;
CL_Pixel_32::f = CL_Pixel_32::f_ddraw;
break;
}
CL_Image::lmode = lmode;
}
// 3DC interface
void CL_Init_DirectDrawMode(CL_VideoMode const vmode)
{
switch (vmode)
{
case CLV_8:
CL_Image::bitsperpixel_ddraw = 8;
CL_Image::imode_ddraw = CLM_GLOBALPALETTE;
break;
case CLV_8TLT:
CL_Image::bitsperpixel_ddraw = 8;
CL_Image::imode_ddraw = CLM_TLTPALETTE;
break;
case CLV_15:
CL_Image::bitsperpixel_ddraw = 16;
CL_Image::imode_ddraw = CLM_16BIT;
CL_Pixel_16::f_ddraw.Init
(
DisplayPixelFormat.dwRBitMask,
DisplayPixelFormat.dwGBitMask,
DisplayPixelFormat.dwBBitMask
);
break;
case CLV_24:
CL_Image::bitsperpixel_ddraw = DisplayPixelFormat.dwRGBBitCount;
if (24 == CL_Image::bitsperpixel_ddraw)
CL_Image::imode_ddraw = CLM_24BIT;
else
{
CL_Image::imode_ddraw = CLM_32BIT;
CL_Pixel_32::f_ddraw.Init
(
DisplayPixelFormat.dwRBitMask,
DisplayPixelFormat.dwGBitMask,
DisplayPixelFormat.dwBBitMask
);
}
break;
case CLV_8T:
CL_Image::bitsperpixel_ddraw = 8;
CL_Image::imode_ddraw = CLM_16BIT;
CL_Pixel_16::f_ddraw.Init(7<<5,7<<2,3);
break;
}
CL_Select_Mode(CLL_DDSURFACE);
}
CL_Error CL_Image::CopyToScanDrawTexture(unsigned char * * const ImagePtrA [], unsigned int n_mips_max)
{
if (!flags.loaded) return CLE_LOADERROR;
if (CLL_DDSURFACE != lmode) CL_Select_Mode(CLL_DDSURFACE);
if (n_mips_max > mipmaps.size()+1) n_mips_max = mipmaps.size()+1;
if (!*ImagePtrA[0])
{
if (flags.raw16bit)
{
if (n_mips_max>1)
*ImagePtrA[0] = (unsigned char *) AllocateMem((2*width+1)*(2*height+1)*2/3+(n_mips_max-3)*2); // slightly more than 4/3 w*h*bytedepth
else
*ImagePtrA[0] = (unsigned char *) AllocateMem(width*height*2);
}
else
{
if (n_mips_max>1)
*ImagePtrA[0] = (unsigned char *) AllocateMem((2*width+1)*(2*height+1)*bitsperpixel/24+(n_mips_max-3)*(bitsperpixel/8)); // slightly more than 4/3 w*h*bytedepth
else
*ImagePtrA[0] = (unsigned char *) AllocateMem(width*height*(bitsperpixel>>3));
}
}
if (!*ImagePtrA[0])
return CLE_ALLOCFAIL;
unsigned int my_bitsperpixel = bitsperpixel;
switch (imode)
{
unsigned char * tptr;
unsigned short * tSptr;
unsigned long * tLptr;
case CLM_GLOBALPALETTE:
case CLM_TLTPALETTE:
if (flags.raw16bit)
{
my_bitsperpixel = 16;
tSptr = (unsigned short *)*ImagePtrA[0];
if (tSptr)
{
for (int i=0; i<height; ++i)
{
memcpy(tSptr,im16raw[i],width*sizeof(unsigned short));
tSptr += width;
}
}
}
else
{
tptr = *ImagePtrA[0];
if (tptr)
{
for (int i=0; i<height; ++i)
{
memcpy(tptr,im8[i],width);
tptr += width;
}
}
}
break;
case CLM_16BIT:
switch (bitsperpixel)
{
case 8:
tptr = *ImagePtrA[0];
if (tptr)
{
for (int i=0; i<height; ++i)
{
unsigned char const * i8ptr = im8[i];
for (int j=width; j; --j)
{
*tptr++ = *i8ptr++;
}
}
}
break;
case 16:
tSptr = (unsigned short *)*ImagePtrA[0];
if (tSptr)
{
for (int i=0; i<height; ++i)
{
CL_Pixel_16 const * i16ptr = im16[i];
for (int j=width; j; --j)
{
*tSptr++ = *i16ptr++;
}
}
}
break;
}
break;
case CLM_24BIT:
switch (bitsperpixel)
{
case 24:
tptr = *ImagePtrA[0];
if (tptr)
{
for (int i=0; i<height; ++i)
{
CL_Pixel_24 const * i24ptr = im24[i];
for (int j=width; j; --j)
{
*tptr++ = i24ptr->b;
*tptr++ = i24ptr->g;
*tptr++ = i24ptr->r;
i24ptr++;
}
}
}
break;
case 32:
tLptr = (unsigned long *)*ImagePtrA[0];
if (tLptr)
{
for (int i=0; i<height; ++i)
{
CL_Pixel_32 const * i32ptr = im32[i];
for (int j=width; j; --j)
{
*tLptr++ = *i32ptr++;
}
}
}
break;
}
break;
default:
return CLE_INVALIDDXMODE;
}
// now do mipmaps if avail
CL_Image * last_mipP = this;
LIF<CL_Image *> i_mip(&mipmaps);
for (int i=1; i<n_mips_max && !i_mip.done(); ++i, i_mip.next())
{
*ImagePtrA[i] = *ImagePtrA[i-1] + last_mipP->width*last_mipP->height*(my_bitsperpixel>>3);
CL_Error thismipmaperror = i_mip()->CopyToScanDrawTexture(&ImagePtrA[i],1);
if (CLE_OK != thismipmaperror) return thismipmaperror;
last_mipP = i_mip();
}
return CLE_OK;
}
// Direct X interface
void CL_Init_D3DMode(LPDDSURFACEDESC const format)
{
CL_Image::format = format;
if (format->ddpfPixelFormat.dwFlags & DDPF_PALETTEINDEXED8)
{
#if QUANTISE_ON_LOAD
CL_Image::imode_d3d = CLM_24BIT;
#else
CL_Image::imode_d3d = CLM_ATTACHEDPALETTE;
#endif
CL_Image::bitsperpixel_d3d = 8;
}
else if (format->ddpfPixelFormat.dwFlags & DDPF_PALETTEINDEXED4)
{
#if QUANTISE_ON_LOAD
CL_Image::imode_d3d = CLM_24BIT;
#else
CL_Image::imode_d3d = CLM_ATTACHEDPALETTE;
#endif
CL_Image::bitsperpixel_d3d = 4;
}
else if (format->ddpfPixelFormat.dwFlags & DDPF_PALETTEINDEXED2)
{
#if QUANTISE_ON_LOAD
CL_Image::imode_d3d = CLM_24BIT;
#else
CL_Image::imode_d3d = CLM_ATTACHEDPALETTE;
#endif
CL_Image::bitsperpixel_d3d = 2;
}
else if (format->ddpfPixelFormat.dwFlags & DDPF_PALETTEINDEXED1)
{
#if QUANTISE_ON_LOAD
CL_Image::imode_d3d = CLM_24BIT;
#else
CL_Image::imode_d3d = CLM_ATTACHEDPALETTE;
#endif
CL_Image::bitsperpixel_d3d = 1;
}
else
{
CL_Image::bitsperpixel_d3d = format->ddpfPixelFormat.dwRGBBitCount;
if (format->ddpfPixelFormat.dwRGBBitCount > 16)
{
CL_Image::imode_d3d = CLM_32BIT;
CL_Pixel_32::f_d3d.Init
(
format->ddpfPixelFormat.dwRBitMask,
format->ddpfPixelFormat.dwGBitMask,
format->ddpfPixelFormat.dwBBitMask
);
}
else
{
CL_Image::imode_d3d = CLM_16BIT;
CL_Pixel_16::f_d3d.Init
(
format->ddpfPixelFormat.dwRBitMask,
format->ddpfPixelFormat.dwGBitMask,
format->ddpfPixelFormat.dwBBitMask
);
}
}
CL_Select_Mode(CLL_D3DTEXTURE);
}
CL_Error CL_Image::CopyToD3DTexture(LPDIRECTDRAWSURFACE * const DDPtrA [], LPVOID * const DDSurfaceA [], int const MemoryType, unsigned int n_mips_max)
{
if (!flags.loaded) return CLE_LOADERROR;
WaitForVRamReady(VWS_D3DTEXCREATE);
if (CLL_D3DTEXTURE != lmode) CL_Select_Mode(CLL_D3DTEXTURE);
LPDIRECTDRAWSURFACE lpDDS;
DDSURFACEDESC ddsd;
HRESULT ddrval;
// Check for image being 4 byte aligned
// and fail if it is not
if (width & 3 || height & 3)
{
// return error code
return CLE_DXERROR;
}
// Set up the surface description. starting
// with the passed texture format and then
// incorporating the information read from the
// ppm.
memcpy(&ddsd, format, sizeof(DDSURFACEDESC));
ddsd.dwSize = sizeof(DDSURFACEDESC);
ddsd.dwFlags = (DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH | DDSD_PIXELFORMAT);
ddsd.ddsCaps.dwCaps = (DDSCAPS_TEXTURE | MemoryType);
ddsd.dwHeight = height;
ddsd.dwWidth = width;
if (n_mips_max > mipmaps.size()+1) n_mips_max = mipmaps.size()+1;
if (n_mips_max>1)
{
ddsd.dwFlags |= DDSD_MIPMAPCOUNT;
ddsd.ddsCaps.dwCaps |= DDSCAPS_MIPMAP | DDSCAPS_COMPLEX;
ddsd.dwMipMapCount = n_mips_max;
}
// Create the surface
ddrval = lpDD->CreateSurface(&ddsd, &lpDDS, NULL);
LOGDXERR(ddrval);
if (ddrval != DD_OK)
{
#if debug
ReleaseDirect3D();
exit(ddrval);
#else
lpDDS->Release();
return CLE_DXERROR;
#endif
}
// now do mipmaps if avail
if (n_mips_max>1)
{
// We must now traverse the mip-map chain from highest to lowest
// resolutions, For each surface AFTER the first one, we must
// load a new file, using a name obtained from the mip map number
int MipMapNum = 0;
LPDIRECTDRAWSURFACE lpThisMipMap, lpNextMipMap;
DDSCAPS ddsCaps;
lpThisMipMap = lpDDS;
// Component Object Model, increase reference count on
// mip-map surface by one.
lpThisMipMap->AddRef();
ddsCaps.dwCaps = (DDSCAPS_TEXTURE | DDSCAPS_MIPMAP);
ddrval = DD_OK;
LIF<CL_Image *> i_mip(&mipmaps);
CL_Image * last_mipP = this;
while ((ddrval == DD_OK) && (MipMapNum < n_mips_max)) // both tests in case...
{
// Call LoadPPMIntoDDSurface with lpThisMipMap, new file name, and
// other values.
*DDSurfaceA[MipMapNum] = last_mipP->CopyToDDSurface(lpThisMipMap);
// Death trap
if (!*DDSurfaceA[MipMapNum])
{
return CLE_DXERROR;
}
*DDPtrA[MipMapNum] = lpThisMipMap;
// Proceed to the next level.
// Collect bonus rings.
ddrval = lpThisMipMap->GetAttachedSurface(&ddsCaps, &lpNextMipMap);
// Necessary to match the manual increment of the reference count on the
// COM texture. I think.
lpThisMipMap->Release();
// ?? lpNextMipMap = lpThisMipMap;
lpThisMipMap = lpNextMipMap;
MipMapNum++;
if (!i_mip.done())
{
last_mipP = i_mip();
i_mip.next();
}
}
}
else
{
*DDSurfaceA[0] = CopyToDDSurface(lpDDS);
if (!*DDSurfaceA[0]) return CLE_DXERROR;
*DDPtrA[0] = lpDDS;
}
return CLE_OK;
}
CL_Error CL_Image::CopyToDirectDrawSurface(LPDIRECTDRAWSURFACE * const DDPtrA [], LPVOID * const DDSurfaceA [], int const MemoryType, unsigned int n_mips_max)
{
if (!flags.loaded) return CLE_LOADERROR;
WaitForVRamReady(VWS_DDCREATE);
if (CLL_DDSURFACE != lmode) CL_Select_Mode(CLL_DDSURFACE);
LPDIRECTDRAWSURFACE lpDDS;
DDSURFACEDESC ddsd;
HRESULT ddrval;
// Check for image being 4 byte aligned
// and fail if it is not
if (width & 3 || height & 3)
{
// return error code
return CLE_DXERROR;
}
if (n_mips_max > mipmaps.size()+1) n_mips_max = mipmaps.size()+1;
// Set up the surface description. starting
// with the passed texture format and then
// incorporating the information read from the
// ppm.
memset(&ddsd, 0, sizeof ddsd);
ddsd.dwSize = sizeof ddsd;
ddsd.dwFlags = (DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH | DDSD_PIXELFORMAT);
// Ensure that created surface has same pixel desription
// as primary
memcpy(&ddsd.ddpfPixelFormat, &DisplayPixelFormat, sizeof(DDPIXELFORMAT));
ddsd.ddsCaps.dwCaps = (DDSCAPS_OFFSCREENPLAIN | MemoryType);
ddsd.dwHeight = height;
ddsd.dwWidth = width;
if (n_mips_max>1)
{
ddsd.dwFlags |= DDSD_MIPMAPCOUNT;
ddsd.ddsCaps.dwCaps |= DDSCAPS_MIPMAP | DDSCAPS_COMPLEX;
ddsd.dwMipMapCount = n_mips_max;
}
// Create the surface
ddrval = lpDD->CreateSurface(&ddsd, &lpDDS, NULL);
LOGDXERR(ddrval);
if (ddrval != DD_OK)
{
#if debug
ReleaseDirect3D();
exit(ddrval);
#else
lpDDS->Release();
return CLE_DXERROR;
#endif
}
DDCOLORKEY set_zero = {0,0};
ddrval = lpDDS->SetColorKey(DDCKEY_SRCBLT, &set_zero);
LOGDXERR(ddrval);
if(ddrval != DD_OK)
{
#if debug
ReleaseDirect3D();
exit(ddrval);
#else
lpDDS->Release();
return CLE_DXERROR;
#endif
}
// now do mipmaps if avail
if (n_mips_max>1)
{
// We must now traverse the mip-map chain from highest to lowest
// resolutions, For each surface AFTER the first one, we must
// load a new file, using a name obtained from the mip map number
int MipMapNum = 0;
LPDIRECTDRAWSURFACE lpThisMipMap, lpNextMipMap;
DDSCAPS ddsCaps;
lpThisMipMap = lpDDS;
// Component Object Model, increase reference count on
// mip-map surface by one.
lpThisMipMap->AddRef();
ddsCaps.dwCaps = (DDSCAPS_TEXTURE | DDSCAPS_MIPMAP);
ddrval = DD_OK;
LIF<CL_Image *> i_mip(&mipmaps);
CL_Image * last_mipP = this;
while ((ddrval == DD_OK) && (MipMapNum < n_mips_max)) // both tests in case...
{
// Call LoadPPMIntoDDSurface with lpThisMipMap, new file name, and
// other values.
*DDSurfaceA[MipMapNum] = last_mipP->CopyToDDSurface(lpThisMipMap);
// Death trap
if (!*DDSurfaceA[MipMapNum])
{
return CLE_DXERROR;
}
*DDPtrA[MipMapNum] = lpThisMipMap;
// Proceed to the next level.
// Collect bonus rings.
ddrval = lpThisMipMap->GetAttachedSurface(&ddsCaps, &lpNextMipMap);
// Necessary to match the manual increment of the reference count on the
// COM texture. I think.
lpThisMipMap->Release();
// ?? lpNextMipMap = lpThisMipMap;
lpThisMipMap = lpNextMipMap;
MipMapNum++;
if (!i_mip.done())
{
last_mipP = i_mip();
i_mip.next();
}
}
}
else
{
*DDSurfaceA[0] = CopyToDDSurface(lpDDS);
if (!*DDSurfaceA[0]) return CLE_DXERROR;
*DDPtrA[0] = lpDDS;
}
return CLE_OK;
}
LPVOID CL_Image::CopyToDDSurface(LPDIRECTDRAWSURFACE lpDDS)
{
if (!flags.loaded) return 0;
LPDIRECTDRAWPALETTE lpDDPPMPal;
PALETTEENTRY ppe[256];
DDSURFACEDESC ddsd;
#if QUANTISE_ON_LOAD
D3DCOLOR colors[256];
D3DCOLOR c;
int color_count;
#endif
int psize;
char *lpC;
HRESULT ddrval;
unsigned int i, j;
unsigned int pcaps;
// Lock the surface so it can be filled with the PPM file
memset(&ddsd, 0, sizeof(DDSURFACEDESC));
ddsd.dwSize = sizeof(DDSURFACEDESC);
ddrval = lpDDS->Lock(NULL, &ddsd, 0, NULL);
if (!ddsd.lpSurface)
{
lpDDS->Unlock(NULL);
lpDDS->Release();
LOGDXSTR("No surface");
return 0;
}
LOGDXERR(ddrval);
if (ddrval != DD_OK)
{
#if debug
ReleaseDirect3D();
exit(ddrval);
#else
lpDDS->Release();
return 0;
#endif
}
// The method of loading depends on the pixel format of the dest surface
switch (imode)
{
case CLM_32BIT:
switch (bitsperpixel)
{
case 32:
for (j = 0; j < height; j++)
{
// Point to next row in texture surface
unsigned long * lpLP = (unsigned long*)((char*)ddsd.lpSurface
+ ddsd.lPitch * j);
CL_Pixel_32 * lpP32 = im32[j];
for (i = width; i; --i)
{
*lpLP++ = *lpP32++;
}
}
break;
case 24:
for (j = 0; j < height; j++)
{
// Point to next row in texture surface
unsigned char * lpCP = (unsigned char*)ddsd.lpSurface
+ ddsd.lPitch * j;
CL_Pixel_32 * lpP32 = im32[j];
for (i = width; i; --i)
{
unsigned long lv = *lpP32++;
unsigned char const * lpSrcCP = (unsigned char const *)&lv;
// dodgy - makes assumtions about 24-bit values...
*lpCP++ = *lpSrcCP++;
*lpCP++ = *lpSrcCP++;
*lpCP++ = *lpSrcCP++;
}
}
break;
default:
#if debug
ReleaseDirect3D();
exit(ddrval);
#else
lpDDS->Unlock(NULL);
lpDDS->Release();
return 0;
#endif
}
lpDDS->Unlock(NULL);
break;
case CLM_16BIT:
switch (bitsperpixel)
{
case 16:
for (j = 0; j < height; j++)
{
// Point to next row in texture surface
unsigned short * lpSP = (unsigned short*)((char*)ddsd.lpSurface
+ ddsd.lPitch * j);
CL_Pixel_16 * lpP16 = im16[j];
for (i = width; i; --i)
{
*lpSP++ = *lpP16++;
}
}
break;
case 8:
for (j = 0; j < height; j++)
{
// Point to next row in texture surface
unsigned char * lpCP = (unsigned char*)ddsd.lpSurface
+ ddsd.lPitch * j;
CL_Pixel_16 * lpP16 = im16[j];
for (i = width; i; --i)
{
*lpCP++ = *lpP16++;
}
}
break;
default:
// Everything's gone pear-shaped...
#if debug
ReleaseDirect3D();
exit(ddrval);
#else
lpDDS->Unlock(NULL);
lpDDS->Release();
return 0;
#endif
}
lpDDS->Unlock(NULL);
break;
case CLM_24BIT:
if (24==bitsperpixel)
{
for (j = 0; j < height; j++)
{
// Point to next row in surface
unsigned char * lpCP = ((unsigned char*)ddsd.lpSurface) + ddsd.lPitch * j;
CL_Pixel_24 * lpP24 = im24[j];
for (i = 0; i < width; i++)
{
// making an assumption about the ordering of r,g,b on the video card
*lpCP++ = lpP24->b;
*lpCP++ = lpP24->g;
*lpCP++ = lpP24->r;
lpP24++;
}
}
lpDDS->Unlock(NULL);
}
else
{
#if QUANTISE_ON_LOAD // Neal's quantize on load stuff
// Paletted target texture surface
psize = 1<<bitsperpixel;
color_count = 0; // number of colors in the texture
for (j = 0; j < height; j++)
{
// Point to next row in surface
lpC = ((char*)ddsd.lpSurface) + ddsd.lPitch * j;
CL_Pixel_24 * lpP24 = im24[j];
for (i = 0; i < width; i++, lpP24++)
{
int k;
// Get the next red, green and blue values and turn them into a
// D3DCOLOR.
c = RGB_MAKE(lpP24->r, lpP24->g, lpP24->b);
// Search for this color in a table of colors in this texture
for (k = 0; k < color_count; k++)
if (c == colors[k]) break;
if (k == color_count)
{
// This is a new color, so add it to the list
color_count++;
// More than 256 and we fail (8-bit)
if (color_count > psize)
{
color_count--;
k = color_count - 1;
}
colors[k] = c;
}
// Set the "pixel" value on the surface to be the index into the
// color table
if (psize<256)
{
unsigned int bitmask;
switch (psize)
{
default:
bitmask = 1;
break;
case 4:
bitmask = 3;
break;
case 2:
bitmask = 7;
break;
}
if ((i & bitmask) == 0)
*lpC = (char)(k & (psize-1));
else
*lpC |= (char)((k & (psize-1)) << (i & (psize-1)));
if ((~i & bitmask) == 0)
lpC++;
}
else
{
*lpC = (char)k;
lpC++;
}
}
}
// Close the file and unlock the surface
lpDDS->Unlock(NULL);
if (color_count > psize)
{
// If there are more than 256 colors, we overran our palette
#if debug
ReleaseDirect3D();
exit(0x321123);
#else
lpDDS->Release();
return 0;
#endif
}
// Create a palette with the colors in our color table
memset(ppe, 0, sizeof(PALETTEENTRY) * 256);
for (i = 0; i < color_count; i++)
{
ppe[i].peRed = (unsigned char)RGB_GETRED(colors[i]);
ppe[i].peGreen = (unsigned char)RGB_GETGREEN(colors[i]);
ppe[i].peBlue = (unsigned char)RGB_GETBLUE(colors[i]);
}
// Set all remaining entry flags to D3DPAL_RESERVED, which are ignored by
// the renderer.
for (; i < 256; i++)
ppe[i].peFlags = D3DPAL_RESERVED;
// Create the palette with the DDPCAPS_ALLOW256 flag because we want to
// have access to all entries.
switch (bitsperpixel)
{
default:
pcaps = DDPCAPS_8BIT | DDPCAPS_ALLOW256;
break;
case 4:
pcaps = DDPCAPS_4BIT;
break;
case 2:
pcaps = DDPCAPS_2BIT;
break;
case 1:
pcaps = DDPCAPS_1BIT;
break;
}
ddrval = lpDD->CreatePalette
(DDPCAPS_INITIALIZE | pcaps,
ppe, &lpDDPPMPal, NULL);
LOGDXERR(ddrval);
if (ddrval != DD_OK)
{
#if debug
ReleaseDirect3D();
exit(ddrval);
#else
lpDDS->Release();
return 0;
#endif
}
// Finally, bind the palette to the surface
ddrval = lpDDS->SetPalette(lpDDPPMPal);
LOGDXERR(ddrval);
if (ddrval != DD_OK)
{
#if debug
ReleaseDirect3D();
exit(ddrval);
#else
lpDDS->Release();
lpDDPPMPal->Release();
return 0;
#endif
}
lpDDPPMPal->Release();
}
break;
#else
// Everything's gone pear-shaped...
#if debug
ReleaseDirect3D();
exit(ddrval);
#else
lpDDS->Unlock(NULL);
lpDDS->Release();
return 0;
#endif
}
break;
case CLM_ATTACHEDPALETTE:
#endif
case CLM_GLOBALPALETTE:
case CLM_TLTPALETTE:
// Paletted target texture surface
psize = 1<<bitsperpixel;
ReducePalette(psize); // will do nothing if palette is already small enough
// ignore returned error code, because I don't care if it failed
for (j = 0; j < height; j++)
{
// Point to next row in surface
lpC = ((char*)ddsd.lpSurface) + ddsd.lPitch * j;
unsigned char * lpP8 = im8[j];
if (psize<256)
{
unsigned int bitmask;
unsigned int nextpixelshift;
switch (psize)
{
default:
nextpixelshift = 2;
break;
case 4:
nextpixelshift = 1;
break;
case 2:
nextpixelshift = 0;
break;
}
bitmask = 7 >> nextpixelshift;
for (i = 0; i < width; i++)
{
unsigned char k = *lpP8++;
// Set the "pixel" value on the surface to be the index into the
// color table
if ((i & bitmask) == 0)
*lpC = (char) (k & (psize-1));
else
*lpC |= (char) ((k & (psize-1)) << ((i & bitmask)<<nextpixelshift));
if ((~i & bitmask) == 0)
lpC++;
}
}
else
{
memcpy(lpC,lpP8,width);
}
}
// Close the file and unlock the surface
lpDDS->Unlock(NULL);
// Create a palette with the colors in our color table
memset(ppe, 0, sizeof(PALETTEENTRY) * 256);
for (i = 0; i < palette_size; i++)
{
ppe[i].peRed = palette[i].r;
ppe[i].peGreen = palette[i].g;
ppe[i].peBlue = palette[i].b;
}
// Set all remaining entry flags to D3DPAL_RESERVED, which are ignored by
// the renderer.
for (; i < 256; i++)
ppe[i].peFlags = D3DPAL_RESERVED;
// Create the palette with the DDPCAPS_ALLOW256 flag because we want to
// have access to all entries.
switch (bitsperpixel)
{
default:
pcaps = DDPCAPS_8BIT | DDPCAPS_ALLOW256;
break;
case 4:
pcaps = DDPCAPS_4BIT;
break;
case 2:
pcaps = DDPCAPS_2BIT;
break;
case 1:
pcaps = DDPCAPS_1BIT;
break;
}
ddrval = lpDD->CreatePalette
(DDPCAPS_INITIALIZE | pcaps,
ppe, &lpDDPPMPal, NULL);
LOGDXERR(ddrval);
if (ddrval != DD_OK)
{
#if debug
ReleaseDirect3D();
exit(ddrval);
#else
lpDDS->Release();
return 0;
#endif
}
// Finally, bind the palette to the surface
ddrval = lpDDS->SetPalette(lpDDPPMPal);
LOGDXERR(ddrval);
if (ddrval != DD_OK)
{
#if debug
ReleaseDirect3D();
exit(ddrval);
#else
lpDDS->Release();
lpDDPPMPal->Release();
return 0;
#endif
}
lpDDPPMPal->Release();
break;
default:
// Everything's gone pear-shaped...
#if debug
ReleaseDirect3D();
exit(ddrval);
#else
lpDDS->Unlock(NULL);
lpDDS->Release();
return 0;
#endif
}
return ddsd.lpSurface;
}