CImageLoaderBLP initial release

This commit is contained in:
shlainn 2008-04-05 14:34:09 +00:00
parent 03f40ffefe
commit 5aeccb7a77
4 changed files with 780 additions and 0 deletions

View File

@ -0,0 +1,310 @@
#include <iostream>
#include <string>
#include "irrlicht/irrlicht.h"
#include "SImage.h"
#include "CImageLoaderBLP.h"
namespace irr
{
namespace video
{
//! returns true if the file maybe is able to be loaded by this class
//! based on the file extension (e.g. ".tga")
bool CImageLoaderBLP::isALoadableFileExtension(const c8* fileName) const
{
// std::cout << "Checking for file extension\n";
return strstr(fileName, ".blp")!=0;
}
//! returns true if the file maybe is able to be loaded by this class
bool CImageLoaderBLP::isALoadableFileFormat(io::IReadFile* file) const
{
// std::cout <<"Checking if file is a BLP file\n";
if (!file)
{
std::cout<<"No such file: "<<file->getFileName()<<"\n";
return false;
}
std::string fileId;
// Read the first few bytes of the BLP file
if (file->read(&fileId[0], 4) != 4)
{
std::cout << "Cannot read BLP file header\n";
return false;
}
if(fileId[0]=='B' && fileId[1]=='L' && fileId[2]=='P' && fileId[3]=='2')
{
std::cout << "Header is BLP2, file should be loadable\n";
return true;
}
else
{
std::cout << "Header doesn't match, this is no BLP file\n";
std::cout << "Expected:BLP2 Got:"<<fileId.c_str()<<"\n";
return false;
}
}
// load in the image data
IImage* CImageLoaderBLP::loadImage(io::IReadFile* file) const
{
if (!file)
return 0;
BLPHeader header;
// std::cout<<"Trying to load the image\n";
// std::cout<<"Checking Header\n";
file->read(&header,sizeof(BLPHeader));
// std::cout<<"Header data: "<<header.fileID<<"\n Alpha depth:"<<(u32)header.alpha_bitdepth<<"bit\nCompression:"<<(u32)header.compression<<"\n";
// std::cout<<"Mystery factor:"<<(u32)header.alpha_unk<<"\n";
// std::cout<<"X-Res: "<< header.x_res<<"\nY-Res:"<<header.y_res<<"\n";
u32 usedMips=0;
for(u32 i=0;i<16;i++)
{
if(header.mip_ofs[i]!=0&&header.mip_size[i]!=0)
usedMips++;
}
// std::cout<<"Mip Levels:"<< usedMips<<"\n";
core::array<PaletteColor> palette;
PaletteColor tempColor;
palette.reallocate(256);
for(u32 i=0;i<256;i++)
{
file->read(&tempColor,sizeof(PaletteColor));
palette.push_back(tempColor);
}
// std::cout<<"Loading Mip 0 Length is "<<header.mip_size[0]<<"\n";
file->seek(header.mip_ofs[0]);
video::IImage* image = 0;
image = new SImage(ECF_A8R8G8B8, core::dimension2d<s32>(header.x_res, header.y_res));
if(header.compression==2)
{
//Reading imageData for DXT1/3/5 (5 not really...)
DXC5chunk tempChunk5;
DXC3chunk tempChunk3;
DXC1chunk tempChunk1;
core::array<DXC5chunk> imagedata5;
core::array<DXC3chunk> imagedata3;
core::array<DXC1chunk> imagedata1;
for(u32 i=0;i<(header.mip_size[0]/8);i++)
{
if(header.compression==2&&header.alpha_bitdepth>1)
{
if(header.alpha_unk==7)//it is not absolutely necessary to divide DXT3 and 5 data here as both are 64bit blocks which have to be dissected later.
{ // But this way it is somehow clearer for me
file->read(&tempChunk5,sizeof(DXC5chunk));
imagedata5.push_back(tempChunk5);
}
else
{
file->read(&tempChunk3,sizeof(DXC3chunk));
imagedata3.push_back(tempChunk3);
}
}
file->read(&tempChunk1,sizeof(DXC1chunk));
imagedata1.push_back(tempChunk1);
}
// std::cout << "Data read\n";
u32 i=0;
u32 alpha=255;
u32 a[8];
u32 r1, g1,b1,r2,g2,b2;
u64 temptransp;
bool transparency_bit=false;
for(u32 y=0;y<header.y_res;y=y+4)
{
for(u32 x=0;x<header.x_res;x=x+4)
{
f32 rb=256/31;
f32 g=256/63;
r1 = (u32)rb*(imagedata1[i].color1 & 0xF800) >>11;
g1 = (u32)g*(imagedata1[i].color1 & 0x07E0) >>5;
b1 = (u32)rb*(imagedata1[i].color1 & 0x001F) ;
r2 = (u32)rb*(imagedata1[i].color2 & 0xF800) >>11;
g2 = (u32)g*(imagedata1[i].color2 & 0x07E0) >>5;
b2 = (u32)rb*(imagedata1[i].color2 & 0x001F) ;
if(imagedata1[i].color1>imagedata1[i].color2||header.alpha_bitdepth==8)
{
transparency_bit=false;
}
else
{
/* std::cout << imagedata1[i].color1 <<","<<imagedata1[i].color2<<"\n";
// f32 rgb=256/31;
r1 = (u32)rgb*(imagedata1[i].color1 & 0xF800) >>11;
g1 = (u32)rgb*(imagedata1[i].color1 & 0x07C0) >>6;
b1 = (u32)rgb*(imagedata1[i].color1 & 0x003E) >>1;
r2 = (u32)rgb*(imagedata1[i].color2 & 0xF800) >>11;
g2 = (u32)rgb*(imagedata1[i].color2 & 0x07C0) >>6;
b2 = (u32)rgb*(imagedata1[i].color2 & 0x003E) >>1;
*/ transparency_bit=true;
}
u32 tempbitmap=imagedata1[i].bitmap;
if(header.alpha_bitdepth==8)
{
if(header.alpha_unk==7)
{
temptransp=(u64)imagedata5[i].bitmap[2]<<32|(u64)imagedata5[i].bitmap[1]<<16|imagedata5[i].bitmap[0];
a[0]=imagedata5[i].alpha1;
a[1]=imagedata5[i].alpha2;
if (a[0] > a[1]) {
// 8-alpha block: derive the other six alphas.
// Bit code 000 = alpha_0, 001 = alpha_1, others are interpolated.
a[2] = (6 * a[0] + 1 * a[1]) / 7; // bit code 010
a[3] = (5 * a[0] + 2 * a[1]) / 7; // bit code 011
a[4] = (4 * a[0] + 3 * a[1]) / 7; // bit code 100
a[5] = (3 * a[0] + 4 * a[1]) / 7; // bit code 101
a[6] = (2 * a[0] + 5 * a[1]) / 7; // bit code 110
a[7] = (1 * a[0] + 6 * a[1]) / 7; // bit code 111
}
else
{
// 6-alpha block.
// Bit code 000 = a[0], 001 = a[1], others are interpolated.
a[2] = (4 * a[0] + 1 * a[1]) / 5; // Bit code 010
a[3] = (3 * a[0] + 2 * a[1]) / 5; // Bit code 011
a[4] = (2 * a[0] + 3 * a[1]) / 5; // Bit code 100
a[5] = (1 * a[0] + 4 * a[1]) / 5; // Bit code 101
a[6] = 0; // Bit code 110
a[7] = 255; // Bit code 111
}
}
else
{
temptransp=imagedata3[i].transparency_block;
}
}
for(u32 ty=0;ty<4;ty++)
{
for(u32 tx=0;tx<4;tx++)
{
if(header.alpha_bitdepth==8)
{
if(header.alpha_unk==7)
{
alpha=a[(temptransp & 7)];
}
else
{
f32 a=256/15;
alpha=(u32)a * (temptransp & 15);
}
}
else
{
alpha=255;
}
switch(tempbitmap & 3)
{
case 0:
{
image->setPixel(x+tx,y+ty,video::SColor(alpha,r1,g1,b1));
break;
}
case 1:
{
image->setPixel(x+tx,y+ty,video::SColor(alpha,r2,g2,b2));
break;
}
case 2:
{
if(transparency_bit==false)
image->setPixel(x+tx,y+ty,video::SColor(alpha,(u32)(0.667f*r1+0.333f*r2),(u32)(0.667f*g1+0.333f*g2),(u32)(0.667f*b1+0.333f*b2)));
else
//image->setPixel(x+tx,y+ty,video::SColor(255,255,0,0));
image->setPixel(x+tx,y+ty,video::SColor(255,(u32)(0.5f*r1+0.5f*r2),(u32)(0.5f*g1+0.5f*g2),(u32)(0.5f*b1+0.5f*b2)));
break;
}
case 3:
{
if(transparency_bit==false)
image->setPixel(x+tx,y+ty,video::SColor(alpha,(u32)(0.333f*r1+0.667f*r2),(u32)(0.333f*g1+0.667f*g2),(u32)(0.333f*b1+0.667f*b2)));
else
if(header.alpha_bitdepth==1)
image->setPixel(x+tx,y+ty,video::SColor(0,0,0,0));
else
image->setPixel(x+tx,y+ty,video::SColor(255,0,0,0));
break;
}
}
tempbitmap=tempbitmap>>2;
if(header.alpha_bitdepth==8)
{
if(header.alpha_unk==7)
{
temptransp=temptransp>>3;
}
else
{
temptransp=temptransp>>4;
}
}
}
}
i++;
}
}
}
else//Palette Images
{
u8 index;
for(u32 y=0;y<header.y_res;y++)
{
for(u32 x=0;x<header.x_res;x++)
{
file->read(&index,sizeof(u8));
image->setPixel(x,y,video::SColor(255,palette[index].R,palette[index].G,palette[index].B));
}
}
if(header.alpha_bitdepth==1)//surely not the best way.
{
for(u32 y=0;y<header.y_res;y++)
{
video::SColor pixel;
for(u32 x=0;x<header.x_res;x=x+8)
{
file->read(&index,sizeof(u8));
for(u32 i=0;i<8;i++)
{
pixel =image->getPixel(x+i,y);
pixel.setAlpha(255*(index & 1));
image->setPixel(x+i,y,pixel);
index =index >>1;
}
}
}
}
if(header.alpha_bitdepth==8)//surely not the best way.
{
for(u32 y=0;y<header.y_res;y++)
{
video::SColor pixel;
for(u32 x=0;x<header.x_res;x++)
{
file->read(&index,sizeof(u8));
pixel =image->getPixel(x,y);
pixel.setAlpha(index);
image->setPixel(x,y,pixel);
}
}
}
}
return image;
}
}//namespace video
}//namespace irr

