| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530 |
- #include "CImageLoaderTEX.h"
- #include <irrlicht/irrlicht.h>
- #include <lz4.h>
- #include <wallpaperengine/irrlicht.h>
- #include <string>
- 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 CImageLoaderTex::isALoadableFileExtension (const io::path &filename) const
- {
- return core::hasFileExtension (filename, "tex");
- }
- //! returns true if the file maybe is able to be loaded by this class
- bool CImageLoaderTex::isALoadableFileFormat (io::IReadFile *file) const
- {
- return false;
- }
- // load in the image data
- IImage *CImageLoaderTex::loadImage (io::IReadFile *input) const
- {
- if (!input)
- return nullptr;
- video::IImage *image = nullptr;
- char buffer [1024];
- if (input->read (buffer, 9) != 9)
- {
- wp::irrlicht::device->getLogger ()->log ("LOAD TEX: cannot read header\n", input->getFileName ().c_str (), irr::ELL_ERROR);
- return nullptr;
- }
- if (memcmp (buffer, "TEXV0005", 9) != 0)
- {
- wp::irrlicht::device->getLogger ()->log ("LOAD TEX: not really a tex\n", input->getFileName ().c_str (), irr::ELL_ERROR);
- return nullptr;
- }
- if (input->read (buffer, 9) != 9)
- {
- wp::irrlicht::device->getLogger ()->log ("LOAD TEX: cannot read second header\n", input->getFileName ().c_str (), irr::ELL_ERROR);
- return nullptr;
- }
- if (memcmp (buffer, "TEXI0001", 9) != 0)
- {
- wp::irrlicht::device->getLogger ()->log ("LOAD TEX: not really a tex\n", input->getFileName ().c_str (), irr::ELL_ERROR);
- return nullptr;
- }
- u32 width;
- u32 height;
- u32 texture_width;
- u32 texture_height;
- u32 format;
- u32 imageFormat = FREE_IMAGE_FORMAT::FIF_UNKNOWN;
- u8 containerVersion = 0;
- input->read (&format, 4);
- input->seek (4, true); // ignore bytes
- input->read (&texture_width, 4);
- input->read (&texture_height, 4);
- input->read (&width, 4);
- input->read (&height, 4);
- input->seek (4, true); // ignore bytes
- input->read (buffer, 9);
- if (memcmp (buffer, "TEXB0003", 9) == 0)
- {
- containerVersion = 3;
- input->seek (4, true);
- input->read (&imageFormat, 4);
- }
- else if (memcmp (buffer, "TEXB0002", 9) == 0)
- {
- containerVersion = 2;
- input->seek (4, true);
- }
- else
- {
- wp::irrlicht::device->getLogger ()->log ("LOAD TEX: Unknown container type\n", input->getFileName ().c_str (), irr::ELL_ERROR);
- return nullptr;
- }
- if (format == TextureFormat::A8)
- {
- wp::irrlicht::device->getLogger ()-> log ("LOAD TEX: A8 not supported\n", input->getFileName ().c_str (), irr::ELL_ERROR);
- return nullptr;
- }
- if (format == TextureFormat::RA88)
- {
- wp::irrlicht::device->getLogger ()->log ("LOAD TEX: RA88 not supported\n", input->getFileName ().c_str (), irr::ELL_ERROR);
- return nullptr;
- }
- u32 mipmap_count = 0;
- input->read (&mipmap_count, 4);
- u32 mipmap_width = 0;
- u32 mipmap_height = 0;
- u32 mipmap_compression = 0;
- u32 mipmap_uncompressed_size = 0;
- u32 mipmap_compressed_size = 0;
- input->read (&mipmap_width, 4);
- input->read (&mipmap_height, 4);
- input->read (&mipmap_compression, 4);
- input->read (&mipmap_uncompressed_size, 4);
- input->read (&mipmap_compressed_size, 4);
- char *decompressedBuffer = new char [mipmap_uncompressed_size];
- if (mipmap_compression == 1)
- {
- char *compressedBuffer = new char [mipmap_compressed_size];
- input->read (compressedBuffer, mipmap_compressed_size);
- int result = LZ4_decompress_safe (compressedBuffer, decompressedBuffer, mipmap_compressed_size, mipmap_uncompressed_size);
- if (!result)
- {
- delete [] decompressedBuffer;
- delete [] compressedBuffer;
- wp::irrlicht::device->getLogger ()->log ("LOAD TEX: cannot decompress texture data\n", input->getFileName ().c_str (), irr::ELL_ERROR);
- return nullptr;
- }
- delete [] compressedBuffer;
- }
- else
- {
- input->read (decompressedBuffer, mipmap_uncompressed_size);
- }
- if (containerVersion == 2)
- {
- image = wp::irrlicht::driver->createImage (ECF_A8R8G8B8, irr::core::dimension2d<u32> (width, height));
- if (!image)
- {
- delete [] decompressedBuffer;
- delete image;
- wp::irrlicht::device->getLogger ()->log ("LOAD TEX: cannot create destination image\n", input->getFileName ().c_str (), irr::ELL_ERROR);
- return nullptr;
- }
- switch (format)
- {
- case TextureFormat::ARGB8888:
- this->loadImageFromARGB8Data (image, decompressedBuffer, width, height, mipmap_width);
- break;
- case TextureFormat::DXT5:
- this->loadImageFromDXT5 (image, decompressedBuffer, width, height, mipmap_width, mipmap_height);
- break;
- case TextureFormat::DXT1:
- this->loadImageFromDXT1 (image, decompressedBuffer, width, height, mipmap_width, mipmap_height);
- break;
- case TextureFormat::DXT3:
- wp::irrlicht::device->getLogger ()->log ("LOAD TEX: DXT3 textures not supported yet\n", input->getFileName ().c_str (), irr::ELL_ERROR);
- delete [] decompressedBuffer;
- delete image;
- return nullptr;
- }
- }
- else
- {
- // copy the buffer to a new address
- char* filebuffer = new char [mipmap_uncompressed_size];
- char tmpname [TMP_MAX];
- // copy file data to the final file buffer to be used
- memcpy (filebuffer, decompressedBuffer, mipmap_uncompressed_size);
- // generate temporal name
- std::tmpnam (tmpname);
- // store it in a std::string
- std::string filename = tmpname;
- irr::io::IReadFile* file;
- // free image format
- switch (imageFormat)
- {
- case FREE_IMAGE_FORMAT::FIF_BMP:
- // add extension to the file
- filename += ".bmp";
- file = wp::irrlicht::device->getFileSystem ()->createMemoryReadFile (filebuffer, mipmap_uncompressed_size, filename.c_str (), true);
- break;
- case FREE_IMAGE_FORMAT::FIF_PNG:
- // add extension to the file
- filename += ".png";
- file = wp::irrlicht::device->getFileSystem ()->createMemoryReadFile (filebuffer, mipmap_uncompressed_size, filename.c_str (), true);
- break;
- case FREE_IMAGE_FORMAT::FIF_JPEG:
- // add extension to the file
- filename += ".jpg";
- file = wp::irrlicht::device->getFileSystem ()->createMemoryReadFile (filebuffer, mipmap_uncompressed_size, filename.c_str (), true);
- break;
- default:
- wp::irrlicht::device->getLogger ()->log ("LOAD TEX: detected unsupported free-image format\n", input->getFileName ().c_str (), irr::ELL_ERROR);
- delete [] decompressedBuffer;
- delete [] filebuffer;
- return nullptr;
- }
- image = wp::irrlicht::driver->createImageFromFile (file);
- if (!image)
- {
- file->drop ();
-
- delete [] decompressedBuffer;
- delete image;
- wp::irrlicht::device->getLogger ()->log ("LOAD TEX: cannot create destination image\n", input->getFileName ().c_str (), irr::ELL_ERROR);
- return nullptr;
- }
- }
- delete [] decompressedBuffer;
- return image;
- }
- void CImageLoaderTex::loadImageFromARGB8Data (IImage* output, const char* input, u32 width, u32 height, u32 mipmap_width) const
- {
- u32 bytesPerPixel = output->getBytesPerPixel ();
- char *imagedata = (char *) output->lock ();
- for (u32 y = 0; y < height; y ++)
- {
- u32 baseDestination = y * output->getPitch ();
- u32 baseOrigin = y * (mipmap_width * 4);
- for (u32 x = 0; x < width; x ++)
- {
- imagedata [baseDestination + (x * bytesPerPixel) + 2] = input [baseOrigin + ((width - x) * 4) + 0]; // r
- imagedata [baseDestination + (x * bytesPerPixel) + 1] = input [baseOrigin + ((width - x) * 4) + 1]; // g
- imagedata [baseDestination + (x * bytesPerPixel) + 0] = input [baseOrigin + ((width - x) * 4) + 2]; // b
- imagedata [baseDestination + (x * bytesPerPixel) + 3] = input [baseOrigin + ((width - x) * 4) + 3]; // alpha
- }
- }
- output->unlock ();
- }
- void CImageLoaderTex::loadImageFromDXT1 (IImage* output, const char* input, u32 destination_width, u32 destination_height, u32 origin_width, u32 origin_height) const
- {
- char* decompressedBuffer = new char [origin_width * origin_height * 4];
- this->BlockDecompressImageDXT1 (origin_width, origin_height, (const unsigned char*) input, (unsigned long*) decompressedBuffer);
- this->loadImageFromARGB8Data (output, decompressedBuffer, destination_width, destination_height, origin_width);
- delete [] decompressedBuffer;
- }
- void CImageLoaderTex::loadImageFromDXT5 (IImage* output, const char* input, u32 destination_width, u32 destination_height, u32 origin_width, u32 origin_height) const
- {
- char* decompressedBuffer = new char [origin_width * origin_height * 4];
- this->BlockDecompressImageDXT5 (origin_width, origin_height, (const unsigned char*) input, (unsigned long*) decompressedBuffer);
- this->loadImageFromARGB8Data (output, decompressedBuffer, destination_width, destination_height, origin_width);
- delete [] decompressedBuffer;
- }
- // ------------------------------------------------------------------------------------
- // The following code is a slightly modified version of this repository
- // https://github.com/Benjamin-Dobell/s3tc-dxt-decompression
- // ------------------------------------------------------------------------------------
- // unsigned long PackRGBA(): Helper method that packs RGBA channels into a single 4 byte pixel.
- //
- // unsigned char r: red channel.
- // unsigned char g: green channel.
- // unsigned char b: blue channel.
- // unsigned char a: alpha channel.
- unsigned long CImageLoaderTex::PackRGBA(unsigned char r, unsigned char g, unsigned char b, unsigned char a) const
- {
- return ((r << 24) | (g << 16) | (b << 8) | a);
- }
- // void DecompressBlockDXT1(): Decompresses one block of a DXT1 texture and stores the resulting pixels at the appropriate offset in 'image'.
- //
- // unsigned long x: x-coordinate of the first pixel in the block.
- // unsigned long y: y-coordinate of the first pixel in the block.
- // unsigned long width: width of the texture being decompressed.
- // unsigned long height: height of the texture being decompressed.
- // const unsigned char *blockStorage: pointer to the block to decompress.
- // unsigned long *image: pointer to image where the decompressed pixel data should be stored.
- void CImageLoaderTex::DecompressBlockDXT1(unsigned long x, unsigned long y, unsigned long width, const unsigned char *blockStorage, unsigned long *image) const
- {
- unsigned short color0 = *reinterpret_cast<const unsigned short *>(blockStorage);
- unsigned short color1 = *reinterpret_cast<const unsigned short *>(blockStorage + 2);
- unsigned long temp;
- temp = (color0 >> 11) * 255 + 16;
- unsigned char r0 = (unsigned char)((temp/32 + temp)/32);
- temp = ((color0 & 0x07E0) >> 5) * 255 + 32;
- unsigned char g0 = (unsigned char)((temp/64 + temp)/64);
- temp = (color0 & 0x001F) * 255 + 16;
- unsigned char b0 = (unsigned char)((temp/32 + temp)/32);
- temp = (color1 >> 11) * 255 + 16;
- unsigned char r1 = (unsigned char)((temp/32 + temp)/32);
- temp = ((color1 & 0x07E0) >> 5) * 255 + 32;
- unsigned char g1 = (unsigned char)((temp/64 + temp)/64);
- temp = (color1 & 0x001F) * 255 + 16;
- unsigned char b1 = (unsigned char)((temp/32 + temp)/32);
- unsigned long code = *reinterpret_cast<const unsigned long *>(blockStorage + 4);
- for (int j=0; j < 4; j++)
- {
- for (int i=0; i < 4; i++)
- {
- unsigned long finalColor = 0;
- unsigned char positionCode = (code >> 2*(4*j+i)) & 0x03;
- if (color0 > color1)
- {
- switch (positionCode)
- {
- case 0:
- finalColor = PackRGBA(r0, g0, b0, 255);
- break;
- case 1:
- finalColor = PackRGBA(r1, g1, b1, 255);
- break;
- case 2:
- finalColor = PackRGBA((2*r0+r1)/3, (2*g0+g1)/3, (2*b0+b1)/3, 255);
- break;
- case 3:
- finalColor = PackRGBA((r0+2*r1)/3, (g0+2*g1)/3, (b0+2*b1)/3, 255);
- break;
- }
- }
- else
- {
- switch (positionCode)
- {
- case 0:
- finalColor = PackRGBA(r0, g0, b0, 255);
- break;
- case 1:
- finalColor = PackRGBA(r1, g1, b1, 255);
- break;
- case 2:
- finalColor = PackRGBA((r0+r1)/2, (g0+g1)/2, (b0+b1)/2, 255);
- break;
- case 3:
- finalColor = PackRGBA(0, 0, 0, 255);
- break;
- }
- }
- if (x + i < width)
- image[(y + j)*width + (x + i)] = finalColor;
- }
- }
- }
- // void BlockDecompressImageDXT1(): Decompresses all the blocks of a DXT1 compressed texture and stores the resulting pixels in 'image'.
- //
- // unsigned long width: Texture width.
- // unsigned long height: Texture height.
- // const unsigned char *blockStorage: pointer to compressed DXT1 blocks.
- // unsigned long *image: pointer to the image where the decompressed pixels will be stored.
- void CImageLoaderTex::BlockDecompressImageDXT1(unsigned long width, unsigned long height, const unsigned char *blockStorage, unsigned long *image) const
- {
- unsigned long blockCountX = (width + 3) / 4;
- unsigned long blockCountY = (height + 3) / 4;
- unsigned long blockWidth = (width < 4) ? width : 4;
- unsigned long blockHeight = (height < 4) ? height : 4;
- for (unsigned long j = 0; j < blockCountY; j++)
- {
- for (unsigned long i = 0; i < blockCountX; i++) DecompressBlockDXT1(i*4, j*4, width, blockStorage + i * 8, image);
- blockStorage += blockCountX * 8;
- }
- }
- // void DecompressBlockDXT5(): Decompresses one block of a DXT5 texture and stores the resulting pixels at the appropriate offset in 'image'.
- //
- // unsigned long x: x-coordinate of the first pixel in the block.
- // unsigned long y: y-coordinate of the first pixel in the block.
- // unsigned long width: width of the texture being decompressed.
- // unsigned long height: height of the texture being decompressed.
- // const unsigned char *blockStorage: pointer to the block to decompress.
- // unsigned long *image: pointer to image where the decompressed pixel data should be stored.
- void CImageLoaderTex::DecompressBlockDXT5(unsigned long x, unsigned long y, unsigned long width, const unsigned char *blockStorage, unsigned long *image) const
- {
- unsigned char alpha0 = *reinterpret_cast<const unsigned char *>(blockStorage);
- unsigned char alpha1 = *reinterpret_cast<const unsigned char *>(blockStorage + 1);
- const unsigned char *bits = blockStorage + 2;
- unsigned long alphaCode1 = bits[2] | (bits[3] << 8) | (bits[4] << 16) | (bits[5] << 24);
- unsigned short alphaCode2 = bits[0] | (bits[1] << 8);
- unsigned short color0 = *reinterpret_cast<const unsigned short *>(blockStorage + 8);
- unsigned short color1 = *reinterpret_cast<const unsigned short *>(blockStorage + 10);
- unsigned long temp;
- temp = (color0 >> 11) * 255 + 16;
- unsigned char r0 = (unsigned char)((temp/32 + temp)/32);
- temp = ((color0 & 0x07E0) >> 5) * 255 + 32;
- unsigned char g0 = (unsigned char)((temp/64 + temp)/64);
- temp = (color0 & 0x001F) * 255 + 16;
- unsigned char b0 = (unsigned char)((temp/32 + temp)/32);
- temp = (color1 >> 11) * 255 + 16;
- unsigned char r1 = (unsigned char)((temp/32 + temp)/32);
- temp = ((color1 & 0x07E0) >> 5) * 255 + 32;
- unsigned char g1 = (unsigned char)((temp/64 + temp)/64);
- temp = (color1 & 0x001F) * 255 + 16;
- unsigned char b1 = (unsigned char)((temp/32 + temp)/32);
- unsigned long code = *reinterpret_cast<const unsigned long *>(blockStorage + 12);
- for (int j=0; j < 4; j++)
- {
- for (int i=0; i < 4; i++)
- {
- int alphaCodeIndex = 3*(4*j+i);
- int alphaCode;
- if (alphaCodeIndex <= 12)
- {
- alphaCode = (alphaCode2 >> alphaCodeIndex) & 0x07;
- }
- else if (alphaCodeIndex == 15)
- {
- alphaCode = (alphaCode2 >> 15) | ((alphaCode1 << 1) & 0x06);
- }
- else // alphaCodeIndex >= 18 && alphaCodeIndex <= 45
- {
- alphaCode = (alphaCode1 >> (alphaCodeIndex - 16)) & 0x07;
- }
- unsigned char finalAlpha;
- if (alphaCode == 0)
- {
- finalAlpha = alpha0;
- }
- else if (alphaCode == 1)
- {
- finalAlpha = alpha1;
- }
- else
- {
- if (alpha0 > alpha1)
- {
- finalAlpha = ((8-alphaCode)*alpha0 + (alphaCode-1)*alpha1)/7;
- }
- else
- {
- if (alphaCode == 6)
- finalAlpha = 0;
- else if (alphaCode == 7)
- finalAlpha = 255;
- else
- finalAlpha = ((6-alphaCode)*alpha0 + (alphaCode-1)*alpha1)/5;
- }
- }
- unsigned char colorCode = (code >> 2*(4*j+i)) & 0x03;
- unsigned long finalColor;
- switch (colorCode)
- {
- case 0:
- finalColor = PackRGBA(r0, g0, b0, finalAlpha);
- break;
- case 1:
- finalColor = PackRGBA(r1, g1, b1, finalAlpha);
- break;
- case 2:
- finalColor = PackRGBA((2*r0+r1)/3, (2*g0+g1)/3, (2*b0+b1)/3, finalAlpha);
- break;
- case 3:
- finalColor = PackRGBA((r0+2*r1)/3, (g0+2*g1)/3, (b0+2*b1)/3, finalAlpha);
- break;
- }
- if (x + i < width)
- image[(y + j)*width + (x + i)] = finalColor;
- }
- }
- }
- // void BlockDecompressImageDXT5(): Decompresses all the blocks of a DXT5 compressed texture and stores the resulting pixels in 'image'.
- //
- // unsigned long width: Texture width.
- // unsigned long height: Texture height.
- // const unsigned char *blockStorage: pointer to compressed DXT5 blocks.
- // unsigned long *image: pointer to the image where the decompressed pixels will be stored.
- void CImageLoaderTex::BlockDecompressImageDXT5(unsigned long width, unsigned long height, const unsigned char *blockStorage, unsigned long *image) const
- {
- unsigned long blockCountX = (width + 3) / 4;
- unsigned long blockCountY = (height + 3) / 4;
- unsigned long blockWidth = (width < 4) ? width : 4;
- unsigned long blockHeight = (height < 4) ? height : 4;
- for (unsigned long j = 0; j < blockCountY; j++)
- {
- for (unsigned long i = 0; i < blockCountX; i++) DecompressBlockDXT5(i*4, j*4, width, blockStorage + i * 16, image);
- blockStorage += blockCountX * 16;
- }
- }
- }// end namespace irr
- }//end namespace video
|