Initial support for the ktx container. Currently, it only supports BC formats and has no support for key-value pairs. Updated the compress utility with a ktx option and exposed sRGB setting.

pull/253/head
Manvydas Šliamka 8 years ago
parent 8659d3fb4a
commit 8ac7fdb3f2

@ -10,7 +10,7 @@ SET(IMAGE_SRCS
FloatImage.h FloatImage.cpp
Image.h Image.cpp
ImageIO.h ImageIO.cpp
#KtxFile.h KtxFile.cpp
KtxFile.h KtxFile.cpp
NormalMap.h NormalMap.cpp
PixelFormat.h
PsdFile.h

@ -1,6 +1,7 @@
// This code is in the public domain -- Ignacio Castaño <castano@gmail.com>
#include "KtxFile.h"
#include "nvcore/StdStream.h"
using namespace nv;
@ -10,7 +11,8 @@ static const uint8 fileIdentifier[12] = {
0x0D, 0x0A, 0x1A, 0x0A
};
namespace nv
{
KtxHeader::KtxHeader() {
memcpy(identifier, fileIdentifier, 12);
@ -19,8 +21,8 @@ KtxHeader::KtxHeader() {
glType = 0;
glTypeSize = 1;
glFormat = 0;
glInternalFormat = KTX_RGBA;
glBaseInternalFormat = KTX_RGBA;
glInternalFormat = KTX_INTERNAL_COMPRESSED_SRGB_S3TC_DXT1;
glBaseInternalFormat = KTX_BASE_INTERNAL_RGB;
pixelWidth = 0;
pixelHeight = 0;
pixelDepth = 0;
@ -31,9 +33,9 @@ KtxHeader::KtxHeader() {
}
Stream & operator<< (Stream & s, DDSHeader & header) {
Stream & operator<< (Stream & s, KtxHeader & header) {
s.serialize(header.identifier, 12);
s << header.endiannes << header.glType << header.glTypeSize << header.glFormat << header.glInternalFormat << header.glBaseInternalFormat;
s << header.endianness << header.glType << header.glTypeSize << header.glFormat << header.glInternalFormat << header.glBaseInternalFormat;
s << header.pixelWidth << header.pixelHeight << header.pixelDepth;
s << header.numberOfArrayElements << header.numberOfFaces << header.numberOfMipmapLevels;
s << header.bytesOfKeyValueData;
@ -41,7 +43,7 @@ Stream & operator<< (Stream & s, DDSHeader & header) {
}
KtxFile::KtxFile() {
/*KtxFile::KtxFile() {
}
KtxFile::~KtxFile() {
}
@ -49,7 +51,7 @@ KtxFile::~KtxFile() {
void KtxFile::addKeyValue(const char * key, const char * value) {
keyArray.append(key);
valueArray.append(value);
bytesOfKeyValueData += strlen(key) + 1 + strlen(value) + 1;
header.bytesOfKeyValueData += strlen(key) + 1 + strlen(value) + 1;
}
@ -77,7 +79,7 @@ Stream & operator<< (Stream & s, KtxFile & file) {
}
return s;
}*/
}

@ -6,6 +6,7 @@
#include "nvimage.h"
#include "nvcore/StrLib.h"
#include "nvcore/Array.h"
// KTX File format specification:
// http://www.khronos.org/opengles/sdk/tools/KTX/file_format_spec/#key
@ -14,22 +15,78 @@ namespace nv
{
class Stream;
// GL types (Table 3.2)
const uint KTX_UNSIGNED_BYTE;
const uint KTX_UNSIGNED_SHORT_5_6_5;
// ...
// GL formats (Table 3.3)
// ...
// GL internal formats (Table 3.12, 3.13)
// ...
// GL base internal format. (Table 3.11)
const uint KTX_RGB;
const uint KTX_RGBA;
const uint KTX_ALPHA;
// ...
// GL types
const uint KTX_UNSIGNED_BYTE = 0x1401;
const uint KTX_BYTE = 0x1400;
const uint KTX_UNSIGNED_SHORT = 0x1403;
const uint KTX_SHORT = 0x1402;
const uint KTX_UNSIGNED_INT = 0x1405;
const uint KTX_INT = 0x1404;
const uint KTX_FLOAT = 0x1406;
const uint KTX_UNSIGNED_BYTE_3_3_2 = 0x8032;
const uint KTX_UNSIGNED_BYTE_2_3_3_REV = 0x8362;
const uint KTX_UNSIGNED_SHORT_5_6_5 = 0x8363;
const uint KTX_UNSIGNED_SHORT_5_6_5_REV = 0x8364;
const uint KTX_UNSIGNED_SHORT_4_4_4_4 = 0x8033;
const uint KTX_UNSIGNED_SHORT_4_4_4_4_REV = 0x8365;
const uint KTX_UNSIGNED_SHORT_5_5_5_1 = 0x8034;
const uint KTX_UNSIGNED_SHORT_1_5_5_5_REV = 0x8366;
const uint KTX_UNSIGNED_INT_8_8_8_8 = 0x8035;
const uint KTX_UNSIGNED_INT_8_8_8_8_REV = 0x8367;
const uint KTX_UNSIGNED_INT_10_10_10_2 = 0x8036;
const uint KTX_UNSIGNED_INT_2_10_10_10_REV = 0x8368;
// GL formats
const uint KTX_FORMAT_RED = 0x1903;
const uint KTX_FORMAT_RG = 0x8227;
const uint KTX_FORMAT_RGB = 0x1907;
const uint KTX_FORMAT_BGR = 0x80E0;
const uint KTX_FORMAT_RGBA = 0x1908;
const uint KTX_FORMAT_BGRA = 0x80E1;
const uint KTX_FORMAT_RED_INTEGER = 0x8D94;
const uint KTX_FORMAT_RG_INTEGER = 0x8228;
const uint KTX_FORMAT_RGB_INTEGER = 0x8D98;
const uint KTX_FORMAT_BGR_INTEGER = 0x8D9A;
const uint KTX_FORMAT_RGBA_INTEGER = 0x8D99;
const uint KTX_FORMAT_BGRA_INTEGER = 0x8D9B;
const uint KTX_FORMAT_STENCIL_INDEX = 0x1901;
const uint KTX_FORMAT_DEPTH_COMPONENT = 0x1902;
const uint KTX_FORMAT_DEPTH_STENCIL = 0x84F9;
// GL internal formats
// BC1
const uint KTX_INTERNAL_COMPRESSED_RGB_S3TC_DXT1 = 0x83F0;
const uint KTX_INTERNAL_COMPRESSED_SRGB_S3TC_DXT1 = 0x8C4C;
// BC1a
const uint KTX_INTERNAL_COMPRESSED_RGBA_S3TC_DXT1 = 0x83F1;
const uint KTX_INTERNAL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1 = 0x8C4D;
// BC2
const uint KTX_INTERNAL_COMPRESSED_RGBA_S3TC_DXT3 = 0x83F2;
const uint KTX_INTERNAL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3 = 0x8C4E;
// BC3
const uint KTX_INTERNAL_COMPRESSED_RGBA_S3TC_DXT5 = 0x83F3;
const uint KTX_INTERNAL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5 = 0x8C4F;
// BC4
const uint KTX_INTERNAL_COMPRESSED_RED_RGTC1 = 0x8DBB;
const uint KTX_INTERNAL_COMPRESSED_SIGNED_RED_RGTC1 = 0x8DBC;
// BC5
const uint KTX_INTERNAL_COMPRESSED_RG_RGTC2 = 0x8DBD;
const uint KTX_INTERNAL_COMPRESSED_SIGNED_RG_RGTC2 = 0x8DBE;
// BC6
const uint KTX_INTERNAL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT = 0x8E8F;
const uint KTX_INTERNAL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT = 0x8E8E;
// BC7
const uint KTX_INTERNAL_COMPRESSED_RGBA_BPTC_UNORM = 0x8E8C;
const uint KTX_INTERNAL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM = 0x8E8D;
// GL base internal formats
const uint KTX_BASE_INTERNAL_DEPTH_COMPONENT = 0x1902;
const uint KTX_BASE_INTERNAL_DEPTH_STENCIL = 0x84F9;
const uint KTX_BASE_INTERNAL_RED = 0x1903;
const uint KTX_BASE_INTERNAL_RG = 0x8227;
const uint KTX_BASE_INTERNAL_RGB = 0x1907;
const uint KTX_BASE_INTERNAL_RGBA = 0x1908;
const uint KTX_BASE_INTERNAL_STENCIL_INDEX = 0x1901;
struct KtxHeader {
@ -52,10 +109,10 @@ namespace nv
};
NVIMAGE_API Stream & operator<< (Stream & s, DDSHeader & header);
NVIMAGE_API Stream & operator<< (Stream & s, KtxHeader & header);
struct KtxFile {
/* struct KtxFile {
KtxFile();
~KtxFile();
@ -66,10 +123,9 @@ namespace nv
Array<String> keyArray;
Array<String> valueArray;
};
NVIMAGE_API Stream & operator<< (Stream & s, KtxFile & file);
NVIMAGE_API Stream & operator<< (Stream & s, KtxFile & file);*/
/*

@ -39,6 +39,7 @@
#include "cuda/CudaCompressorDXT.h"
#include "nvimage/DirectDrawSurface.h"
#include "nvimage/KtxFile.h"
#include "nvimage/ColorBlock.h"
#include "nvimage/BlockDXT.h"
#include "nvimage/Image.h"
@ -51,6 +52,7 @@
#include "nvcore/Memory.h"
#include "nvcore/Ptr.h"
#include "nvcore/Array.inl"
using namespace nv;
using namespace nvtt;
@ -222,11 +224,6 @@ bool Compressor::Private::compress(const InputOptions::Private & inputOptions, c
return false;
}
nvtt::Surface img;
img.setWrapMode(inputOptions.wrapMode);
img.setAlphaMode(inputOptions.alphaMode);
img.setNormalMap(inputOptions.isNormalMap);
const int faceCount = inputOptions.faceCount;
int width = inputOptions.width;
int height = inputOptions.height;
@ -244,97 +241,246 @@ bool Compressor::Private::compress(const InputOptions::Private & inputOptions, c
if (inputOptions.maxLevel > 0) mipmapCount = min(mipmapCount, inputOptions.maxLevel);
}
if (!outputHeader(inputOptions.textureType, width, height, depth, arraySize, mipmapCount, img.isNormalMap(), compressionOptions, outputOptions)) {
if (!outputHeader(inputOptions.textureType, width, height, depth, arraySize, mipmapCount, inputOptions.isNormalMap, compressionOptions, outputOptions)) {
return false;
}
// Output images.
for (int f = 0; f < faceCount; f++)
if (outputOptions.container != Container_KTX)
{
nvtt::Surface img;
img.setWrapMode(inputOptions.wrapMode);
img.setAlphaMode(inputOptions.alphaMode);
img.setNormalMap(inputOptions.isNormalMap);
for (int f = 0; f < faceCount; f++)
{
int w = width;
int h = height;
int d = depth;
bool canUseSourceImagesForThisFace = canUseSourceImages;
img.setImage(inputOptions.inputFormat, inputOptions.width, inputOptions.height, inputOptions.depth, inputOptions.images[f]);
// To normal map.
if (inputOptions.convertToNormalMap) {
img.toGreyScale(inputOptions.heightFactors.x, inputOptions.heightFactors.y, inputOptions.heightFactors.z, inputOptions.heightFactors.w);
img.toNormalMap(inputOptions.bumpFrequencyScale.x, inputOptions.bumpFrequencyScale.y, inputOptions.bumpFrequencyScale.z, inputOptions.bumpFrequencyScale.w);
}
// To linear space.
if (!img.isNormalMap()) {
img.toLinear(inputOptions.inputGamma);
}
// Resize input.
img.resize(w, h, d, ResizeFilter_Box);
nvtt::Surface tmp = img;
if (!img.isNormalMap()) {
tmp.toGamma(inputOptions.outputGamma);
}
quantize(tmp, compressionOptions);
compress(tmp, f, 0, compressionOptions, outputOptions);
for (int m = 1; m < mipmapCount; m++) {
w = max(1, w/2);
h = max(1, h/2);
d = max(1, d/2);
int idx = m * faceCount + f;
bool useSourceImages = false;
if (canUseSourceImagesForThisFace) {
if (inputOptions.images[idx] == NULL) { // One face is missing in this mipmap level.
canUseSourceImagesForThisFace = false; // If one level is missing, ignore the following source images.
}
else {
useSourceImages = true;
}
}
if (useSourceImages) {
img.setImage(inputOptions.inputFormat, w, h, d, inputOptions.images[idx]);
// For already generated mipmaps, we need to convert to linear.
if (!img.isNormalMap()) {
img.toLinear(inputOptions.inputGamma);
}
}
else {
if (inputOptions.mipmapFilter == MipmapFilter_Kaiser) {
float params[2] = { inputOptions.kaiserStretch, inputOptions.kaiserAlpha };
img.buildNextMipmap(MipmapFilter_Kaiser, inputOptions.kaiserWidth, params);
}
else {
img.buildNextMipmap(inputOptions.mipmapFilter);
}
}
nvDebugCheck(img.width() == w);
nvDebugCheck(img.height() == h);
nvDebugCheck(img.depth() == d);
if (img.isNormalMap()) {
if (inputOptions.normalizeMipmaps) {
img.normalizeNormalMap();
}
tmp = img;
}
else {
tmp = img;
tmp.toGamma(inputOptions.outputGamma);
}
quantize(tmp, compressionOptions);
compress(tmp, f, m, compressionOptions, outputOptions);
}
}
}
else
{
Array<nvtt::Surface> images(faceCount);
Array<bool> mipChainBroken(faceCount);
int w = width;
int h = height;
int d = depth;
bool canUseSourceImagesForThisFace = canUseSourceImages;
img.setImage(inputOptions.inputFormat, inputOptions.width, inputOptions.height, inputOptions.depth, inputOptions.images[f]);
uint32 imageSize = 0;
// To normal map.
if (inputOptions.convertToNormalMap) {
img.toGreyScale(inputOptions.heightFactors.x, inputOptions.heightFactors.y, inputOptions.heightFactors.z, inputOptions.heightFactors.w);
img.toNormalMap(inputOptions.bumpFrequencyScale.x, inputOptions.bumpFrequencyScale.y, inputOptions.bumpFrequencyScale.z, inputOptions.bumpFrequencyScale.w);
// https://www.khronos.org/opengles/sdk/tools/KTX/file_format_spec/#2.16
if (faceCount == 6 && arraySize == 1)
{
imageSize = estimateSize(w, h, 1, 1, compressionOptions) * faceCount;
}
// To linear space.
if (!img.isNormalMap()) {
img.toLinear(inputOptions.inputGamma);
else
{
imageSize = estimateSize(w, h, d, 1, compressionOptions) * faceCount;
}
// Resize input.
img.resize(w, h, d, ResizeFilter_Box);
outputOptions.writeData(&imageSize, sizeof(uint32));
for (int f = 0; f < faceCount; f++)
{
nvtt::Surface s;
s.setWrapMode(inputOptions.wrapMode);
s.setAlphaMode(inputOptions.alphaMode);
s.setNormalMap(inputOptions.isNormalMap);
s.setImage(inputOptions.inputFormat, inputOptions.width, inputOptions.height, inputOptions.depth, inputOptions.images[f]);
// To normal map.
if (inputOptions.convertToNormalMap) {
s.toGreyScale(inputOptions.heightFactors.x, inputOptions.heightFactors.y, inputOptions.heightFactors.z, inputOptions.heightFactors.w);
s.toNormalMap(inputOptions.bumpFrequencyScale.x, inputOptions.bumpFrequencyScale.y, inputOptions.bumpFrequencyScale.z, inputOptions.bumpFrequencyScale.w);
}
nvtt::Surface tmp = img;
if (!img.isNormalMap()) {
tmp.toGamma(inputOptions.outputGamma);
}
// To linear space.
if (!s.isNormalMap()) {
s.toLinear(inputOptions.inputGamma);
}
quantize(tmp, compressionOptions);
compress(tmp, f, 0, compressionOptions, outputOptions);
// Resize input.
s.resize(w, h, d, ResizeFilter_Box);
nvtt::Surface tmp = s;
if (!s.isNormalMap()) {
tmp.toGamma(inputOptions.outputGamma);
}
for (int m = 1; m < mipmapCount; m++) {
quantize(tmp, compressionOptions);
compress(tmp, f, 0, compressionOptions, outputOptions);
images.push_back(s);
mipChainBroken.push_back(false);
}
unsigned char padding[3] = {0, 0, 0};
for (int m = 1; m < mipmapCount; m++)
{
w = max(1, w/2);
h = max(1, h/2);
d = max(1, d/2);
int idx = m * faceCount + f;
// https://www.khronos.org/opengles/sdk/tools/KTX/file_format_spec/#2.16
if (faceCount == 6 && arraySize == 1)
{
imageSize = estimateSize(w, h, 1, 1, compressionOptions) * faceCount;
}
else
{
imageSize = estimateSize(w, h, d, 1, compressionOptions) * faceCount;
}
bool useSourceImages = false;
if (canUseSourceImagesForThisFace) {
if (inputOptions.images[idx] == NULL) { // One face is missing in this mipmap level.
canUseSourceImagesForThisFace = false; // If one level is missing, ignore the following source images.
}
else {
useSourceImages = true;
outputOptions.writeData(&imageSize, sizeof(uint32));
nvtt::Surface tmp;
for (int f = 0; f < faceCount; f++)
{
nvtt::Surface& img = images[f];
int idx = m * faceCount + f;
bool useSourceImages = false;
if (!mipChainBroken[f]) {
if (inputOptions.images[idx] == NULL) { // One face is missing in this mipmap level.
mipChainBroken[f] = false; // If one level is missing, ignore the following source images.
}
else {
useSourceImages = true;
}
}
}
if (useSourceImages) {
img.setImage(inputOptions.inputFormat, w, h, d, inputOptions.images[idx]);
if (useSourceImages) {
img.setImage(inputOptions.inputFormat, w, h, d, inputOptions.images[idx]);
// For already generated mipmaps, we need to convert to linear.
if (!img.isNormalMap()) {
img.toLinear(inputOptions.inputGamma);
// For already generated mipmaps, we need to convert to linear.
if (!img.isNormalMap()) {
img.toLinear(inputOptions.inputGamma);
}
}
}
else {
if (inputOptions.mipmapFilter == MipmapFilter_Kaiser) {
float params[2] = { inputOptions.kaiserStretch, inputOptions.kaiserAlpha };
img.buildNextMipmap(MipmapFilter_Kaiser, inputOptions.kaiserWidth, params);
else {
if (inputOptions.mipmapFilter == MipmapFilter_Kaiser) {
float params[2] = { inputOptions.kaiserStretch, inputOptions.kaiserAlpha };
img.buildNextMipmap(MipmapFilter_Kaiser, inputOptions.kaiserWidth, params);
}
else {
img.buildNextMipmap(inputOptions.mipmapFilter);
}
}
nvDebugCheck(img.width() == w);
nvDebugCheck(img.height() == h);
nvDebugCheck(img.depth() == d);
if (img.isNormalMap()) {
if (inputOptions.normalizeMipmaps) {
img.normalizeNormalMap();
}
tmp = img;
}
else {
img.buildNextMipmap(inputOptions.mipmapFilter);
tmp = img;
tmp.toGamma(inputOptions.outputGamma);
}
}
nvDebugCheck(img.width() == w);
nvDebugCheck(img.height() == h);
nvDebugCheck(img.depth() == d);
if (img.isNormalMap()) {
if (inputOptions.normalizeMipmaps) {
img.normalizeNormalMap();
quantize(tmp, compressionOptions);
compress(tmp, f, m, compressionOptions, outputOptions);
//cube padding
if (faceCount == 6 && arraySize == 1)
{
//TODO calc offset for uncompressed images
}
tmp = img;
}
else {
tmp = img;
tmp.toGamma(inputOptions.outputGamma);
int mipPadding = 3 - ((imageSize + 3) % 4);
if (mipPadding != 0) {
outputOptions.writeData(&padding, mipPadding);
}
quantize(tmp, compressionOptions);
compress(tmp, f, m, compressionOptions, outputOptions);
}
}
return true;
}
@ -670,6 +816,112 @@ bool Compressor::Private::outputHeader(nvtt::TextureType textureType, int w, int
return writeSucceed;
}
else if (outputOptions.container == Container_KTX)
{
KtxHeader header;
// TODO cube arrays
if (textureType == TextureType_2D) {
nvCheck(arraySize == 1);
header.numberOfArrayElements = 0;
header.numberOfFaces = 1;
header.pixelDepth = 0;
}
else if (textureType == TextureType_Cube) {
nvCheck(arraySize == 1);
header.numberOfArrayElements = 0;
header.numberOfFaces = 6;
header.pixelDepth = 0;
}
else if (textureType == TextureType_3D) {
nvCheck(arraySize == 1);
header.numberOfArrayElements = 0;
header.numberOfFaces = 1;
header.pixelDepth = d;
}
else if (textureType == TextureType_Array) {
header.numberOfArrayElements = arraySize;
header.numberOfFaces = 1;
header.pixelDepth = 0; // Is it?
}
header.pixelWidth = w;
header.pixelHeight = h;
header.numberOfMipmapLevels = mipmapCount;
bool supported = true;
// TODO non-compressed formats
if (compressionOptions.format == Format_RGBA)
{
//header.glType = ?;
//header.glTypeSize = ?;
//header.glFormat = ?;
}
else
{
header.glType = 0;
header.glTypeSize = 1;
header.glFormat = 0;
if (compressionOptions.format == Format_DXT1) {
header.glInternalFormat = outputOptions.srgb ? KTX_INTERNAL_COMPRESSED_SRGB_S3TC_DXT1 : KTX_INTERNAL_COMPRESSED_RGB_S3TC_DXT1;
header.glBaseInternalFormat = KTX_BASE_INTERNAL_RGB;
}
else if (compressionOptions.format == Format_DXT1a) {
header.glInternalFormat = outputOptions.srgb ? KTX_INTERNAL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1 : KTX_INTERNAL_COMPRESSED_RGBA_S3TC_DXT1;
header.glBaseInternalFormat = KTX_BASE_INTERNAL_RGBA;
}
else if (compressionOptions.format == Format_DXT3) {
header.glInternalFormat = outputOptions.srgb ? KTX_INTERNAL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3 : KTX_INTERNAL_COMPRESSED_RGBA_S3TC_DXT3;
header.glBaseInternalFormat = KTX_BASE_INTERNAL_RGBA;
}
else if (compressionOptions.format == Format_DXT5 || compressionOptions.format == Format_BC3_RGBM) {
header.glInternalFormat = outputOptions.srgb ? KTX_INTERNAL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5 : KTX_INTERNAL_COMPRESSED_RGBA_S3TC_DXT5;
header.glBaseInternalFormat = KTX_BASE_INTERNAL_RGBA;
}
else if (compressionOptions.format == Format_BC4) {
header.glInternalFormat = KTX_INTERNAL_COMPRESSED_RED_RGTC1; // KTX_INTERNAL_COMPRESSED_SIGNED_RED_RGTC1 ?
header.glBaseInternalFormat = KTX_BASE_INTERNAL_RED;
}
else if (compressionOptions.format == Format_BC5) {
header.glInternalFormat = KTX_INTERNAL_COMPRESSED_RG_RGTC2; // KTX_INTERNAL_COMPRESSED_SIGNED_RG_RGTC2 ?
header.glBaseInternalFormat = KTX_BASE_INTERNAL_RG;
}
else if (compressionOptions.format == Format_BC6) {
if (compressionOptions.pixelType == PixelType_Float) header.glInternalFormat = KTX_INTERNAL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT;
/*if (compressionOptions.pixelType == PixelType_UnsignedFloat)*/ header.glInternalFormat = KTX_INTERNAL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT; // By default we assume unsigned.
header.glBaseInternalFormat = KTX_BASE_INTERNAL_RGB;
}
else if (compressionOptions.format == Format_BC7) {
header.glInternalFormat = outputOptions.srgb ? KTX_INTERNAL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM : KTX_INTERNAL_COMPRESSED_RGBA_BPTC_UNORM;
header.glBaseInternalFormat = KTX_BASE_INTERNAL_RGBA;
}
else if (compressionOptions.format == Format_CTX1) {
supported = false;
}
else {
supported = false;
}//TODO compressionOptions.format == Format_DXT1n, Format_DXT5n ? There seems to be no way to indicate a normal map using ktx. Maybe via key value data?
}
if (!supported)
{
// This container does not support the requested format.
outputOptions.error(Error_UnsupportedOutputFormat);
return false;
}
uint headerSize = 64;
nvStaticCheck(sizeof(KtxHeader) == 64);
bool writeSucceed = outputOptions.writeData(&header, headerSize);
if (!writeSucceed)
{
outputOptions.error(Error_FileWrite);
}
return writeSucceed;
}
return true;
}
@ -857,3 +1109,24 @@ CompressorInterface * Compressor::Private::chooseGpuCompressor(const Compression
return NULL;
}
int Compressor::Private::estimateSize(int w, int h, int d, int mipmapCount, const CompressionOptions::Private & compressionOptions) const
{
const Format format = compressionOptions.format;
const uint bitCount = compressionOptions.bitcount;
const uint pitchAlignment = compressionOptions.pitchAlignment;
int size = 0;
for (int m = 0; m < mipmapCount; m++)
{
size += computeImageSize(w, h, d, bitCount, pitchAlignment, format);
// Compute extents of next mipmap:
w = max(1, w / 2);
h = max(1, h / 2);
d = max(1, d / 2);
}
return size;
}

@ -56,6 +56,7 @@ namespace nvtt
nv::CompressorInterface * chooseCpuCompressor(const CompressionOptions::Private & compressionOptions) const;
nv::CompressorInterface * chooseGpuCompressor(const CompressionOptions::Private & compressionOptions) const;
int estimateSize(int w, int h, int d, int mipmapCount, const CompressionOptions::Private & compressionOptions) const;
bool cudaSupported;
bool cudaEnabled;

@ -345,7 +345,7 @@ namespace nvtt
{
Container_DDS,
Container_DDS10,
// Container_KTX, // Khronos Texture: http://www.khronos.org/opengles/sdk/tools/KTX/
Container_KTX, // Khronos Texture: http://www.khronos.org/opengles/sdk/tools/KTX/
// Container_VTF, // Valve Texture Format: http://developer.valvesoftware.com/wiki/Valve_Texture_Format
};

@ -154,11 +154,13 @@ int main(int argc, char *argv[])
bool loadAsFloat = false;
bool rgbm = false;
bool rangescale = false;
bool srgb = false;
const char * externalCompressor = NULL;
bool silent = false;
bool dds10 = false;
bool ktx = false;
nv::Path input;
nv::Path output;
@ -309,7 +311,15 @@ int main(int argc, char *argv[])
{
dds10 = true;
}
else if (strcmp("-ktx", argv[i]) == 0)
{
ktx = true;
}
else if (strcmp("-srgb", argv[i]) == 0)
{
srgb = true;
}
else if (argv[i][0] != '-')
{
input = argv[i];
@ -321,7 +331,15 @@ int main(int argc, char *argv[])
{
output.copy(input.str());
output.stripExtension();
output.append(".dds");
if (ktx)
{
output.append(".ktx");
}
else
{
output.append(".dds");
}
}
break;
@ -380,7 +398,9 @@ int main(int argc, char *argv[])
printf("Output options:\n");
printf(" -silent \tDo not output progress messages\n");
printf(" -dds10 \tUse DirectX 10 DDS format (enabled by default for BC6/7)\n\n");
printf(" -dds10 \tUse DirectX 10 DDS format (enabled by default for BC6/7, unless ktx is being used)\n");
printf(" -ktx \tUse KTX container format\n");
printf(" -srgb \tIf the requested format allows it, output will be in sRGB color space\n\n");
return EXIT_FAILURE;
}
@ -494,7 +514,7 @@ int main(int argc, char *argv[])
nvDebugCheck(dds.isTextureArray());
inputOptions.setTextureLayout(nvtt::TextureType_Array, dds.width(), dds.height(), 1, dds.arraySize());
faceCount = dds.arraySize();
dds10 = true;
dds10 = ktx ? false : true;
}
uint mipmapCount = dds.mipmapCount();
@ -721,7 +741,7 @@ int main(int argc, char *argv[])
outputOptions.setErrorHandler(&errorHandler);
// Automatically use dds10 if compressing to BC6 or BC7
if (format == nvtt::Format_BC6 || format == nvtt::Format_BC7)
if (format == nvtt::Format_BC6 || format == nvtt::Format_BC7 && !ktx)
{
dds10 = true;
}
@ -730,6 +750,15 @@ int main(int argc, char *argv[])
{
outputOptions.setContainer(nvtt::Container_DDS10);
}
if (ktx)
{
outputOptions.setContainer(nvtt::Container_KTX);
}
if (srgb) {
outputOptions.setSrgbFlag(true);
}
// printf("Press ENTER.\n");
// fflush(stdout);

Loading…
Cancel
Save