diff --git a/src/nvtt/Context.cpp b/src/nvtt/Context.cpp index a1fe68d..71b1fd8 100644 --- a/src/nvtt/Context.cpp +++ b/src/nvtt/Context.cpp @@ -279,7 +279,7 @@ void Compressor::enableCudaAcceleration(bool enable) } } -/// Check if CUDA acceleration is enabled. +/// Return true if CUDA acceleration is enabled, false otherwise. bool Compressor::isCudaAccelerationEnabled() const { return m.cudaEnabled; @@ -299,14 +299,13 @@ int Compressor::estimateSize(const InputOptions & inputOptions, const Compressio return m.estimateSize(inputOptions.m, compressionOptions.m); } -/// Create a texture. -TexImage Compressor::createTexImage() +// RAW api. +bool Compressor::compress2D(InputFormat format, int w, int h, void * data, const CompressionOptions & compressionOptions, const OutputOptions & outputOptions) const { - return *new TexImage(); + // @@ Make sure type of input format matches compression format. } -/// Estimate the size of compressing the given texture. -int Compressor::estimateSize(const TexImage & tex, const CompressionOptions & compressionOptions) +int Compressor::estimateSize(int w, int h, int d, const CompressionOptions & compressionOptions) const { const CompressionOptions::Private & co = compressionOptions.m; @@ -315,17 +314,39 @@ int Compressor::estimateSize(const TexImage & tex, const CompressionOptions & co uint bitCount = co.bitcount; if (format == Format_RGBA && bitCount == 0) bitCount = co.rsize + co.gsize + co.bsize + co.asize; + return computeImageSize(w, h, d, bitCount, format); +} + + + +/// Create a TexImage. +TexImage Compressor::createTexImage() const +{ + return *new TexImage(); +} + + +bool Compressor::outputHeader(const TexImage & tex, int mipmapCount, const CompressionOptions & compressionOptions, const OutputOptions & outputOptions) const +{ + m.outputHeader(tex, mipmapCount, compressionOptions.m, outputOptions.m); +} + +bool Compressor::compress(const TexImage & tex, const CompressionOptions & compressionOptions, const OutputOptions & outputOptions) const +{ + // @@ Convert to fixed point and call compress2D for each face. +} + +/// Estimate the size of compressing the given texture. +int Compressor::estimateSize(const TexImage & tex, const CompressionOptions & compressionOptions) const +{ const uint w = tex.width(); const uint h = tex.height(); const uint d = tex.depth(); const uint faceCount = tex.faceCount(); - return faceCount * computeImageSize(w, h, d, bitCount, format); + return faceCount * estimateSize(w, h, d, compressionOptions); } -void Compressor::outputCompressed(const TexImage & tex, const CompressionOptions & compressionOptions, const OutputOptions & outputOptions) -{ -} @@ -605,6 +626,247 @@ bool Compressor::Private::outputHeader(const InputOptions::Private & inputOption return true; } +bool Compressor::Private::outputHeader(const TexImage & tex, int mipmapCount, const CompressionOptions::Private & compressionOptions, const OutputOptions::Private & outputOptions) +{ + if (tex.width() <= 0 || tex.height() <= 0 || tex.depth() <= 0 || mipmapCount <= 0) + { +#pragma message(NV_FILE_LINE "Set invalid argument error") + return false; + } + + if (outputOptions.outputHandler == NULL || !outputOptions.outputHeader) + { + return true; + } + + // Output DDS header. + if (outputOptions.container == Container_DDS || outputOptions.container == Container_DDS10) + { + DDSHeader header; + + header.setWidth(tex.width()); + header.setHeight(tex.height()); + header.setMipmapCount(mipmapCount); + + bool supported = true; + + if (outputOptions.container == Container_DDS10) + { + if (compressionOptions.format == Format_RGBA) + { + int bitcount = compressionOptions.bitcount; + if (bitcount == 0) { + bitcount = compressionOptions.rsize + compressionOptions.gsize + compressionOptions.bsize + compressionOptions.asize; + } + + if (bitcount == 16) + { + if (compressionOptions.rsize == 16) + { + header.setDX10Format(56); // R16_UNORM + } + else + { + // B5G6R5_UNORM + // B5G5R5A1_UNORM + supported = false; + } + } + else if (bitcount == 32) + { + // B8G8R8A8_UNORM + // B8G8R8X8_UNORM + // R8G8B8A8_UNORM + // R10G10B10A2_UNORM + supported = false; + } + else { + supported = false; + } + } + else + { + if (compressionOptions.format == Format_DXT1 || compressionOptions.format == Format_DXT1a || compressionOptions.format == Format_DXT1n) { + header.setDX10Format(71); + if (compressionOptions.format == Format_DXT1a) header.setHasAlphaFlag(true); + if (tex.isNormalMap()) header.setNormalFlag(true); + } + else if (compressionOptions.format == Format_DXT3) { + header.setDX10Format(74); + } + else if (compressionOptions.format == Format_DXT5) { + header.setDX10Format(77); + } + else if (compressionOptions.format == Format_DXT5n) { + header.setDX10Format(77); + if (tex.isNormalMap()) header.setNormalFlag(true); + } + else if (compressionOptions.format == Format_BC4) { + header.setDX10Format(80); + } + else if (compressionOptions.format == Format_BC5) { + header.setDX10Format(83); + if (tex.isNormalMap()) header.setNormalFlag(true); + } + else { + supported = false; + } + } + } + else + { + if (compressionOptions.format == Format_RGBA) + { + // Get output bit count. + header.setPitch(computePitch(tex.width(), compressionOptions.getBitCount())); + + if (compressionOptions.pixelType == PixelType_Float) + { + if (compressionOptions.rsize == 16 && compressionOptions.gsize == 0 && compressionOptions.bsize == 0 && compressionOptions.asize == 0) + { + header.setFormatCode(111); // D3DFMT_R16F + } + else if (compressionOptions.rsize == 16 && compressionOptions.gsize == 16 && compressionOptions.bsize == 0 && compressionOptions.asize == 0) + { + header.setFormatCode(112); // D3DFMT_G16R16F + } + else if (compressionOptions.rsize == 16 && compressionOptions.gsize == 16 && compressionOptions.bsize == 16 && compressionOptions.asize == 16) + { + header.setFormatCode(113); // D3DFMT_A16B16G16R16F + } + else if (compressionOptions.rsize == 32 && compressionOptions.gsize == 0 && compressionOptions.bsize == 0 && compressionOptions.asize == 0) + { + header.setFormatCode(114); // D3DFMT_R32F + } + else if (compressionOptions.rsize == 32 && compressionOptions.gsize == 32 && compressionOptions.bsize == 0 && compressionOptions.asize == 0) + { + header.setFormatCode(115); // D3DFMT_G32R32F + } + else if (compressionOptions.rsize == 32 && compressionOptions.gsize == 32 && compressionOptions.bsize == 32 && compressionOptions.asize == 32) + { + header.setFormatCode(116); // D3DFMT_A32B32G32R32F + } + else + { + supported = false; + } + } + else // Fixed point + { + const uint bitcount = compressionOptions.getBitCount(); + + if (compressionOptions.bitcount != 0) + { + // Masks already computed. + header.setPixelFormat(compressionOptions.bitcount, compressionOptions.rmask, compressionOptions.gmask, compressionOptions.bmask, compressionOptions.amask); + } + else if (bitcount <= 32) + { + // Compute pixel format masks. + const uint ashift = 0; + const uint bshift = ashift + compressionOptions.asize; + const uint gshift = bshift + compressionOptions.bsize; + const uint rshift = gshift + compressionOptions.gsize; + + const uint rmask = ((1 << compressionOptions.rsize) - 1) << rshift; + const uint gmask = ((1 << compressionOptions.gsize) - 1) << gshift; + const uint bmask = ((1 << compressionOptions.bsize) - 1) << bshift; + const uint amask = ((1 << compressionOptions.asize) - 1) << ashift; + + header.setPixelFormat(bitcount, rmask, gmask, bmask, amask); + } + else + { + supported = false; + } + } + } + else + { + header.setLinearSize(computeImageSize(tex.width(), tex.height(), tex.depth(), compressionOptions.bitcount, compressionOptions.format)); + + if (compressionOptions.format == Format_DXT1 || compressionOptions.format == Format_DXT1a || compressionOptions.format == Format_DXT1n) { + header.setFourCC('D', 'X', 'T', '1'); + if (tex.isNormalMap()) header.setNormalFlag(true); + } + else if (compressionOptions.format == Format_DXT3) { + header.setFourCC('D', 'X', 'T', '3'); + } + else if (compressionOptions.format == Format_DXT5) { + header.setFourCC('D', 'X', 'T', '5'); + } + else if (compressionOptions.format == Format_DXT5n) { + header.setFourCC('D', 'X', 'T', '5'); + if (tex.isNormalMap()) { + header.setNormalFlag(true); + header.setSwizzleCode('A', '2', 'D', '5'); + //header.setSwizzleCode('x', 'G', 'x', 'R'); + } + } + else if (compressionOptions.format == Format_BC4) { + header.setFourCC('A', 'T', 'I', '1'); + } + else if (compressionOptions.format == Format_BC5) { + header.setFourCC('A', 'T', 'I', '2'); + if (tex.isNormalMap()) { + header.setNormalFlag(true); + header.setSwizzleCode('A', '2', 'X', 'Y'); + } + } + else if (compressionOptions.format == Format_CTX1) { + header.setFourCC('C', 'T', 'X', '1'); + if (tex.isNormalMap()) header.setNormalFlag(true); + } + else { + supported = false; + } + } + } + + if (!supported) + { + // This container does not support the requested format. + if (outputOptions.errorHandler != NULL) + { + outputOptions.errorHandler->error(Error_UnsupportedOutputFormat); + } + + return false; + } + + if (tex.textureType() == TextureType_2D) { + header.setTexture2D(); + } + else if (tex.textureType() == TextureType_Cube) { + header.setTextureCube(); + } + /*else if (tex.textureType() == TextureType_3D) { + header.setTexture3D(); + header.setDepth(tex.depth()); + }*/ + + // Swap bytes if necessary. + header.swapBytes(); + + uint headerSize = 128; + if (header.hasDX10Header()) + { + nvStaticCheck(sizeof(DDSHeader) == 128 + 20); + headerSize = 128 + 20; + } + + bool writeSucceed = outputOptions.outputHandler->writeData(&header, headerSize); + if (!writeSucceed && outputOptions.errorHandler != NULL) + { + outputOptions.errorHandler->error(Error_FileWrite); + } + + return writeSucceed; + } + + return true; +} + bool Compressor::Private::compressMipmaps(uint f, const InputOptions::Private & inputOptions, const CompressionOptions::Private & compressionOptions, const OutputOptions::Private & outputOptions) const { diff --git a/src/nvtt/Context.h b/src/nvtt/Context.h index 9b98bdb..727bd1c 100644 --- a/src/nvtt/Context.h +++ b/src/nvtt/Context.h @@ -46,6 +46,8 @@ namespace nvtt bool compress(const InputOptions::Private & inputOptions, const CompressionOptions::Private & compressionOptions, const OutputOptions::Private & outputOptions) const; int estimateSize(const InputOptions::Private & inputOptions, const CompressionOptions::Private & compressionOptions) const; + bool outputHeader(const TexImage & tex, int mipmapCount, const CompressionOptions::Private & compressionOptions, const OutputOptions::Private & outputOptions); + private: bool outputHeader(const InputOptions::Private & inputOptions, const CompressionOptions::Private & compressionOptions, const OutputOptions::Private & outputOptions) const; diff --git a/src/nvtt/TexImage.cpp b/src/nvtt/TexImage.cpp index d3305d1..8f4fb7d 100644 --- a/src/nvtt/TexImage.cpp +++ b/src/nvtt/TexImage.cpp @@ -60,6 +60,44 @@ namespace return pp2; } } + +#pragma message(NV_FILE_LINE "Move these functions to a common location") + static int blockSize(Format format) + { + if (format == Format_DXT1 || format == Format_DXT1a || format == Format_DXT1n) { + return 8; + } + else if (format == Format_DXT3) { + return 16; + } + else if (format == Format_DXT5 || format == Format_DXT5n) { + return 16; + } + else if (format == Format_BC4) { + return 8; + } + else if (format == Format_BC5) { + return 16; + } + else if (format == Format_CTX1) { + return 8; + } + return 0; + } + + static uint countMipmaps(int w, int h, int d) + { + uint mipmap = 0; + + while (w != 1 || h != 1 || d != 1) { + w = max(1, w / 2); + h = max(1, h / 2); + d = max(1, d / 2); + mipmap++; + } + + return mipmap + 1; + } } @@ -191,9 +229,15 @@ bool TexImage::isNormalMap() const return m->isNormalMap; } +int TexImage::countMipmaps() const +{ + return ::countMipmaps(width(), height(), depth()); +} + + bool TexImage::load(const char * fileName) { - // @@ Add support for DDS textures! +#pragma message(NV_FILE_LINE "Add support for DDS textures in TexImage::load") AutoPtr img(ImageIO::loadFloat(fileName)); @@ -351,7 +395,7 @@ bool TexImage::setImage2D(Format format, Decoder decoder, int w, int h, int idx, return false; } - if (format != nvtt::Format_BC1 && format != nvtt::Format_BC3) + if (format != nvtt::Format_BC1 && format != nvtt::Format_BC2 && format != nvtt::Format_BC3) { return false; } @@ -366,85 +410,72 @@ bool TexImage::setImage2D(Format format, Decoder decoder, int w, int h, int idx, const int count = w * h; - int bw = (w + 3) / 4; - int bh = (h + 3) / 4; + const int bw = (w + 3) / 4; + const int bh = (h + 3) / 4; - float * restrict rdst = img->channel(0); - float * restrict gdst = img->channel(1); - float * restrict bdst = img->channel(2); - float * restrict adst = img->channel(3); + const uint bs = blockSize(format); + + const uint8 * ptr = (const uint8 *)data; try { - if (format == nvtt::Format_BC1) + for (int y = 0; y < bh; y++) { - BlockDXT1 * block = (BlockDXT1 *)data; - - for (int y = 0; y < bh; y++) + for (int x = 0; x < bw; x++) { - for (int x = 0; x < bw; x++) + ColorBlock colors; + + if (format == nvtt::Format_BC1) { - ColorBlock colors; + const BlockDXT1 * block = (const BlockDXT1 *)ptr; + if (decoder == Decoder_Reference) { block->decodeBlock(&colors); } else if (decoder == Decoder_NV5x) { block->decodeBlockNV5x(&colors); } - - for (int yy = 0; yy < 4; yy++) - { - for (int xx = 0; xx < 4; xx++) - { - Color32 c = colors.color(xx, yy); - - if (x * 4 + xx < w && y * 4 + yy < h) - { - img->setPixel(float(c.r) * 1.0f/255.0f, x*4 + xx, y*4 + yy, 0); - img->setPixel(float(c.g) * 1.0f/255.0f, x*4 + xx, y*4 + yy, 1); - img->setPixel(float(c.b) * 1.0f/255.0f, x*4 + xx, y*4 + yy, 2); - img->setPixel(float(c.a) * 1.0f/255.0f, x*4 + xx, y*4 + yy, 3); - } - } - } - - block++; } - } - } - else if (format == nvtt::Format_BC3) - { - BlockDXT5 * block = (BlockDXT5 *)data; + else if (format == nvtt::Format_BC2) + { + const BlockDXT3 * block = (const BlockDXT3 *)ptr; - for (int y = 0; y < bh; y++) - { - for (int x = 0; x < bw; x++) +#pragma message(NV_FILE_LINE "Add NV5x decoder to DXT3 block") + //if (decoder == Decoder_Reference) { + block->decodeBlock(&colors); + //} + //else if (decoder == Decoder_NV5x) { + // block->decodeBlockNV5x(&colors); + //} + } + else if (format == nvtt::Format_BC3) { - ColorBlock colors; + const BlockDXT5 * block = (const BlockDXT5 *)ptr; + if (decoder == Decoder_Reference) { block->decodeBlock(&colors); } else if (decoder == Decoder_NV5x) { block->decodeBlockNV5x(&colors); } + } - for (int yy = 0; yy < 4; yy++) + for (int yy = 0; yy < 4; yy++) + { + for (int xx = 0; xx < 4; xx++) { - for (int xx = 0; xx < 4; xx++) + Color32 c = colors.color(xx, yy); + + if (x * 4 + xx < w && y * 4 + yy < h) { - Color32 c = colors.color(xx, yy); - - if (x * 4 + xx < w && y * 4 + yy < h) - { - img->setPixel(float(c.r) * 1.0f/255.0f, x*4 + xx, y*4 + yy, 0); - img->setPixel(float(c.g) * 1.0f/255.0f, x*4 + xx, y*4 + yy, 1); - img->setPixel(float(c.b) * 1.0f/255.0f, x*4 + xx, y*4 + yy, 2); - img->setPixel(float(c.a) * 1.0f/255.0f, x*4 + xx, y*4 + yy, 3); - } + img->setPixel(float(c.r) * 1.0f/255.0f, x*4 + xx, y*4 + yy, 0); + img->setPixel(float(c.g) * 1.0f/255.0f, x*4 + xx, y*4 + yy, 1); + img->setPixel(float(c.b) * 1.0f/255.0f, x*4 + xx, y*4 + yy, 2); + img->setPixel(float(c.a) * 1.0f/255.0f, x*4 + xx, y*4 + yy, 3); } } - - block++; } + + ptr += bs; } } } @@ -1014,13 +1045,3 @@ float TexImage::rootMeanSquaredError_alpha(const TexImage & reference) const return float(sqrt(mse / totalCount)); } -float TexImage::structuralSimilarityError_rgb(const TexImage & reference) const -{ - return 0.0f; -} - -float TexImage::structuralSimilarityError_alpha(const TexImage & reference) const -{ - return 0.0f; -} - diff --git a/src/nvtt/TexImage.h b/src/nvtt/TexImage.h index 57092ff..59a805b 100644 --- a/src/nvtt/TexImage.h +++ b/src/nvtt/TexImage.h @@ -21,8 +21,8 @@ // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR // OTHER DEALINGS IN THE SOFTWARE. -#ifndef NV_TT_TEXTURE_H -#define NV_TT_TEXTURE_H +#ifndef NV_TT_TEXIMAGE_H +#define NV_TT_TEXIMAGE_H #include "nvtt.h" @@ -76,4 +76,4 @@ namespace nvtt } // nvtt namespace -#endif // NV_TT_TEXTURE_H +#endif // NV_TT_TEXIMAGE_H diff --git a/src/nvtt/nvtt.h b/src/nvtt/nvtt.h index dd4ff93..902e8f5 100644 --- a/src/nvtt/nvtt.h +++ b/src/nvtt/nvtt.h @@ -349,19 +349,24 @@ namespace nvtt NVTT_API Compressor(); NVTT_API ~Compressor(); + // Context settings. NVTT_API void enableCudaAcceleration(bool enable); NVTT_API bool isCudaAccelerationEnabled() const; - // Main entrypoint of the compression library. + // InputOptions api. NVTT_API bool process(const InputOptions & inputOptions, const CompressionOptions & compressionOptions, const OutputOptions & outputOptions) const; - - // Estimate the size of compressing the input with the given options. NVTT_API int estimateSize(const InputOptions & inputOptions, const CompressionOptions & compressionOptions) const; - // TexImage api - NVTT_API TexImage createTexImage(); - NVTT_API int estimateSize(const TexImage & tex, const CompressionOptions & compressionOptions); - NVTT_API void outputCompressed(const TexImage & tex, const CompressionOptions & compressionOptions, const OutputOptions & outputOptions); + // RAW api. + NVTT_API bool compress2D(InputFormat format, int w, int h, void * data, const CompressionOptions & compressionOptions, const OutputOptions & outputOptions) const; + //ßNVTT_API bool compress3D(InputFormat format, int w, int h, int d, void * data, const CompressionOptions & compressionOptions, const OutputOptions & outputOptions) const; + NVTT_API int estimateSize(int w, int h, int d, const CompressionOptions & compressionOptions) const; + + // TexImage api. + NVTT_API TexImage createTexImage() const; + NVTT_API bool outputHeader(const TexImage & tex, int mipmapCount, const CompressionOptions & compressionOptions, const OutputOptions & outputOptions) const; + NVTT_API bool compress(const TexImage & tex, const CompressionOptions & compressionOptions, const OutputOptions & outputOptions) const; + NVTT_API int estimateSize(const TexImage & tex, const CompressionOptions & compressionOptions) const; }; // "Compressor" is deprecated. This should have been called "Context" @@ -399,6 +404,7 @@ namespace nvtt NVTT_API WrapMode wrapMode() const; NVTT_API AlphaMode alphaMode() const; NVTT_API bool isNormalMap() const; + NVTT_API int countMipmaps() const; // Texture data. NVTT_API bool load(const char * fileName); @@ -429,14 +435,9 @@ namespace nvtt NVTT_API void toHeightMap(); NVTT_API void normalizeNormalMap(); - // Compress. - //NVTT_API void outputCompressed(const CompressionOptions & compressionOptions, const OutputOptions & outputOptions); - // Error compare. NVTT_API float rootMeanSquaredError_rgb(const TexImage & reference) const; NVTT_API float rootMeanSquaredError_alpha(const TexImage & reference) const; - NVTT_API float structuralSimilarityError_rgb(const TexImage & reference) const; - NVTT_API float structuralSimilarityError_alpha(const TexImage & reference) const; private: void detach(); diff --git a/src/nvtt/tests/imperativeapi.cpp b/src/nvtt/tests/imperativeapi.cpp index 9ebc98f..4a50c74 100644 --- a/src/nvtt/tests/imperativeapi.cpp +++ b/src/nvtt/tests/imperativeapi.cpp @@ -39,6 +39,8 @@ int main(int argc, char *argv[]) image.load("kodim01.png"); + context.outputHeader(image, image.countMipmaps(), compressionOptions, outputOptions); + float gamma = 2.2; image.toLinear(gamma); @@ -47,8 +49,8 @@ int main(int argc, char *argv[]) nvtt::TexImage tmpImage = image; tmpImage.toGamma(gamma); - context.outputCompressed(tmpImage, compressionOptions, outputOptions); - // tmpImage.outputCompressed(compressionOptions, outputOptions); + context.compress(tmpImage, compressionOptions, outputOptions); + // tmpImage.compress(compressionOptions, outputOptions); } return EXIT_SUCCESS;