diff --git a/src/nvtt/Compressor.cpp b/src/nvtt/Compressor.cpp index e69de29..036e23e 100644 --- a/src/nvtt/Compressor.cpp +++ b/src/nvtt/Compressor.cpp @@ -0,0 +1,754 @@ +// Copyright NVIDIA Corporation 2008 -- Ignacio Castano +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "Compressor.h" +#include "InputOptions.h" +#include "CompressionOptions.h" +#include "OutputOptions.h" + +#include "CompressDXT.h" +#include "FastCompressDXT.h" +#include "CompressRGB.h" +#include "cuda/CudaUtils.h" +#include "cuda/CudaCompressDXT.h" + + +using namespace nv; +using namespace nvtt; + + +namespace +{ + + static int blockSize(Format format) + { + if (format == Format_DXT1 || format == Format_DXT1a) { + 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; + } + return 0; + } + + inline uint computePitch(uint w, uint bitsize) + { + uint p = w * ((bitsize + 7) / 8); + + // Align to 32 bits. + return ((p + 3) / 4) * 4; + } + + static int computeImageSize(uint w, uint h, uint d, uint bitCount, Format format) + { + if (format == Format_RGBA) { + return d * h * computePitch(w, bitCount); + } + else { + // @@ Handle 3D textures. DXT and VTC have different behaviors. + return ((w + 3) / 4) * ((h + 3) / 4) * blockSize(format); + } + } + +} // namespace + + +Compressor::Compressor() : m(*new Compressor::Private()) +{ + m.cudaSupported = cuda::isHardwarePresent(); + m.cudaEnabled = true; + + // @@ Do CUDA initialization here. + +} + +Compressor::~Compressor() +{ + // @@ Free CUDA resources here. +} + + +/// Enable CUDA acceleration. +void Compressor::enableCudaAceleration(bool enable) +{ + if (m.cudaSupported) + { + m.cudaEnabled = enable; + } +} + +/// Check if CUDA acceleration is enabled. +bool Compressor::isCudaAcelerationEnabled() const +{ + return m.cudaEnabled; +} + +#if 0 + +/// Compress the input texture with the given compression options. +bool Compressor::process(const InputOptions & inputOptions, const CompressionOptions & compressionOptions, const OutputOptions & outputOptions) const +{ + return m.compress(inputOptions.m, outputOptions.m, compressionOptions.m); +} + +#endif // 0 + +/// Estimate the size of compressing the input with the given options. +int Compressor::estimateSize(const InputOptions & inputOptions, const CompressionOptions & compressionOptions) const +{ + return m.estimateSize(inputOptions.m, compressionOptions.m); +} + + + + +bool Compressor::Private::compress(const InputOptions::Private & inputOptions, const CompressionOptions::Private & compressionOptions, const OutputOptions::Private & outputOptions) const +{ + // Make sure enums match. + nvStaticCheck(FloatImage::WrapMode_Clamp == (FloatImage::WrapMode)WrapMode_Clamp); + nvStaticCheck(FloatImage::WrapMode_Mirror == (FloatImage::WrapMode)WrapMode_Mirror); + nvStaticCheck(FloatImage::WrapMode_Repeat == (FloatImage::WrapMode)WrapMode_Repeat); + + // Get output handler. + if (!outputOptions.openFile()) + { + if (outputOptions.errorHandler) outputOptions.errorHandler->error(Error_FileOpen); + return false; + } + + inputOptions.computeTargetExtents(); + + // Output DDS header. + if (!outputHeader(inputOptions, outputOptions, compressionOptions)) + { + return false; + } + + for (uint f = 0; f < inputOptions.faceCount; f++) + { + /*if (!compressMipmaps(f, inputOptions, outputOptions, compressionOptions)) + { + return false; + }*/ + } + + outputOptions.closeFile(); + + return true; +} + + +// Output DDS header. +bool Compressor::Private::outputHeader(const InputOptions::Private & inputOptions, const OutputOptions::Private & outputOptions, const CompressionOptions::Private & compressionOptions) const +{ + // Output DDS header. + if (outputOptions.outputHandler == NULL || !outputOptions.outputHeader) + { + return true; + } + + DDSHeader header; + + header.setWidth(inputOptions.targetWidth); + header.setHeight(inputOptions.targetHeight); + + int mipmapCount = inputOptions.realMipmapCount(); + nvDebugCheck(mipmapCount > 0); + + header.setMipmapCount(mipmapCount); + + if (inputOptions.textureType == TextureType_2D) { + header.setTexture2D(); + } + else if (inputOptions.textureType == TextureType_Cube) { + header.setTextureCube(); + } + /*else if (inputOptions.textureType == TextureType_3D) { + header.setTexture3D(); + header.setDepth(inputOptions.targetDepth); + }*/ + + if (compressionOptions.format == Format_RGBA) + { + header.setPitch(4 * inputOptions.targetWidth); + header.setPixelFormat(compressionOptions.bitcount, compressionOptions.rmask, compressionOptions.gmask, compressionOptions.bmask, compressionOptions.amask); + } + else + { + header.setLinearSize(computeImageSize(inputOptions.targetWidth, inputOptions.targetHeight, inputOptions.targetDepth, compressionOptions.bitcount, compressionOptions.format)); + + if (compressionOptions.format == Format_DXT1 || compressionOptions.format == Format_DXT1a) { + header.setFourCC('D', 'X', 'T', '1'); + if (inputOptions.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 (inputOptions.isNormalMap) header.setNormalFlag(true); + } + 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 (inputOptions.isNormalMap) header.setNormalFlag(true); + } + } + + // 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; +} + +// @@ compressMipmaps... + +#if 0 + +// Convert input image to linear float image. +static FloatImage * toFloatImage(const Image * image, const InputOptions::Private & inputOptions) +{ + nvDebugCheck(image != NULL); + + FloatImage * floatImage = new FloatImage(image); + + if (inputOptions.isNormalMap) + { + // Expand normals. to [-1, 1] range. + // floatImage->expandNormals(0); + } + else if (inputOptions.inputGamma != 1.0f) + { + // Convert to linear space. + floatImage->toLinear(0, 3, inputOptions.inputGamma); + } + + return floatImage; +} + + +// Convert linear float image to output image. +static Image * toFixedImage(const FloatImage * floatImage, const InputOptions::Private & inputOptions) +{ + nvDebugCheck(floatImage != NULL); + + if (inputOptions.isNormalMap || inputOptions.outputGamma == 1.0f) + { + return floatImage->createImage(); + } + else + { + return floatImage->createImageGammaCorrect(inputOptions.outputGamma); + } +} + + +// Create mipmap from the given image. +static FloatImage * createMipmap(const FloatImage * floatImage, const InputOptions::Private & inputOptions) +{ + FloatImage * result = NULL; + + if (inputOptions.mipmapFilter == MipmapFilter_Box) + { + // Use fast downsample. + result = floatImage->fastDownSample(); + } + else if (inputOptions.mipmapFilter == MipmapFilter_Triangle) + { + TriangleFilter filter; + result = floatImage->downSample(filter, (FloatImage::WrapMode)inputOptions.wrapMode); + } + else /*if (inputOptions.mipmapFilter == MipmapFilter_Kaiser)*/ + { + nvDebugCheck(inputOptions.mipmapFilter == MipmapFilter_Kaiser); + KaiserFilter filter(inputOptions.kaiserWidth); + filter.setParameters(inputOptions.kaiserAlpha, inputOptions.kaiserStretch); + result = floatImage->downSample(filter, (FloatImage::WrapMode)inputOptions.wrapMode); + } + + // Normalize mipmap. + if ((inputOptions.isNormalMap || inputOptions.convertToNormalMap) && inputOptions.normalizeMipmaps) + { + normalizeNormalMap(result); + } + + return result; +} + + +// Quantize the input image to the precision of the output format. +static void quantize(Image * img, const CompressionOptions::Private & compressionOptions) +{ + if (compressionOptions.enableColorDithering) + { + if (compressionOptions.format >= Format_DXT1 && compressionOptions.format <= Format_DXT5) + { + Quantize::FloydSteinberg_RGB16(img); + } + } + if (compressionOptions.binaryAlpha) + { + if (compressionOptions.enableAlphaDithering) + { + Quantize::FloydSteinberg_BinaryAlpha(img, compressionOptions.alphaThreshold); + } + else + { + Quantize::BinaryAlpha(img, compressionOptions.alphaThreshold); + } + } + else + { + if (compressionOptions.enableAlphaDithering) + { + if (compressionOptions.format == Format_DXT3) + { + Quantize::Alpha4(img); + } + else if (compressionOptions.format == Format_DXT1a) + { + Quantize::BinaryAlpha(img, compressionOptions.alphaThreshold); + } + } + } +} + +// Process the input, convert to normal map, normalize, or convert to linear space. +static FloatImage * processInput(const InputOptions::Private & inputOptions, int idx) +{ + const InputOptions::Private::Image & mipmap = inputOptions.images[idx]; + + if (inputOptions.convertToNormalMap) + { + // Scale height factor by 1 / 2 ^ m // @@ Compute scale factor exactly... + Vector4 heightScale = inputOptions.heightFactors / float(1 << idx); + return createNormalMap(mipmap.data.ptr(), (FloatImage::WrapMode)inputOptions.wrapMode, heightScale, inputOptions.bumpFrequencyScale); + } + else if (inputOptions.isNormalMap) + { + if (inputOptions.normalizeMipmaps) + { + FloatImage * img = new FloatImage(mipmap.data.ptr()); + img->normalize(0); + return img; + } + } + else + { + if (inputOptions.inputGamma != inputOptions.outputGamma) + { + FloatImage * img = new FloatImage(mipmap.data.ptr()); + img->toLinear(0, 3, inputOptions.inputGamma); + return img; + } + } + + return NULL; +} + + + + +struct ImagePair +{ + ImagePair() : m_floatImage(NULL), m_fixedImage(NULL), m_deleteFixedImage(false) {} + ~ImagePair() + { + if (m_deleteFixedImage) { + delete m_fixedImage; + } + } + + void setFloatImage(FloatImage * image) + { + m_floatImage = image; + if (m_deleteFixedImage) delete m_fixedImage; + m_fixedImage = NULL; + } + + void setFixedImage(Image * image, bool deleteImage) + { + m_floatImage = NULL; + if (m_deleteFixedImage) delete m_fixedImage; + m_fixedImage = image; + m_deleteFixedImage = deleteImage; + } + + FloatImage * floatImage() const { return m_floatImage.ptr(); } + Image * fixedImage() const { return m_fixedImage; } + + void toFixed(const InputOptions::Private & inputOptions) + { + if (m_floatImage != NULL) + { + // Convert to fixed. + m_fixedImage = toFixedImage(m_floatImage.ptr(), inputOptions); + } + } + +private: + AutoPtr m_floatImage; + Image * m_fixedImage; + bool m_deleteFixedImage; +}; + + + + +// Find the first mipmap provided that is greater or equal to the target image size. +static int findMipmap(const InputOptions::Private & inputOptions, uint f, int firstMipmap, uint w, uint h, uint d) +{ + nvDebugCheck(firstMipmap >= 0); + + int bestIdx = -1; + + for (int m = firstMipmap; m < int(inputOptions.mipmapCount); m++) + { + int idx = f * inputOptions.mipmapCount + m; + const InputOptions::Private::Image & mipmap = inputOptions.images[idx]; + + if (mipmap.width >= int(w) && mipmap.height >= int(h) && mipmap.depth >= int(d)) + { + if (mipmap.data != NULL) + { + bestIdx = idx; + } + } + else + { + // Do not look further down. + break; + } + } + + return bestIdx; +} + + + +static int findImage(const InputOptions::Private & inputOptions, uint f, uint w, uint h, uint d, int inputImageIdx, ImagePair * pair) +{ + nvDebugCheck(w > 0 && h > 0); + nvDebugCheck(inputImageIdx >= 0 && inputImageIdx < int(inputOptions.mipmapCount)); + nvDebugCheck(pair != NULL); + + int bestIdx = findMipmap(inputOptions, f, inputImageIdx, w, h, d); + const InputOptions::Private::Image & mipmap = inputOptions.images[bestIdx]; + + if (mipmap.width == w && mipmap.height == h && mipmap.depth == d) + { + // Generate from input image. + AutoPtr processedImage( processInput(inputOptions, bestIdx) ); + + if (processedImage != NULL) + { + pair->setFloatImage(processedImage.release()); + } + else + { + pair->setFixedImage(mipmap.data.ptr(), false); + } + + return bestIdx; + } + else + { + if (pair->floatImage() == NULL && pair->fixedImage() == NULL) + { + // Generate from input image and resize. + AutoPtr processedImage( processInput(inputOptions, bestIdx) ); + + if (processedImage == NULL) + { + processedImage = new FloatImage(mipmap.data.ptr()); + } + + // Resize image. @@ Add more filters. @@ Distinguish between downscaling and reconstruction filters. + BoxFilter boxFilter; + pair->setFloatImage(processedImage->downSample(boxFilter, w, h, (FloatImage::WrapMode)inputOptions.wrapMode)); + } + else + { + // Generate from previous mipmap. + if (pair->floatImage() == NULL) + { + nvDebugCheck(pair->fixedImage() != NULL); + pair->setFloatImage(toFloatImage(pair->fixedImage(), inputOptions)); + } + + // Create mipmap. + pair->setFloatImage(createMipmap(pair->floatImage(), inputOptions)); + } + } + + + return bestIdx; // @@ ??? +} + + +static bool compressMipmaps(uint f, const InputOptions::Private & inputOptions, const OutputOptions::Private & outputOptions, const CompressionOptions::Private & compressionOptions) +{ + uint w = inputOptions.targetWidth; + uint h = inputOptions.targetHeight; + uint d = inputOptions.targetDepth; + + int inputImageIdx = findMipmap(inputOptions, f, 0, w, h, d); + if (inputImageIdx == -1) + { + // First mipmap missing. + if (outputOptions.errorHandler != NULL) outputOptions.errorHandler->error(Error_InvalidInput); + return false; + } + + ImagePair pair; + + const uint mipmapCount = inputOptions.realMipmapCount(); + nvDebugCheck(mipmapCount > 0); + + for (uint m = 0; m < mipmapCount; m++) + { + if (outputOptions.outputHandler) + { + int size = computeImageSize(w, h, compressionOptions.bitcount, compressionOptions.format); + outputOptions.outputHandler->mipmap(size, w, h, d, f, m); + } + + inputImageIdx = findImage(inputOptions, f, w, h, d, inputImageIdx, &pair); + + // @@ Where to do the color transform? + // - Color transform may not be linear, so we cannot do before computing mipmaps. + // - Should be done in linear space, that is, after gamma correction. + + + pair.toFixed(inputOptions); + + // @@ Quantization should be done in compressMipmap! @@ It should not modify the input image!!! + quantize(pair.fixedImage(), compressionOptions); + + compressMipmap(pair.fixedImage(), outputOptions, compressionOptions); + + // Compute extents of next mipmap: + w = max(1U, w / 2); + h = max(1U, h / 2); + d = max(1U, d / 2); + } + + return true; +} + +#endif // 0 + +bool Compressor::Private::compressMipmap(const Image * image, const OutputOptions::Private & outputOptions, const CompressionOptions::Private & compressionOptions) const +{ + nvDebugCheck(image != NULL); + + if (compressionOptions.format == Format_RGBA || compressionOptions.format == Format_RGB) + { + compressRGB(image, outputOptions, compressionOptions); + } + else if (compressionOptions.format == Format_DXT1) + { +#if defined(HAVE_S3QUANT) + if (compressionOptions.externalCompressor == "s3") + { + s3CompressDXT1(image, outputOptions); + } + else +#endif + +#if defined(HAVE_ATITC) + if (compressionOptions.externalCompressor == "ati") + { + atiCompressDXT1(image, outputOptions); + } + else +#endif + if (compressionOptions.quality == Quality_Fastest) + { + fastCompressDXT1(image, outputOptions); + } + else + { + if (cudaEnabled) + { + nvDebugCheck(cudaSupported); + cudaCompressDXT1(image, outputOptions, compressionOptions); + } + else + { + compressDXT1(image, outputOptions, compressionOptions); + } + } + } + else if (compressionOptions.format == Format_DXT1a) + { + if (compressionOptions.quality == Quality_Fastest) + { + fastCompressDXT1a(image, outputOptions); + } + else + { + if (cudaEnabled) + { + nvDebugCheck(cudaSupported); + /*cuda*/compressDXT1a(image, outputOptions, compressionOptions); + } + else + { + compressDXT1a(image, outputOptions, compressionOptions); + } + } + } + else if (compressionOptions.format == Format_DXT3) + { + if (compressionOptions.quality == Quality_Fastest) + { + fastCompressDXT3(image, outputOptions); + } + else + { + if (cudaEnabled) + { + nvDebugCheck(cudaSupported); + cudaCompressDXT3(image, outputOptions, compressionOptions); + } + else + { + compressDXT3(image, outputOptions, compressionOptions); + } + } + } + else if (compressionOptions.format == Format_DXT5) + { + if (compressionOptions.quality == Quality_Fastest) + { + fastCompressDXT5(image, outputOptions); + } + else + { + if (cudaEnabled) + { + nvDebugCheck(cudaSupported); + cudaCompressDXT5(image, outputOptions, compressionOptions); + } + else + { + compressDXT5(image, outputOptions, compressionOptions); + } + } + } + else if (compressionOptions.format == Format_DXT5n) + { + if (compressionOptions.quality == Quality_Fastest) + { + fastCompressDXT5n(image, outputOptions); + } + else + { + compressDXT5n(image, outputOptions, compressionOptions); + } + } + else if (compressionOptions.format == Format_BC4) + { + compressBC4(image, outputOptions, compressionOptions); + } + else if (compressionOptions.format == Format_BC5) + { + compressBC5(image, outputOptions, compressionOptions); + } + + return true; +} + + +int Compressor::Private::estimateSize(const InputOptions::Private & inputOptions, const CompressionOptions::Private & compressionOptions) const +{ + const Format format = compressionOptions.format; + const uint bitCount = compressionOptions.bitcount; + + inputOptions.computeTargetExtents(); + + uint mipmapCount = inputOptions.realMipmapCount(); + + int size = 0; + + for (uint f = 0; f < inputOptions.faceCount; f++) + { + uint w = inputOptions.targetWidth; + uint h = inputOptions.targetHeight; + uint d = inputOptions.targetDepth; + + for (uint m = 0; m < mipmapCount; m++) + { + size += computeImageSize(w, h, d, bitCount, format); + + // Compute extents of next mipmap: + w = max(1U, w / 2); + h = max(1U, h / 2); + d = max(1U, d / 2); + } + } + + return size; +} diff --git a/src/nvtt/Compressor.h b/src/nvtt/Compressor.h index 2e1e0e8..5e6ddc3 100644 --- a/src/nvtt/Compressor.h +++ b/src/nvtt/Compressor.h @@ -26,6 +26,11 @@ #include "nvtt.h" +namespace nv +{ + class Image; +} + namespace nvtt { @@ -33,6 +38,17 @@ namespace nvtt { Private() {} + 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; + + private: + + bool outputHeader(const InputOptions::Private & inputOptions, const OutputOptions::Private & outputOptions, const CompressionOptions::Private & compressionOptions) const; + bool compressMipmap(const nv::Image * image, const OutputOptions::Private & outputOptions, const CompressionOptions::Private & compressionOptions) const; + + + public: + bool cudaSupported; bool cudaEnabled; }; diff --git a/src/nvtt/InputOptions.cpp b/src/nvtt/InputOptions.cpp index c52749f..459ae57 100644 --- a/src/nvtt/InputOptions.cpp +++ b/src/nvtt/InputOptions.cpp @@ -102,7 +102,7 @@ void InputOptions::reset() m.colorTransform = ColorTransform_None; m.linearTransform = Matrix(identity); - m.generateMipmaps = false; + m.generateMipmaps = true; m.maxLevel = -1; m.mipmapFilter = MipmapFilter_Box; diff --git a/src/nvtt/nvtt.cpp b/src/nvtt/nvtt.cpp index 0981248..3c20b44 100644 --- a/src/nvtt/nvtt.cpp +++ b/src/nvtt/nvtt.cpp @@ -139,6 +139,7 @@ static bool outputHeader(const InputOptions::Private & inputOptions, const Outpu if (compressionOptions.format == Format_DXT1 || compressionOptions.format == Format_DXT1a) { header.setFourCC('D', 'X', 'T', '1'); + if (inputOptions.isNormalMap) header.setNormalFlag(true); } else if (compressionOptions.format == Format_DXT3) { header.setFourCC('D', 'X', 'T', '3'); @@ -175,9 +176,6 @@ static bool outputHeader(const InputOptions::Private & inputOptions, const Outpu outputOptions.errorHandler->error(Error_FileWrite); } - // Revert swap. - header.swapBytes(); - return writeSucceed; } @@ -670,32 +668,6 @@ static bool compress(const InputOptions::Private & inputOptions, const OutputOpt } -Compressor::Compressor() : m(*new Compressor::Private()) -{ - m.cudaSupported = cuda::isHardwarePresent(); - m.cudaEnabled = true; - - // @@ Do CUDA initialization here. - -} - -Compressor::~Compressor() -{ - // @@ Free CUDA resources here. -} - -void Compressor::enableCudaAceleration(bool enable) -{ - if (m.cudaSupported) - { - m.cudaEnabled = enable; - } -} - -bool Compressor::isCudaAcelerationEnabled() const -{ - return m.cudaEnabled; -} /// Compress the input texture with the given compression options. @@ -705,38 +677,6 @@ bool Compressor::process(const InputOptions & inputOptions, const CompressionOpt } -/// Estimate the size of compressing the input with the given options. -int Compressor::estimateSize(const InputOptions & inputOptions, const CompressionOptions & compressionOptions) const -{ - const Format format = compressionOptions.m.format; - const uint bitCount = compressionOptions.m.bitcount; - - inputOptions.m.computeTargetExtents(); - - uint mipmapCount = inputOptions.m.realMipmapCount(); - - int size = 0; - - for (uint f = 0; f < inputOptions.m.faceCount; f++) - { - uint w = inputOptions.m.targetWidth; - uint h = inputOptions.m.targetHeight; - uint d = inputOptions.m.targetDepth; - - for (uint m = 0; m < mipmapCount; m++) - { - size += computeImageSize(w, h, bitCount, format); - - // Compute extents of next mipmap: - w = max(1U, w / 2); - h = max(1U, h / 2); - d = max(1U, d / 2); - } - } - - return size; -} - /// Return a string for the given error. const char * nvtt::errorString(Error e)