View File

@ -0,0 +1,63 @@
#include <vector>
#include "irrlicht/IImageLoader.h"
typedef unsigned long long int u64;
namespace irr
{
namespace video
{
//! Surface Loader for BLP files
class CImageLoaderBLP : public IImageLoader
{
public:
//! returns true if the file maybe is able to be loaded by this class
//! based on the file extension (e.g. ".blp")
virtual bool isALoadableFileExtension(const c8* fileName) const;
//! returns true if the file maybe is able to be loaded by this class
virtual bool isALoadableFileFormat(io::IReadFile* file) const;
//! creates a surface from the file
virtual IImage* loadImage(io::IReadFile* file) const;
private:
struct BLPHeader
{
c8 fileID[4];
u32 version;
u8 compression;
u8 alpha_bitdepth;
u8 alpha_unk;
u8 miplevel;
u32 x_res;
u32 y_res;
u32 mip_ofs[16];
u32 mip_size[16];
};
struct DXC1chunk
{
u16 color1;
u16 color2;
u32 bitmap;
};
struct DXC3chunk//This is kind of useless
{
u64 transparency_block;
};
struct DXC5chunk
{
u8 alpha1, alpha2;
u16 bitmap[3];//how do i express an "u48"?
};
struct PaletteColor//Not sure if an Irrlicht color can handle the changed color sequence
{
u8 B,G,R,A;
};
};
} // end namespace video
} // end namespace irr

