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.
This commit is contained in:
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 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 (Table 3.3)
|
||||
// ...
|
||||
// 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 (Table 3.12, 3.13)
|
||||
// ...
|
||||
// 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 format. (Table 3.11)
|
||||
const uint KTX_RGB;
|
||||
const uint KTX_RGBA;
|
||||
const uint KTX_ALPHA;
|
||||
// ...
|
||||
// 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)
|
||||
{
|
||||
int w = width;
|
||||
int h = height;
|
||||
int d = depth;
|
||||
bool canUseSourceImagesForThisFace = canUseSourceImages;
|
||||
nvtt::Surface img;
|
||||
img.setWrapMode(inputOptions.wrapMode);
|
||||
img.setAlphaMode(inputOptions.alphaMode);
|
||||
img.setNormalMap(inputOptions.isNormalMap);
|
||||
|
||||
img.setImage(inputOptions.inputFormat, inputOptions.width, inputOptions.height, inputOptions.depth, inputOptions.images[f]);
|
||||
for (int f = 0; f < faceCount; f++)
|
||||
{
|
||||
int w = width;
|
||||
int h = height;
|
||||
int d = depth;
|
||||
bool canUseSourceImagesForThisFace = canUseSourceImages;
|
||||
|
||||
// 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);
|
||||
}
|
||||
img.setImage(inputOptions.inputFormat, inputOptions.width, inputOptions.height, inputOptions.depth, inputOptions.images[f]);
|
||||
|
||||
// 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;
|
||||
}
|
||||
// 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);
|
||||
}
|
||||
|
||||
if (useSourceImages) {
|
||||
img.setImage(inputOptions.inputFormat, w, h, d, inputOptions.images[idx]);
|
||||
// To linear space.
|
||||
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 {
|
||||
img.buildNextMipmap(inputOptions.mipmapFilter);
|
||||
}
|
||||
}
|
||||
nvDebugCheck(img.width() == w);
|
||||
nvDebugCheck(img.height() == h);
|
||||
nvDebugCheck(img.depth() == d);
|
||||
// Resize input.
|
||||
img.resize(w, h, d, ResizeFilter_Box);
|
||||
|
||||
if (img.isNormalMap()) {
|
||||
if (inputOptions.normalizeMipmaps) {
|
||||
img.normalizeNormalMap();
|
||||
}
|
||||
tmp = img;
|
||||
}
|
||||
else {
|
||||
tmp = img;
|
||||
nvtt::Surface tmp = img;
|
||||
if (!img.isNormalMap()) {
|
||||
tmp.toGamma(inputOptions.outputGamma);
|
||||
}
|
||||
|
||||
quantize(tmp, compressionOptions);
|
||||
compress(tmp, f, m, compressionOptions, outputOptions);
|
||||
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;
|
||||
|
||||
uint32 imageSize = 0;
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
// To linear space.
|
||||
if (!s.isNormalMap()) {
|
||||
s.toLinear(inputOptions.inputGamma);
|
||||
}
|
||||
|
||||
// Resize input.
|
||||
s.resize(w, h, d, ResizeFilter_Box);
|
||||
|
||||
nvtt::Surface tmp = s;
|
||||
if (!s.isNormalMap()) {
|
||||
tmp.toGamma(inputOptions.outputGamma);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
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]);
|
||||
|
||||
// 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);
|
||||
|
||||
//cube padding
|
||||
if (faceCount == 6 && arraySize == 1)
|
||||
{
|
||||
//TODO calc offset for uncompressed images
|
||||
}
|
||||
}
|
||||
|
||||
int mipPadding = 3 - ((imageSize + 3) % 4);
|
||||
if (mipPadding != 0) {
|
||||
outputOptions.writeData(&padding, mipPadding);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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…
Reference in New Issue
Block a user