diff --git a/CMakeLists.txt b/CMakeLists.txt index 947f723..07f8878 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,3 +3,4 @@ PROJECT(TexC) ADD_SUBDIRECTORY(BPTCEncoder) ADD_SUBDIRECTORY(IO) +ADD_SUBDIRECTORY(Core) diff --git a/Core/CMakeLists.txt b/Core/CMakeLists.txt index f8a9de6..c17b51d 100644 --- a/Core/CMakeLists.txt +++ b/Core/CMakeLists.txt @@ -1,15 +1,21 @@ SET( SOURCES "src/TexComp.cpp" + "src/CompressedImage.cpp" ) SET( HEADERS "include/TexComp.h" + "include/CompressedImage.h" ) +INCLUDE_DIRECTORIES( ${TexC_BINARY_DIR}/IO/include ) +INCLUDE_DIRECTORIES( ${TexC_SOURCE_DIR}/IO/include ) INCLUDE_DIRECTORIES( ${TexC_SOURCE_DIR}/Core/include ) ADD_LIBRARY( TexCompCore ${HEADERS} ${SOURCES} ) + +TARGET_LINK_LIBRARIES( TexCompCore TexCompIO ) diff --git a/Core/include/CompressedImage.h b/Core/include/CompressedImage.h new file mode 100644 index 0000000..f7737fa --- /dev/null +++ b/Core/include/CompressedImage.h @@ -0,0 +1,34 @@ +#ifndef _COMPRESSED_IMAGE_H_ +#define _COMPRESSED_IMAGE_H_ + +enum ECompressionFormat { + eCompressionFormat_DXT1, + eCompressionFormat_DXT5, + eCompressionFormat_BPTC, + + kNumCompressionFormats +}; + +class CompressedImage { + + private: + unsigned char *m_Data; + unsigned int m_DataSz; + unsigned int m_Width; + unsigned int m_Height; + ECompressionFormat m_Format; + + void InitData(const unsigned char *withData); + public: + CompressedImage( + const unsigned int width, + const unsigned int height, + const ECompressionFormat format, + const unsigned char *data + ); + + CompressedImage( const CompressedImage &other ); + ~CompressedImage(); +}; + +#endif // _COMPRESSED_IMAGE_H_ diff --git a/Core/include/TexComp.h b/Core/include/TexComp.h index 64d37b2..41a6b58 100644 --- a/Core/include/TexComp.h +++ b/Core/include/TexComp.h @@ -1,4 +1,27 @@ #ifndef _TEX_COMP_H_ #define _TEX_COMP_H_ +#include "ImageFile.h" +#include "CompressedImage.h" + +struct SCompressionSettings { + SCompressionSettings(); // defaults + ECompressionFormat format; + bool bUseSIMD; + int iNumThreads; +}; + +extern void CompressImage( + const ImageFile &, + CompressedImage &, + const SCompressionSettings &settings +); + +typedef void (* CompressionFunc)( + const unsigned char *inData, + unsigned char *outData, + unsigned int width, + unsigned int height +); + #endif //_TEX_COMP_H_ diff --git a/Core/src/CompressedImage.cpp b/Core/src/CompressedImage.cpp new file mode 100644 index 0000000..5e1b639 --- /dev/null +++ b/Core/src/CompressedImage.cpp @@ -0,0 +1,50 @@ +#include "CompressedImage.h" + +#include +#include + +CompressedImage::CompressedImage( const CompressedImage &other ) + : m_Width(other.m_Width) + , m_Height(other.m_Height) + , m_Format(other.m_Format) + , m_Data(0) +{ + InitData(other.m_Data); +} + +CompressedImage::CompressedImage( + const unsigned int width, + const unsigned int height, + const ECompressionFormat format, + const unsigned char *data +) +: m_Width(width) +, m_Height(height) +, m_Format(format) +, m_Data(0) +{ + InitData(data); +} + +void CompressedImage::InitData(const unsigned char *withData) { + unsigned int dataSz = 0; + int uncompDataSz = m_Width * m_Height * 4; + + switch(m_Format) { + case eCompressionFormat_DXT1: dataSz = uncompDataSz / 8; break; + case eCompressionFormat_DXT5: dataSz = uncompDataSz / 4; break; + case eCompressionFormat_BPTC: dataSz = uncompDataSz / 4; break; + } + + if(dataSz > 0) { + m_Data = new unsigned char[dataSz]; + memcpy(m_Data, withData, dataSz); + } +} + +CompressedImage::~CompressedImage() { + if(m_Data) { + delete [] m_Data; + m_Data = NULL; + } +} diff --git a/Core/src/TexComp.cpp b/Core/src/TexComp.cpp index 5aa2a27..6e88e9e 100644 --- a/Core/src/TexComp.cpp +++ b/Core/src/TexComp.cpp @@ -1 +1,55 @@ #include "TexComp.h" + +#include +#include + +SCompressionSettings:: SCompressionSettings() + : format(eCompressionFormat_BPTC) + , bUseSIMD(false) + , iNumThreads(1) +{} + +static CompressionFunc ChooseFuncFromSettings(const SCompressionSettings &s) { + return NULL; +} + +static void ReportError(const char *msg) { + fprintf(stderr, "TexComp -- %s\n", msg); +} + +void CompressImage( + const ImageFile &img, + CompressedImage &outImg, + const SCompressionSettings &settings +) { + + const unsigned int dataSz = img.GetWidth() * img.GetHeight() * 4; + + // Allocate data based on the compression method + int cmpDataSz = 0; + switch(settings.format) { + case eCompressionFormat_DXT1: cmpDataSz = dataSz / 8; + case eCompressionFormat_DXT5: cmpDataSz = dataSz / 4; + case eCompressionFormat_BPTC: cmpDataSz = dataSz / 4; + } + + if(cmpDataSz == 0) { + ReportError("Unknown compression format"); + return; + } + + unsigned char *cmpData = new unsigned char[cmpDataSz]; + + CompressionFunc f = ChooseFuncFromSettings(settings); + if(f) { + (*f)(img.GetPixels(), cmpData, img.GetWidth(), img.GetHeight()); + outImg = CompressedImage(img.GetWidth(), img.GetHeight(), settings.format, cmpData); + } + else { + ReportError("Could not find adequate compression function for specified settings"); + // return + } + + // Cleanup + delete [] cmpData; +} diff --git a/IO/CMakeLists.txt b/IO/CMakeLists.txt index 20aa058..f97cdad 100644 --- a/IO/CMakeLists.txt +++ b/IO/CMakeLists.txt @@ -12,7 +12,6 @@ FIND_PACKAGE( PNG ) IF( PNG_FOUND ) INCLUDE_DIRECTORIES( ${PNG_INCLUDE_DIR} ) - SET( SOURCES ${SOURCES} "src/ImageLoaderPNG.cpp" ) SET( HEADERS ${HEADERS} "src/ImageLoaderPNG.h" ) ENDIF() diff --git a/IO/config/ImageLoader.h.in b/IO/config/ImageLoader.h.in index 758b9a5..d76731a 100644 --- a/IO/config/ImageLoader.h.in +++ b/IO/config/ImageLoader.h.in @@ -62,16 +62,19 @@ class ImageLoader { virtual bool ReadData() = 0; int GetRedChannelPrecision() const { return m_RedChannelPrecision; } - unsigned char * GetRedPixelData() const { return m_RedData; } + const unsigned char * GetRedPixelData() const { return m_RedData; } int GetGreenChannelPrecision() const { return m_GreenChannelPrecision; } - unsigned char * GetGreenPixelData() const { return m_GreenData; } + const unsigned char * GetGreenPixelData() const { return m_GreenData; } int GetBlueChannelPrecision() const { return m_BlueChannelPrecision; } - unsigned char * GetBluePixelData() const { return m_BlueData; } + const unsigned char * GetBluePixelData() const { return m_BlueData; } int GetAlphaChannelPrecision() const { return m_AlphaChannelPrecision; } - unsigned char * GetAlphaPixelData() const { return m_AlphaData; } + const unsigned char * GetAlphaPixelData() const { return m_AlphaData; } + + int GetWidth() const { return m_Width; } + int GetHeight() const { return m_Height; } }; #cmakedefine PNG_FOUND diff --git a/IO/src/ImageFile.cpp b/IO/src/ImageFile.cpp index 9b53120..ab5e623 100644 --- a/IO/src/ImageFile.cpp +++ b/IO/src/ImageFile.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include #include "ImageFile.h" @@ -9,6 +10,101 @@ # include "ImageLoaderPNG.h" #endif +////////////////////////////////////////////////////////////////////////////////////////// +// +// Static helper functions +// +////////////////////////////////////////////////////////////////////////////////////////// + +static inline void ReportError(const char *msg) { + fprintf(stderr, "ImageFile -- %s\n", msg); +} + +template +static inline T abs(const T &a) { + return a > 0? a : -a; +} + +template +static inline T min(const T &a, const T &b) { + return (a < b)? a : b; +} + +static unsigned int GetChannelForPixel( + const ImageLoader *loader, + unsigned int x, unsigned int y, + int ch +) { + unsigned int prec; + const unsigned char *data; + + switch(ch) { + case 0: + prec = loader->GetRedChannelPrecision(); + data = loader->GetRedPixelData(); + break; + + case 1: + prec = loader->GetGreenChannelPrecision(); + data = loader->GetGreenPixelData(); + break; + + case 2: + prec = loader->GetBlueChannelPrecision(); + data = loader->GetBluePixelData(); + break; + + case 3: + prec = loader->GetAlphaChannelPrecision(); + data = loader->GetAlphaPixelData(); + break; + + default: + ReportError("Unspecified channel"); + return INT_MAX; + } + + if(0 == prec) + return 0; + + assert(x < loader->GetWidth()); + assert(y < loader->GetHeight()); + + int pixelIdx = y * loader->GetWidth() + x; + const unsigned int val = data[pixelIdx]; + + if(prec < 8) { + unsigned int ret = 0; + for(unsigned int precLeft = 8; precLeft > 0; precLeft -= min(prec, abs(prec - precLeft))) { + + if(prec > precLeft) { + const int toShift = prec - precLeft; + ret = ret << precLeft; + ret |= val >> toShift; + } + else { + ret = ret << prec; + ret |= val; + } + + } + + return ret; + } + else if(prec > 8) { + const int toShift = prec - 8; + return val >> toShift; + } + + return val; +} + +////////////////////////////////////////////////////////////////////////////////////////// +// +// ImageFile implementation +// +////////////////////////////////////////////////////////////////////////////////////////// + ImageFile::ImageFile(const char *filename) : m_PixelData(0), m_FileFormat( DetectFileFormat(filename) ) @@ -80,7 +176,77 @@ bool ImageFile::LoadImage(const unsigned char *rawImageData) { break; } - return false; + m_Width = loader->GetWidth(); + m_Height = loader->GetHeight(); + + // Create RGBA buffer + const unsigned int dataSz = 4 * m_Width * m_Height; + m_PixelData = new unsigned char[dataSz]; + + // Populate buffer in block stream order... make + // sure that width and height are aligned to multiples of four. + const unsigned int aw = ((m_Width + 3) >> 2) << 2; + const unsigned int ah = ((m_Height + 3) >> 2) << 2; + + int byteIdx = 0; + for(int i = 0; i < ah; i+=4) { + for(int j = 0; j < aw; j+= 4) { + + // For each block, visit the pixels in sequential order + for(int y = i; y < i+4; y++) { + for(int x = j; x < j+4; x++) { + + if(y >= m_Height || x >= m_Width) { + m_PixelData[byteIdx++] = 0; // r + m_PixelData[byteIdx++] = 0; // g + m_PixelData[byteIdx++] = 0; // b + m_PixelData[byteIdx++] = 0; // a + continue; + } + + unsigned int redVal = GetChannelForPixel(loader, x, y, 0); + if(redVal == INT_MAX) + return false; + + unsigned int greenVal = redVal; + unsigned int blueVal = redVal; + + if(loader->GetGreenChannelPrecision() > 0) { + greenVal = GetChannelForPixel(loader, x, y, 1); + if(greenVal == INT_MAX) + return false; + } + + if(loader->GetBlueChannelPrecision() > 0) { + blueVal = GetChannelForPixel(loader, x, y, 2); + if(blueVal == INT_MAX) + return false; + } + + unsigned int alphaVal = 0xFF; + if(loader->GetAlphaChannelPrecision() > 0) { + alphaVal = GetChannelForPixel(loader, x, y, 3); + if(alphaVal == INT_MAX) + return false; + } + + // Red channel + m_PixelData[byteIdx++] = redVal & 0xFF; + + // Green channel + m_PixelData[byteIdx++] = greenVal & 0xFF; + + // Blue channel + m_PixelData[byteIdx++] = blueVal & 0xFF; + + // Alpha channel + m_PixelData[byteIdx++] = alphaVal & 0xFF; + } + } + } + } + + return true; } #ifdef _MSC_VER