diff --git a/src/Client/GUI/CImageLoaderBLP.cpp b/src/Client/GUI/CImageLoaderBLP.cpp new file mode 100644 index 0000000..d03d615 --- /dev/null +++ b/src/Client/GUI/CImageLoaderBLP.cpp @@ -0,0 +1,310 @@ +#include +#include +#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: "<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:"<read(&header,sizeof(BLPHeader)); + +// std::cout<<"Header data: "< 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 "<seek(header.mip_ofs[0]); + + video::IImage* image = 0; + image = new SImage(ECF_A8R8G8B8, core::dimension2d(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 imagedata5; + core::array imagedata3; + core::array 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>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 <<","<>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;yread(&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;yread(&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;yread(&index,sizeof(u8)); + pixel =image->getPixel(x,y); + pixel.setAlpha(index); + image->setPixel(x,y,pixel); + } + } + } + } + + return image; +} + +}//namespace video +}//namespace irr diff --git a/src/Client/GUI/CImageLoaderBLP.h b/src/Client/GUI/CImageLoaderBLP.h new file mode 100644 index 0000000..d8f449d --- /dev/null +++ b/src/Client/GUI/CImageLoaderBLP.h @@ -0,0 +1,63 @@ +#include +#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 diff --git a/src/Client/GUI/SImage.cpp b/src/Client/GUI/SImage.cpp new file mode 100644 index 0000000..f79935f --- /dev/null +++ b/src/Client/GUI/SImage.cpp @@ -0,0 +1,306 @@ +#include "SImage.h" +namespace irr +{ +namespace video +{ + + + //! constructor for empty image +//! constructor +SImage::SImage(ECOLOR_FORMAT format, const core::dimension2d& 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& 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& 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& pos) +{ +// Blit ( BLITTER_TEXTURE, target, 0, &pos, this, 0, 0 ); +} + + +//! copies this surface into another +void SImage::copyTo(IImage* target, const core::position2d& pos, const core::rect& sourceRect, const core::rect* clipRect) +{ +// Blit ( BLITTER_TEXTURE, target, clipRect, &pos, this, &sourceRect, 0 ); +} + + +}//video +}//irr + diff --git a/src/Client/GUI/SImage.h b/src/Client/GUI/SImage.h new file mode 100644 index 0000000..ef09481 --- /dev/null +++ b/src/Client/GUI/SImage.h @@ -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& 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& 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& pos=core::position2d(0,0)); + + //! copies this surface into another + void copyTo(IImage* target, const core::position2d& pos, const core::rect& sourceRect, const core::rect* 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 Size; + u32 BitsPerPixel; + u32 BytesPerPixel; + u32 Pitch; + ECOLOR_FORMAT Format; + + u32 RedMask; + u32 GreenMask; + u32 BlueMask; + u32 AlphaMask; +}; + +}//video +}//irr