Some progress with full DXT1a support.
Move quantization settings to compression options.
This commit is contained in:
parent
f3a73e3de5
commit
2903886498
@ -225,6 +225,37 @@ void nv::compressDXT1(const Image * image, const OutputOptions::Private & output
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void nv::compressDXT1a(const Image * image, const OutputOptions::Private & outputOptions, const CompressionOptions::Private & compressionOptions)
|
||||||
|
{
|
||||||
|
const uint w = image->width();
|
||||||
|
const uint h = image->height();
|
||||||
|
|
||||||
|
ColorBlock rgba;
|
||||||
|
BlockDXT1 block;
|
||||||
|
|
||||||
|
doPrecomputation();
|
||||||
|
|
||||||
|
for (uint y = 0; y < h; y += 4) {
|
||||||
|
for (uint x = 0; x < w; x += 4) {
|
||||||
|
|
||||||
|
rgba.init(image, x, y);
|
||||||
|
|
||||||
|
// Compress color.
|
||||||
|
squish::WeightedClusterFit fit(&colours, squish::kDxt1);
|
||||||
|
squish::ClusterFit fit(&colours, squish::kDxt1);
|
||||||
|
fit.setMetric(compressionOptions.colorWeight.x(), compressionOptions.colorWeight.y(), compressionOptions.colorWeight.z());
|
||||||
|
fit.Compress(&block);
|
||||||
|
|
||||||
|
// @@ Use iterative cluster fit algorithm to improve error in highest quality mode.
|
||||||
|
|
||||||
|
if (outputOptions.outputHandler != NULL) {
|
||||||
|
outputOptions.outputHandler->writeData(&block, sizeof(block));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void nv::compressDXT3(const Image * image, const OutputOptions::Private & outputOptions, const CompressionOptions::Private & compressionOptions)
|
void nv::compressDXT3(const Image * image, const OutputOptions::Private & outputOptions, const CompressionOptions::Private & compressionOptions)
|
||||||
{
|
{
|
||||||
const uint w = image->width();
|
const uint w = image->width();
|
||||||
|
@ -45,6 +45,7 @@ namespace nv
|
|||||||
|
|
||||||
// Normal compressors.
|
// Normal compressors.
|
||||||
void compressDXT1(const Image * image, const nvtt::OutputOptions::Private & outputOptions, const nvtt::CompressionOptions::Private & compressionOptions);
|
void compressDXT1(const Image * image, const nvtt::OutputOptions::Private & outputOptions, const nvtt::CompressionOptions::Private & compressionOptions);
|
||||||
|
void compressDXT1a(const Image * image, const nvtt::OutputOptions::Private & outputOptions, const nvtt::CompressionOptions::Private & compressionOptions);
|
||||||
void compressDXT3(const Image * image, const nvtt::OutputOptions::Private & outputOptions, const nvtt::CompressionOptions::Private & compressionOptions);
|
void compressDXT3(const Image * image, const nvtt::OutputOptions::Private & outputOptions, const nvtt::CompressionOptions::Private & compressionOptions);
|
||||||
void compressDXT5(const Image * image, const nvtt::OutputOptions::Private & outputOptions, const nvtt::CompressionOptions::Private & compressionOptions);
|
void compressDXT5(const Image * image, const nvtt::OutputOptions::Private & outputOptions, const nvtt::CompressionOptions::Private & compressionOptions);
|
||||||
void compressDXT5n(const Image * image, const nvtt::OutputOptions::Private & outputOptions, const nvtt::CompressionOptions::Private & compressionOptions);
|
void compressDXT5n(const Image * image, const nvtt::OutputOptions::Private & outputOptions, const nvtt::CompressionOptions::Private & compressionOptions);
|
||||||
|
@ -49,11 +49,17 @@ void CompressionOptions::reset()
|
|||||||
m.quality = Quality_Normal;
|
m.quality = Quality_Normal;
|
||||||
m.colorWeight.set(1.0f, 1.0f, 1.0f);
|
m.colorWeight.set(1.0f, 1.0f, 1.0f);
|
||||||
m.useCuda = true;
|
m.useCuda = true;
|
||||||
|
|
||||||
m.bitcount = 32;
|
m.bitcount = 32;
|
||||||
m.bmask = 0x000000FF;
|
m.bmask = 0x000000FF;
|
||||||
m.gmask = 0x0000FF00;
|
m.gmask = 0x0000FF00;
|
||||||
m.rmask = 0x00FF0000;
|
m.rmask = 0x00FF0000;
|
||||||
m.amask = 0xFF000000;
|
m.amask = 0xFF000000;
|
||||||
|
|
||||||
|
m.enableColorDithering = false;
|
||||||
|
m.enableAlphaDithering = false;
|
||||||
|
m.binaryAlpha = false;
|
||||||
|
m.alphaThreshold = 127;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -134,3 +140,19 @@ void CompressionOptions::setExternalCompressor(const char * name)
|
|||||||
m.externalCompressor = name;
|
m.externalCompressor = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set quantization options.
|
||||||
|
/// @warning Do not enable dithering unless you know what you are doing. Quantization
|
||||||
|
/// introduces errors. It's better to let the compressor quantize the result to
|
||||||
|
/// minimize the error, instead of quantizing the data before handling it to
|
||||||
|
/// the compressor.
|
||||||
|
void CompressionOptions::setQuantization(bool colorDithering, bool alphaDithering, bool binaryAlpha, int alphaThreshold/*= 127*/)
|
||||||
|
{
|
||||||
|
nvCheck(alphaThreshold >= 0 && alphaThreshold < 256);
|
||||||
|
m.enableColorDithering = colorDithering;
|
||||||
|
m.enableAlphaDithering = alphaDithering;
|
||||||
|
m.binaryAlpha = binaryAlpha;
|
||||||
|
m.alphaThreshold = alphaThreshold;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -36,10 +36,11 @@ namespace nvtt
|
|||||||
Format format;
|
Format format;
|
||||||
|
|
||||||
Quality quality;
|
Quality quality;
|
||||||
float errorThreshold;
|
float errorThreshold; // deprecated.
|
||||||
|
|
||||||
nv::Vector3 colorWeight;
|
nv::Vector3 colorWeight;
|
||||||
|
|
||||||
|
// Pixel format description.
|
||||||
uint bitcount;
|
uint bitcount;
|
||||||
uint rmask;
|
uint rmask;
|
||||||
uint gmask;
|
uint gmask;
|
||||||
@ -49,6 +50,12 @@ namespace nvtt
|
|||||||
bool useCuda;
|
bool useCuda;
|
||||||
|
|
||||||
nv::String externalCompressor;
|
nv::String externalCompressor;
|
||||||
|
|
||||||
|
// Quantization.
|
||||||
|
bool enableColorDithering;
|
||||||
|
bool enableAlphaDithering;
|
||||||
|
bool binaryAlpha;
|
||||||
|
int alphaThreshold; // reference value used for binary alpha quantization.
|
||||||
};
|
};
|
||||||
|
|
||||||
} // nvtt namespace
|
} // nvtt namespace
|
||||||
|
@ -94,11 +94,6 @@ void InputOptions::reset()
|
|||||||
m.textureType = TextureType_2D;
|
m.textureType = TextureType_2D;
|
||||||
m.inputFormat = InputFormat_BGRA_8UB;
|
m.inputFormat = InputFormat_BGRA_8UB;
|
||||||
|
|
||||||
m.enableColorDithering = false;
|
|
||||||
m.enableAlphaDithering = false;
|
|
||||||
m.binaryAlpha = false;
|
|
||||||
m.alphaThreshold = 127;
|
|
||||||
|
|
||||||
m.alphaMode = AlphaMode_Transparency;
|
m.alphaMode = AlphaMode_Transparency;
|
||||||
|
|
||||||
m.inputGamma = 2.2f;
|
m.inputGamma = 2.2f;
|
||||||
@ -262,21 +257,6 @@ void InputOptions::setKaiserParameters(float width, float alpha, float stretch)
|
|||||||
m.kaiserStretch = stretch;
|
m.kaiserStretch = stretch;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set quantization options.
|
|
||||||
/// @warning Do not enable dithering unless you know what you are doing. Quantization
|
|
||||||
/// introduces errors. It's better to let the compressor quantize the result to
|
|
||||||
/// minimize the error, instead of quantizing the data before handling it to
|
|
||||||
/// the compressor.
|
|
||||||
void InputOptions::setQuantization(bool colorDithering, bool alphaDithering, bool binaryAlpha, int alphaThreshold/*= 127*/)
|
|
||||||
{
|
|
||||||
nvCheck(alphaThreshold >= 0 && alphaThreshold < 256);
|
|
||||||
m.enableColorDithering = colorDithering;
|
|
||||||
m.enableAlphaDithering = alphaDithering;
|
|
||||||
m.binaryAlpha = binaryAlpha;
|
|
||||||
m.alphaThreshold = alphaThreshold;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// Indicate whether input is a normal map or not.
|
/// Indicate whether input is a normal map or not.
|
||||||
void InputOptions::setNormalMap(bool b)
|
void InputOptions::setNormalMap(bool b)
|
||||||
{
|
{
|
||||||
|
@ -49,12 +49,6 @@ namespace nvtt
|
|||||||
struct Image;
|
struct Image;
|
||||||
Image * images;
|
Image * images;
|
||||||
|
|
||||||
// Quantization.
|
|
||||||
bool enableColorDithering;
|
|
||||||
bool enableAlphaDithering;
|
|
||||||
bool binaryAlpha;
|
|
||||||
int alphaThreshold; // reference value used for binary alpha quantization.
|
|
||||||
|
|
||||||
// Gamma conversion.
|
// Gamma conversion.
|
||||||
float inputGamma;
|
float inputGamma;
|
||||||
float outputGamma;
|
float outputGamma;
|
||||||
|
@ -216,8 +216,21 @@ static bool compressMipmap(const Image * image, const OutputOptions::Private & o
|
|||||||
}
|
}
|
||||||
else if (compressionOptions.format == Format_DXT1a)
|
else if (compressionOptions.format == Format_DXT1a)
|
||||||
{
|
{
|
||||||
// @@ Only fast compression mode for now.
|
if (compressionOptions.quality == Quality_Fastest)
|
||||||
fastCompressDXT1a(image, outputOptions);
|
{
|
||||||
|
fastCompressDXT1a(image, outputOptions);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (compressionOptions.useCuda && nv::cuda::isHardwarePresent())
|
||||||
|
{
|
||||||
|
/*cuda*/compressDXT1a(image, outputOptions);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
compressDXT1a(image, outputOptions);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (compressionOptions.format == Format_DXT3)
|
else if (compressionOptions.format == Format_DXT3)
|
||||||
{
|
{
|
||||||
@ -351,37 +364,37 @@ static FloatImage * createMipmap(const FloatImage * floatImage, const InputOptio
|
|||||||
|
|
||||||
|
|
||||||
// Quantize the input image to the precision of the output format.
|
// Quantize the input image to the precision of the output format.
|
||||||
static void quantize(Image * img, const InputOptions::Private & inputOptions, Format format)
|
static void quantize(Image * img, const CompressionOptions::Private & compressionOptions)
|
||||||
{
|
{
|
||||||
if (inputOptions.enableColorDithering)
|
if (compressionOptions.enableColorDithering)
|
||||||
{
|
{
|
||||||
if (format >= Format_DXT1 && format <= Format_DXT5)
|
if (compressionOptions.format >= Format_DXT1 && compressionOptions.format <= Format_DXT5)
|
||||||
{
|
{
|
||||||
Quantize::FloydSteinberg_RGB16(img);
|
Quantize::FloydSteinberg_RGB16(img);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (inputOptions.binaryAlpha)
|
if (compressionOptions.binaryAlpha)
|
||||||
{
|
{
|
||||||
if (inputOptions.enableAlphaDithering)
|
if (compressionOptions.enableAlphaDithering)
|
||||||
{
|
{
|
||||||
Quantize::FloydSteinberg_BinaryAlpha(img, inputOptions.alphaThreshold);
|
Quantize::FloydSteinberg_BinaryAlpha(img, compressionOptions.alphaThreshold);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Quantize::BinaryAlpha(img, inputOptions.alphaThreshold);
|
Quantize::BinaryAlpha(img, compressionOptions.alphaThreshold);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (inputOptions.enableAlphaDithering)
|
if (compressionOptions.enableAlphaDithering)
|
||||||
{
|
{
|
||||||
if (format == Format_DXT3)
|
if (compressionOptions.format == Format_DXT3)
|
||||||
{
|
{
|
||||||
Quantize::Alpha4(img);
|
Quantize::Alpha4(img);
|
||||||
}
|
}
|
||||||
else if (format == Format_DXT1a)
|
else if (compressionOptions.format == Format_DXT1a)
|
||||||
{
|
{
|
||||||
Quantize::BinaryAlpha(img, inputOptions.alphaThreshold);
|
Quantize::BinaryAlpha(img, compressionOptions.alphaThreshold);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -575,8 +588,11 @@ static bool compressMipmaps(uint f, const InputOptions::Private & inputOptions,
|
|||||||
}
|
}
|
||||||
|
|
||||||
ImagePair pair;
|
ImagePair pair;
|
||||||
|
|
||||||
for (uint m = 0; m < inputOptions.mipmapCount; m++)
|
const uint mipmapCount = inputOptions.realMipmapCount();
|
||||||
|
nvDebugCheck(mipmapCount > 0);
|
||||||
|
|
||||||
|
for (uint m = 0; m < mipmapCount; m++)
|
||||||
{
|
{
|
||||||
if (outputOptions.outputHandler)
|
if (outputOptions.outputHandler)
|
||||||
{
|
{
|
||||||
@ -594,7 +610,7 @@ static bool compressMipmaps(uint f, const InputOptions::Private & inputOptions,
|
|||||||
pair.toFixed(inputOptions);
|
pair.toFixed(inputOptions);
|
||||||
|
|
||||||
// @@ Quantization should be done in compressMipmap! @@ It should not modify the input image!!!
|
// @@ Quantization should be done in compressMipmap! @@ It should not modify the input image!!!
|
||||||
quantize(pair.fixedImage(), inputOptions, compressionOptions.format);
|
quantize(pair.fixedImage(), compressionOptions);
|
||||||
|
|
||||||
compressMipmap(pair.fixedImage(), outputOptions, compressionOptions);
|
compressMipmap(pair.fixedImage(), outputOptions, compressionOptions);
|
||||||
|
|
||||||
@ -609,10 +625,6 @@ static bool compressMipmaps(uint f, const InputOptions::Private & inputOptions,
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static bool compress(const InputOptions::Private & inputOptions, const OutputOptions::Private & outputOptions, const CompressionOptions::Private & compressionOptions)
|
static bool compress(const InputOptions::Private & inputOptions, const OutputOptions::Private & outputOptions, const CompressionOptions::Private & compressionOptions)
|
||||||
{
|
{
|
||||||
// Make sure enums match.
|
// Make sure enums match.
|
||||||
@ -629,9 +641,6 @@ static bool compress(const InputOptions::Private & inputOptions, const OutputOpt
|
|||||||
|
|
||||||
inputOptions.computeTargetExtents();
|
inputOptions.computeTargetExtents();
|
||||||
|
|
||||||
uint mipmapCount = inputOptions.realMipmapCount();
|
|
||||||
nvDebugCheck(mipmapCount > 0);
|
|
||||||
|
|
||||||
// Output DDS header.
|
// Output DDS header.
|
||||||
outputHeader(inputOptions, outputOptions, compressionOptions);
|
outputHeader(inputOptions, outputOptions, compressionOptions);
|
||||||
|
|
||||||
@ -641,105 +650,6 @@ static bool compress(const InputOptions::Private & inputOptions, const OutputOpt
|
|||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
Image * lastImage = NULL;
|
|
||||||
AutoPtr<FloatImage> floatImage(NULL);
|
|
||||||
|
|
||||||
uint w = inputOptions.targetWidth;
|
|
||||||
uint h = inputOptions.targetHeight;
|
|
||||||
uint d = inputOptions.targetDepth;
|
|
||||||
|
|
||||||
for (uint m = 0; m < mipmapCount; m++)
|
|
||||||
{
|
|
||||||
if (outputOptions.outputHandler)
|
|
||||||
{
|
|
||||||
int size = computeImageSize(w, h, bitCount, format);
|
|
||||||
outputOptions.outputHandler->mipmap(size, w, h, d, f, m);
|
|
||||||
}
|
|
||||||
|
|
||||||
// @@ Write a more sofisticated get input image, that:
|
|
||||||
// - looks for the nearest image in the input mipmap chain, resizes it to desired extents.
|
|
||||||
// - uses previous floating point image, if available.
|
|
||||||
// - uses previous byte image if available.
|
|
||||||
|
|
||||||
|
|
||||||
int idx = f * inputOptions.mipmapCount + m;
|
|
||||||
InputOptions::Private::Image & mipmap = inputOptions.images[idx];
|
|
||||||
|
|
||||||
// @@ Prescale not implemented yet.
|
|
||||||
nvCheck(w == mipmap.width);
|
|
||||||
nvCheck(h == mipmap.height);
|
|
||||||
nvCheck(d == mipmap.depth);
|
|
||||||
|
|
||||||
Image * img = NULL; // Image to compress.
|
|
||||||
|
|
||||||
if (mipmap.data != NULL) // Mipmap provided.
|
|
||||||
{
|
|
||||||
// Convert to normal map.
|
|
||||||
if (inputOptions.convertToNormalMap)
|
|
||||||
{
|
|
||||||
// Scale height factor by 1 / 2 ^ m
|
|
||||||
Vector4 heightScale = inputOptions.heightFactors / float(1 << m);
|
|
||||||
floatImage = createNormalMap(mipmap.data.ptr(), (FloatImage::WrapMode)inputOptions.wrapMode, heightScale, inputOptions.bumpFrequencyScale);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
lastImage = img = mipmap.data.ptr();
|
|
||||||
|
|
||||||
// Delete float image.
|
|
||||||
floatImage = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else // Create mipmap from last.
|
|
||||||
{
|
|
||||||
if (m == 0) {
|
|
||||||
// First mipmap missing.
|
|
||||||
if (outputOptions.errorHandler != NULL) outputOptions.errorHandler->error(Error_InvalidInput);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (floatImage == NULL)
|
|
||||||
{
|
|
||||||
nvDebugCheck(lastImage != NULL);
|
|
||||||
floatImage = toFloatImage(lastImage, inputOptions);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create mipmap.
|
|
||||||
floatImage = createMipmap(floatImage.ptr(), inputOptions);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (floatImage != NULL)
|
|
||||||
{
|
|
||||||
// Convert to fixed.
|
|
||||||
img = toFixedImage(floatImage.ptr(), inputOptions);
|
|
||||||
}
|
|
||||||
|
|
||||||
// @@ 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.
|
|
||||||
|
|
||||||
// @@ Error! gamma correction is not performed when mipmap data provided. (only if inputGamma != outputGamma)
|
|
||||||
|
|
||||||
// @@ This code is too complicated, too prone to erros, and hard to understand. Must be simplified!
|
|
||||||
|
|
||||||
|
|
||||||
// @@ Quantization should be done in compressMipmap!
|
|
||||||
quantize(img, inputOptions, format);
|
|
||||||
|
|
||||||
compressMipmap(img, outputOptions, compressionOptions);
|
|
||||||
|
|
||||||
if (img != mipmap.data)
|
|
||||||
{
|
|
||||||
delete img;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compute extents of next mipmap:
|
|
||||||
w = max(1U, w / 2);
|
|
||||||
h = max(1U, h / 2);
|
|
||||||
d = max(1U, d / 2);
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
outputOptions.closeFile();
|
outputOptions.closeFile();
|
||||||
|
@ -100,6 +100,8 @@ namespace nvtt
|
|||||||
// Set color mask to describe the RGB/RGBA format.
|
// Set color mask to describe the RGB/RGBA format.
|
||||||
NVTT_API void setPixelFormat(unsigned int bitcount, unsigned int rmask, unsigned int gmask, unsigned int bmask, unsigned int amask);
|
NVTT_API void setPixelFormat(unsigned int bitcount, unsigned int rmask, unsigned int gmask, unsigned int bmask, unsigned int amask);
|
||||||
|
|
||||||
|
NVTT_API void setQuantization(bool colorDithering, bool alphaDithering, bool binaryAlpha, int alphaThreshold/*= 127*/);
|
||||||
|
|
||||||
//private:
|
//private:
|
||||||
struct Private;
|
struct Private;
|
||||||
Private & m;
|
Private & m;
|
||||||
|
Loading…
Reference in New Issue
Block a user