306
src/Client/GUI/SImage.cpp Normal file
View File

@ -0,0 +1,306 @@
#include "SImage.h"
namespace irr
{
namespace video
{
//! constructor for empty image
//! constructor
SImage::SImage(ECOLOR_FORMAT format, const core::dimension2d<s32>& size): Size(size), Format(format), Data(0)
{
initData();
}
void SImage::initData()
{
setBitMasks();
BitsPerPixel = getBitsPerPixelFromFormat(Format);
BytesPerPixel = BitsPerPixel / 8;
// Pitch should be aligned...
Pitch = BytesPerPixel * Size.Width;
if (!Data)
Data = new s8[Size.Height * Pitch];
}
u32 SImage::getBitsPerPixelFromFormat(ECOLOR_FORMAT format)
{
switch(format)
{
case ECF_A1R5G5B5:
return 16;
case ECF_R5G6B5:
return 16;
case ECF_R8G8B8:
return 24;
case ECF_A8R8G8B8:
return 32;
}
return 0;
}
SImage::~SImage()
{
}
//! Returns width and height of image data.
const core::dimension2d<s32>& SImage::getDimension() const
{
return Size;
}
//! Returns bits per pixel.
u32 SImage::getBitsPerPixel() const
{
return BitsPerPixel;
}
//! Returns bytes per pixel
u32 SImage::getBytesPerPixel() const
{
return BytesPerPixel;
}
//! Returns image data size in bytes
u32 SImage::getImageDataSizeInBytes() const
{
return Pitch * Size.Height;
}
//! Returns image data size in pixels
u32 SImage::getImageDataSizeInPixels() const
{
return Size.Width * Size.Height;
}
//! returns mask for red value of a pixel
u32 SImage::getRedMask() const
{
return RedMask;
}
//! returns mask for green value of a pixel
u32 SImage::getGreenMask() const
{
return GreenMask;
}
//! returns mask for blue value of a pixel
u32 SImage::getBlueMask() const
{
return BlueMask;
}
//! returns mask for alpha value of a pixel
u32 SImage::getAlphaMask() const
{
return AlphaMask;
}
void SImage::setBitMasks()
{
switch(Format)
{
case ECF_A1R5G5B5:
AlphaMask = 0x1<<15;
RedMask = 0x1F<<10;
GreenMask = 0x1F<<5;
BlueMask = 0x1F;
break;
case ECF_R5G6B5:
AlphaMask = 0x0;
RedMask = 0x1F<<11;
GreenMask = 0x3F<<5;
BlueMask = 0x1F;
break;
case ECF_R8G8B8:
AlphaMask = 0x0;
RedMask = 0x00FF0000;
GreenMask = 0x0000FF00;
BlueMask = 0x000000FF;
break;
case ECF_A8R8G8B8:
AlphaMask = 0xFF000000;
RedMask = 0x00FF0000;
GreenMask = 0x0000FF00;
BlueMask = 0x000000FF;
break;
}
}
//! sets a pixel
void SImage::setPixel(u32 x, u32 y, const SColor &color )
{
if (x >= (u32)Size.Width || y >= (u32)Size.Height)
return;
switch(Format)
{
case ECF_A1R5G5B5:
{
u16 * dest = (u16*) ((u8*) Data + ( y * Pitch ) + ( x << 1 ));
*dest = video::A8R8G8B8toA1R5G5B5 ( color.color );
} break;
case ECF_R5G6B5:
{
u16 * dest = (u16*) ((u8*) Data + ( y * Pitch ) + ( x << 1 ));
*dest = video::A8R8G8B8toR5G6B5 ( color.color );
} break;
case ECF_R8G8B8:
{
u8* dest = (u8*) Data + ( y * Pitch ) + ( x * 3 );
dest[0] = color.getRed();
dest[1] = color.getGreen();
dest[2] = color.getBlue();
} break;
case ECF_A8R8G8B8:
{
u32 * dest = (u32*) ((u8*) Data + ( y * Pitch ) + ( x << 2 ));
*dest = color.color;
} break;
}
}
//! returns a pixel
SColor SImage::getPixel(u32 x, u32 y) const
{
if (x >= (u32)Size.Width || y >= (u32)Size.Height)
return SColor(0);
switch(Format)
{
case ECF_A1R5G5B5:
return A1R5G5B5toA8R8G8B8(((u16*)Data)[y*Size.Width + x]);
case ECF_R5G6B5:
return R5G6B5toA8R8G8B8(((u16*)Data)[y*Size.Width + x]);
case ECF_A8R8G8B8:
return ((u32*)Data)[y*Size.Width + x];
case ECF_R8G8B8:
{
u8* p = &((u8*)Data)[(y*3)*Size.Width + (x*3)];
return SColor(255,p[0],p[1],p[2]);
}
}
return SColor(0);
}
//! returns the color format
ECOLOR_FORMAT SImage::getColorFormat() const
{
return Format;
}
//! copies this surface into another, scaling it to the target image size
// note: this is very very slow. (i didn't want to write a fast version.
// but hopefully, nobody wants to scale surfaces every frame.
void SImage::copyToScaling(void* target, s32 width, s32 height, ECOLOR_FORMAT format, u32 pitch)
{
if (!target || !width || !height)
return;
const u32 bpp=getBitsPerPixelFromFormat(format)/8;
if (0==pitch)
pitch = width*bpp;
if (Format==format && Size.Width==width && Size.Height==height)
{
if (pitch==Pitch)
{
memcpy(target, Data, height*pitch);
return;
}
else
{
u8* tgtpos = (u8*) target;
u8* dstpos = (u8*) Data;
const u32 bwidth = width*bpp;
for (s32 y=0; y<height; ++y)
{
memcpy(target, Data, height*pitch);
memset(tgtpos+width, 0, pitch-bwidth);
tgtpos += pitch;
dstpos += Pitch;
}
return;
}
}
const f32 sourceXStep = (f32)Size.Width / (f32)width;
const f32 sourceYStep = (f32)Size.Height / (f32)height;
s32 yval=0, syval=0;
f32 sy = 0.0f;
for (s32 y=0; y<height; ++y)
{
f32 sx = 0.0f;
for (s32 x=0; x<width; ++x)
{
// CColorConverter::convert_viaFormat(((u8*)Data)+ syval + ((s32)sx)*BytesPerPixel, Format, 1, ((u8*)target)+ yval + (x*bpp), format);
sx+=sourceXStep;
}
sy+=sourceYStep;
syval=((s32)sy)*Pitch;
yval+=pitch;
}
}
//! copies this surface into another, scaling it to the target image size
// note: this is very very slow. (i didn't want to write a fast version.
// but hopefully, nobody wants to scale surfaces every frame.
void SImage::copyToScaling(IImage* target)
{
if (!target)
return;
const core::dimension2d<s32>& targetSize = target->getDimension();
if (targetSize==Size)
{
copyTo(target);
return;
}
copyToScaling(target->lock(), targetSize.Width, targetSize.Height, target->getColorFormat());
target->unlock();
}
//! copies this surface into another
void SImage::copyTo(IImage* target, const core::position2d<s32>& pos)
{
// Blit ( BLITTER_TEXTURE, target, 0, &pos, this, 0, 0 );
}
//! copies this surface into another
void SImage::copyTo(IImage* target, const core::position2d<s32>& pos, const core::rect<s32>& sourceRect, const core::rect<s32>* clipRect)
{
// Blit ( BLITTER_TEXTURE, target, clipRect, &pos, this, &sourceRect, 0 );
}
}//video
}//irr

