+ Added support for DXT1 and DXT5 (not tested yet)

+ Added support for textures with normal images in them (bmp. png and jpg)

Signed-off-by: Alexis Maiquez Murcia <almamu@almamu.com>
This commit is contained in:
Alexis Maiquez Murcia 2019-04-03 16:58:23 +02:00
parent ae9665772a
commit e5affb470d
2 changed files with 447 additions and 49 deletions

View File

@ -1,6 +1,3 @@
// Copyright (C) 2002-2012 Nikolaus Gebhardt
// This file is part of the "Irrlicht Engine".
// For conditions of distribution and use, see copyright notice in irrlicht.h
#include "CImageLoaderTEX.h"
@ -31,34 +28,34 @@ namespace irr {
IImage *CImageLoaderTex::loadImage (io::IReadFile *input) const
{
if (!input)
return 0;
return nullptr;
video::IImage *image = 0;
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 0;
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 0;
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 0;
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 0;
return nullptr;
}
u32 width;
@ -66,6 +63,8 @@ namespace irr {
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
@ -78,23 +77,32 @@ namespace irr {
if (memcmp (buffer, "TEXB0003", 9) == 0)
{
wp::irrlicht::device->getLogger ()->log ("LOAD TEX: TEXB0003 container not supported yet\n", input->getFileName ().c_str (), irr::ELL_ERROR);
return 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 0;
return nullptr;
}
if (format != TextureFormat::ARGB8888)
if (format == TextureFormat::A8)
{
wp::irrlicht::device->getLogger ()->log ("LOAD TEX: Only ARGB8888 supported\n", input->getFileName ().c_str (), irr::ELL_ERROR);
return 0;
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;
@ -128,7 +136,7 @@ namespace irr {
delete [] decompressedBuffer;
delete [] compressedBuffer;
wp::irrlicht::device->getLogger ()->log ("LOAD TEX: cannot decompress texture data\n", input->getFileName ().c_str (), irr::ELL_ERROR);
return 0;
return nullptr;
}
delete [] compressedBuffer;
@ -138,41 +146,385 @@ namespace irr {
input->read (decompressedBuffer, mipmap_uncompressed_size);
}
image = wp::irrlicht::driver->createImage (ECF_A8R8G8B8, irr::core::dimension2d<u32> (width, height));
if (!image)
if (containerVersion == 2)
{
delete [] decompressedBuffer;
delete image;
wp::irrlicht::device->getLogger ()->log ("LOAD TEX: cannot create destination image\n", input->getFileName ().c_str (), irr::ELL_ERROR);
return 0;
}
image = wp::irrlicht::driver->createImage (ECF_A8R8G8B8, irr::core::dimension2d<u32> (width, height));
u32 bytesPerPixel = image->getBytesPerPixel ();
char *imagedata = (char *) image->lock ();
for (u32 y = 0; y < height; y ++)
{
u32 baseDestination = y * image->getPitch ();
u32 baseOrigin = y * (mipmap_width * 4);
for (u32 x = 0; x < width; x ++)
if (!image)
{
imagedata [baseDestination + (x * bytesPerPixel) + 2] = decompressedBuffer [baseOrigin + ((width - x) * 4) + 0]; // r
imagedata [baseDestination + (x * bytesPerPixel) + 1] = decompressedBuffer [baseOrigin + ((width - x) * 4) + 1]; // g
imagedata [baseDestination + (x * bytesPerPixel) + 0] = decompressedBuffer [baseOrigin + ((width - x) * 4) + 2]; // b
imagedata [baseDestination + (x * bytesPerPixel) + 3] = decompressedBuffer [baseOrigin + ((width - x) * 4) + 3]; // alpha
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];
image->unlock ();
// 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

View File

@ -1,13 +1,5 @@
// Copyright (C) 2002-2012 Nikolaus Gebhardt
// This file is part of the "Irrlicht Engine".
// For conditions of distribution and use, see copyright notice in irrlicht.h
// this file was created by rt (www.tomkorp.com), based on ttk's png-reader
// i wanted to be able to read in PNG images with irrlicht :)
// why? lossless compression with 8-bit alpha channel!
#ifndef __C_IMAGE_LOADER_TEX_H_INCLUDED__
#define __C_IMAGE_LOADER_PNG_H_INCLUDED__
#define __C_IMAGE_LOADER_TEX_H_INCLUDED__
#include <irrlicht/irrlicht.h>
@ -31,7 +23,18 @@ public:
//! creates a surface from the file
virtual IImage* loadImage(io::IReadFile* input) const;
virtual void loadImageFromARGB8Data (IImage* output, const char* input, u32 width, u32 height, u32 mipmap_width) const;
virtual void loadImageFromDXT1 (IImage* output, const char* input, u32 destination_width, u32 destination_height, u32 origin_width, u32 origin_height) const;
virtual void loadImageFromDXT5 (IImage* output, const char* input, u32 destination_width, u32 destination_height, u32 origin_width, u32 origin_height) const;
private:
void BlockDecompressImageDXT1(unsigned long width, unsigned long height, const unsigned char *blockStorage, unsigned long *image) const;
void DecompressBlockDXT1(unsigned long x, unsigned long y, unsigned long width, const unsigned char *blockStorage, unsigned long *image) const;
unsigned long PackRGBA(unsigned char r, unsigned char g, unsigned char b, unsigned char a) const;
void BlockDecompressImageDXT5(unsigned long width, unsigned long height, const unsigned char *blockStorage, unsigned long *image) const;
void DecompressBlockDXT5(unsigned long x, unsigned long y, unsigned long width, const unsigned char *blockStorage, unsigned long *image) const;
enum TextureFormat
{
ARGB8888,
@ -41,11 +44,54 @@ private:
DXT3,
DXT1
};
// extracted from the free image library
enum FREE_IMAGE_FORMAT
{
FIF_UNKNOWN = -1,
FIF_BMP = 0,
FIF_ICO = 1,
FIF_JPEG = 2,
FIF_JNG = 3,
FIF_KOALA = 4,
FIF_LBM = 5,
FIF_IFF = FIF_LBM,
FIF_MNG = 6,
FIF_PBM = 7,
FIF_PBMRAW = 8,
FIF_PCD = 9,
FIF_PCX = 10,
FIF_PGM = 11,
FIF_PGMRAW = 12,
FIF_PNG = 13,
FIF_PPM = 14,
FIF_PPMRAW = 15,
FIF_RAS = 16,
FIF_TARGA = 17,
FIF_TIFF = 18,
FIF_WBMP = 19,
FIF_PSD = 20,
FIF_CUT = 21,
FIF_XBM = 22,
FIF_XPM = 23,
FIF_DDS = 24,
FIF_GIF = 25,
FIF_HDR = 26,
FIF_FAXG3 = 27,
FIF_SGI = 28,
FIF_EXR = 29,
FIF_J2K = 30,
FIF_JP2 = 31,
FIF_PFM = 32,
FIF_PICT = 33,
FIF_RAW = 34,
FIF_WEBP = 35,
FIF_JXR = 36
};
};
} // end namespace video
} // end namespace irr
#endif
#endif /* !__C_IMAGE_LOADER_TEX_H_INCLUDED__ */