101
src/Client/GUI/SImage.h Normal file
View File

@ -0,0 +1,101 @@
//most simplistic IImage Implementation, copypasted form irrlichts CImage
#include "irrlicht/irrlicht.h"
namespace irr
{
namespace video
{
class SImage : public IImage
{
public:
//! constructor for empty image
SImage(ECOLOR_FORMAT format, const core::dimension2d<s32>& size);
//! destructor
virtual ~SImage();
//! returns a pixel
virtual SColor getPixel(u32 x, u32 y) const;
//! sets a pixel
virtual void setPixel(u32 x, u32 y, const SColor &color );
//! Lock function.
virtual void* lock()
{
return Data;
};
//! Unlock function.
virtual void unlock() {};
//! Returns width and height of image data.
virtual const core::dimension2d<s32>& getDimension() const;
//! Returns bits per pixel.
virtual u32 getBitsPerPixel() const;
//! Returns bytes per pixel
virtual u32 getBytesPerPixel() const;
//! Returns image data size in bytes
virtual u32 getImageDataSizeInBytes() const;
//! Returns image data size in pixels
virtual u32 getImageDataSizeInPixels() const;
//! returns mask for red value of a pixel
virtual u32 getRedMask() const;
//! returns mask for green value of a pixel
virtual u32 getGreenMask() const;
//! returns mask for blue value of a pixel
virtual u32 getBlueMask() const;
//! returns mask for alpha value of a pixel
virtual u32 getAlphaMask() const;
//! returns the color format
virtual ECOLOR_FORMAT getColorFormat() const;
//! copies this surface into another
void copyTo(IImage* target, const core::position2d<s32>& pos=core::position2d<s32>(0,0));
//! copies this surface into another
void copyTo(IImage* target, const core::position2d<s32>& pos, const core::rect<s32>& sourceRect, const core::rect<s32>* clipRect=0);
//! copies this surface into another, scaling it to fit.
void copyToScaling(void* target, s32 width, s32 height, ECOLOR_FORMAT format, u32 pitch=0);
//! copies this surface into another, scaling it to fit.
void copyToScaling(IImage* target);
//! returns pitch of image
virtual u32 getPitch() const
{
return Pitch;
}
static u32 getBitsPerPixelFromFormat(ECOLOR_FORMAT format);
private:
//! assumes format and size has been set and creates the rest
void initData();
void setBitMasks();
inline SColor getPixelBox ( s32 x, s32 y, s32 fx, s32 fy, s32 bias ) const;
void* Data;
core::dimension2d<s32> Size;
u32 BitsPerPixel;
u32 BytesPerPixel;
u32 Pitch;
ECOLOR_FORMAT Format;
u32 RedMask;
u32 GreenMask;
u32 BlueMask;
u32 AlphaMask;
};
}//video
}//irr