Merge changes from The Witness.
This commit is contained in:
@ -25,6 +25,7 @@
|
||||
#include "BlockCompressor.h"
|
||||
#include "OutputOptions.h"
|
||||
#include "TaskDispatcher.h"
|
||||
#include "CompressionOptions.h"
|
||||
|
||||
#include "nvimage/Image.h"
|
||||
#include "nvimage/ColorBlock.h"
|
||||
@ -33,6 +34,7 @@
|
||||
#include "nvmath/Vector.inl"
|
||||
|
||||
#include "nvcore/Memory.h"
|
||||
#include "nvcore/Array.inl"
|
||||
|
||||
#include <new> // placement new
|
||||
|
||||
@ -40,85 +42,13 @@
|
||||
using namespace nv;
|
||||
using namespace nvtt;
|
||||
|
||||
/*
|
||||
// OpenMP
|
||||
#if defined(HAVE_OPENMP)
|
||||
#include <omp.h>
|
||||
#endif
|
||||
|
||||
void ColorBlockCompressor::compress(nvtt::AlphaMode alphaMode, uint w, uint h, const float * data, const nvtt::CompressionOptions::Private & compressionOptions, const nvtt::OutputOptions::Private & outputOptions)
|
||||
{
|
||||
const uint bs = blockSize();
|
||||
const uint bw = (w + 3) / 4;
|
||||
const uint bh = (h + 3) / 4;
|
||||
|
||||
#if defined(HAVE_OPENMP)
|
||||
bool singleThreaded = false;
|
||||
#else
|
||||
bool singleThreaded = true;
|
||||
#endif
|
||||
|
||||
// Use a single thread to compress small textures.
|
||||
if (bw * bh < 16) singleThreaded = true;
|
||||
|
||||
if (singleThreaded)
|
||||
{
|
||||
nvDebugCheck(bs <= 16);
|
||||
uint8 mem[16]; // @@ Output one row at a time!
|
||||
|
||||
for (int y = 0; y < int(h); y += 4) {
|
||||
for (uint x = 0; x < w; x += 4) {
|
||||
|
||||
ColorBlock rgba;
|
||||
rgba.init(w, h, data, x, y);
|
||||
|
||||
compressBlock(rgba, alphaMode, compressionOptions, mem);
|
||||
|
||||
if (outputOptions.outputHandler != NULL) {
|
||||
outputOptions.outputHandler->writeData(mem, bs);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#if defined(HAVE_OPENMP)
|
||||
else
|
||||
{
|
||||
const uint size = bs * bw * bh;
|
||||
uint8 * mem = new uint8[size];
|
||||
|
||||
#pragma omp parallel
|
||||
{
|
||||
#pragma omp for
|
||||
for (int i = 0; i < int(bw*bh); i++)
|
||||
{
|
||||
const uint x = i % bw;
|
||||
const uint y = i / bw;
|
||||
|
||||
ColorBlock rgba;
|
||||
rgba.init(w, h, data, 4*x, 4*y);
|
||||
|
||||
uint8 * ptr = mem + (y * bw + x) * bs;
|
||||
compressBlock(rgba, alphaMode, compressionOptions, ptr);
|
||||
} // omp for
|
||||
} // omp parallel
|
||||
|
||||
if (outputOptions.outputHandler != NULL) {
|
||||
outputOptions.outputHandler->writeData(mem, size);
|
||||
}
|
||||
|
||||
delete [] mem;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
struct CompressorContext
|
||||
{
|
||||
nvtt::AlphaMode alphaMode;
|
||||
AlphaMode alphaMode;
|
||||
uint w, h, d;
|
||||
const float * data;
|
||||
const nvtt::CompressionOptions::Private * compressionOptions;
|
||||
const CompressionOptions::Private * compressionOptions;
|
||||
|
||||
uint bw, bh, bs;
|
||||
uint8 * mem;
|
||||
@ -144,7 +74,7 @@ void ColorBlockCompressorTask(void * data, int i)
|
||||
}
|
||||
}
|
||||
|
||||
void ColorBlockCompressor::compress(nvtt::AlphaMode alphaMode, uint w, uint h, uint d, const float * data, nvtt::TaskDispatcher * dispatcher, const nvtt::CompressionOptions::Private & compressionOptions, const nvtt::OutputOptions::Private & outputOptions)
|
||||
void ColorBlockCompressor::compress(AlphaMode alphaMode, uint w, uint h, uint d, const float * data, TaskDispatcher * dispatcher, const CompressionOptions::Private & compressionOptions, const OutputOptions::Private & outputOptions)
|
||||
{
|
||||
nvDebugCheck(d == 1);
|
||||
|
||||
@ -182,66 +112,6 @@ void ColorBlockCompressor::compress(nvtt::AlphaMode alphaMode, uint w, uint h, u
|
||||
delete [] context.mem;
|
||||
}
|
||||
|
||||
|
||||
#if 0
|
||||
// Each task compresses one block.
|
||||
void ColorSetCompressorTask(void * data, int i)
|
||||
{
|
||||
CompressorContext * d = (CompressorContext *) data;
|
||||
|
||||
uint x = i % d->bw;
|
||||
uint y = i / d->bw;
|
||||
|
||||
//for (uint x = 0; x < d->bw; x++)
|
||||
{
|
||||
ColorSet set;
|
||||
set.setColors(d->data, d->w, d->h, x * 4, y * 4);
|
||||
|
||||
uint8 * ptr = d->mem + (y * d->bw + x) * d->bs;
|
||||
((ColorSetCompressor *)d->compressor)->compressBlock(set, d->alphaMode, *d->compressionOptions, ptr);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ColorSetCompressor::compress(AlphaMode alphaMode, uint w, uint h, uint d, const float * data, nvtt::TaskDispatcher * dispatcher, const CompressionOptions::Private & compressionOptions, const OutputOptions::Private & outputOptions)
|
||||
{
|
||||
nvDebugCheck(d == 1);
|
||||
|
||||
CompressorContext context;
|
||||
context.alphaMode = alphaMode;
|
||||
context.w = w;
|
||||
context.h = h;
|
||||
context.data = data;
|
||||
context.compressionOptions = &compressionOptions;
|
||||
|
||||
context.bs = blockSize();
|
||||
context.bw = (w + 3) / 4;
|
||||
context.bh = (h + 3) / 4;
|
||||
|
||||
context.compressor = this;
|
||||
|
||||
SequentialTaskDispatcher sequential;
|
||||
|
||||
// Use a single thread to compress small textures.
|
||||
if (context.bh < 4) dispatcher = &sequential;
|
||||
|
||||
#if _DEBUG
|
||||
dispatcher = &sequential;
|
||||
#endif
|
||||
|
||||
const uint count = context.bw * context.bh;
|
||||
const uint size = context.bs * count;
|
||||
context.mem = new uint8[size];
|
||||
|
||||
dispatcher->dispatch(ColorSetCompressorTask, &context, count);
|
||||
|
||||
outputOptions.writeData(context.mem, size);
|
||||
|
||||
delete [] context.mem;
|
||||
}
|
||||
#endif // 0
|
||||
|
||||
|
||||
// Each task compresses one block.
|
||||
void FloatColorCompressorTask(void * data, int i)
|
||||
{
|
||||
@ -262,8 +132,8 @@ void FloatColorCompressorTask(void * data, int i)
|
||||
Vector4 colors[16];
|
||||
float weights[16];
|
||||
|
||||
const uint block_w = min(d->w - block_x * 4U, 4U);
|
||||
const uint block_h = min(d->h - block_y * 4U, 4U);
|
||||
const uint block_w = min(d->w - block_x * 4, 4U);
|
||||
const uint block_h = min(d->h - block_y * 4, 4U);
|
||||
|
||||
uint x, y;
|
||||
for (y = 0; y < block_h; y++) {
|
||||
@ -274,7 +144,7 @@ void FloatColorCompressorTask(void * data, int i)
|
||||
colors[dst_idx].y = g[src_idx];
|
||||
colors[dst_idx].z = b[src_idx];
|
||||
colors[dst_idx].w = a[src_idx];
|
||||
weights[dst_idx] = (d->alphaMode == nvtt::AlphaMode_Transparency) ? a[src_idx] : 1.0f;
|
||||
weights[dst_idx] = (d->alphaMode == AlphaMode_Transparency) ? saturate(a[src_idx]) : 1.0f;
|
||||
}
|
||||
for (; x < 4; x++) {
|
||||
uint dst_idx = 4 * y + x;
|
||||
@ -289,14 +159,14 @@ void FloatColorCompressorTask(void * data, int i)
|
||||
weights[dst_idx] = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Compress block.
|
||||
uint8 * output = d->mem + (block_y * d->bw + block_x) * d->bs;
|
||||
((FloatColorCompressor *)d->compressor)->compressBlock(colors, weights, *d->compressionOptions, output);
|
||||
}
|
||||
|
||||
|
||||
void FloatColorCompressor::compress(AlphaMode alphaMode, uint w, uint h, uint d, const float * data, nvtt::TaskDispatcher * dispatcher, const CompressionOptions::Private & compressionOptions, const OutputOptions::Private & outputOptions)
|
||||
void FloatColorCompressor::compress(AlphaMode alphaMode, uint w, uint h, uint d, const float * data, TaskDispatcher * dispatcher, const CompressionOptions::Private & compressionOptions, const OutputOptions::Private & outputOptions)
|
||||
{
|
||||
nvDebugCheck(d == 1); // @@ Add support for compressed 3D textures.
|
||||
|
||||
@ -308,7 +178,7 @@ void FloatColorCompressor::compress(AlphaMode alphaMode, uint w, uint h, uint d,
|
||||
context.data = data;
|
||||
context.compressionOptions = &compressionOptions;
|
||||
|
||||
context.bs = blockSize();
|
||||
context.bs = blockSize(compressionOptions);
|
||||
context.bw = (w + 3) / 4;
|
||||
context.bh = (h + 3) / 4;
|
||||
|
||||
@ -333,3 +203,466 @@ void FloatColorCompressor::compress(AlphaMode alphaMode, uint w, uint h, uint d,
|
||||
|
||||
delete [] context.mem;
|
||||
}
|
||||
|
||||
|
||||
// BC1
|
||||
#include "CompressorDXT1.h"
|
||||
|
||||
void FastCompressorDXT1::compressBlock(Vector4 colors[16], float weights[16], const CompressionOptions::Private & compressionOptions, void * output)
|
||||
{
|
||||
compress_dxt1_fast(colors, weights, compressionOptions.colorWeight.xyz(), (BlockDXT1 *)output);
|
||||
}
|
||||
void CompressorDXT1::compressBlock(Vector4 colors[16], float weights[16], const CompressionOptions::Private & compressionOptions, void * output)
|
||||
{
|
||||
compress_dxt1(colors, weights, compressionOptions.colorWeight.xyz(), /*three_color_mode*/true, (BlockDXT1 *)output);
|
||||
}
|
||||
|
||||
|
||||
// @@ BC1a
|
||||
|
||||
// @@ BC2
|
||||
|
||||
// @@ BC3
|
||||
|
||||
|
||||
// BC3_RGBM
|
||||
#include "CompressorDXT5_RGBM.h"
|
||||
|
||||
void CompressorBC3_RGBM::compressBlock(Vector4 colors[16], float weights[16], const CompressionOptions::Private & compressionOptions, void * output)
|
||||
{
|
||||
compress_dxt5_rgbm(colors, weights, compressionOptions.rgbmThreshold, (BlockDXT5 *)output);
|
||||
}
|
||||
|
||||
|
||||
// ETC
|
||||
#include "CompressorETC.h"
|
||||
|
||||
void CompressorETC1::compressBlock(Vector4 colors[16], float weights[16], const CompressionOptions::Private & compressionOptions, void * output)
|
||||
{
|
||||
compress_etc1(colors, weights, compressionOptions.colorWeight.xyz(), output);
|
||||
}
|
||||
void CompressorETC2_R::compressBlock(Vector4 colors[16], float weights[16], const CompressionOptions::Private & compressionOptions, void * output)
|
||||
{
|
||||
// @@ Change radius based on quality.
|
||||
compress_eac(colors, weights, /*input_channel=*/1, /*search_radius=*/1, /*use_11bit_mode=*/true, output);
|
||||
}
|
||||
void CompressorETC2_RG::compressBlock(Vector4 colors[16], float weights[16], const CompressionOptions::Private & compressionOptions, void * output)
|
||||
{
|
||||
//compress_eac_rg(colors, weights, 1, 2, output);
|
||||
}
|
||||
void CompressorETC2_RGB::compressBlock(Vector4 colors[16], float weights[16], const CompressionOptions::Private & compressionOptions, void * output)
|
||||
{
|
||||
// @@ Tweak quality options.
|
||||
compress_etc2(colors, weights, compressionOptions.colorWeight.xyz(), output);
|
||||
}
|
||||
void CompressorETC2_RGBA::compressBlock(Vector4 colors[16], float weights[16], const CompressionOptions::Private & compressionOptions, void * output)
|
||||
{
|
||||
// @@ Tweak quality options.
|
||||
// @@ Change radius based on quality.
|
||||
compress_etc2_eac(colors, weights, compressionOptions.colorWeight.xyz(), output);
|
||||
}
|
||||
/*void CompressorETC2_RG::compressBlock(Vector4 colors[16], float weights[16], const CompressionOptions::Private & compressionOptions, void * output)
|
||||
{
|
||||
// @@ Change radius based on quality.
|
||||
compress_eac_rg(colors, weights, compressionOptions.colorWeight.xyz(), output);
|
||||
}*/
|
||||
void CompressorETC2_RGBM::compressBlock(Vector4 colors[16], float weights[16], const CompressionOptions::Private & compressionOptions, void * output)
|
||||
{
|
||||
compress_etc2_rgbm(colors, weights, compressionOptions.rgbmThreshold, output);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// External compressors.
|
||||
|
||||
#if defined(HAVE_ATITC)
|
||||
|
||||
typedef int BOOL;
|
||||
typedef _W64 unsigned long ULONG_PTR;
|
||||
typedef ULONG_PTR DWORD_PTR;
|
||||
#include "atitc/ATI_Compress.h"
|
||||
|
||||
void AtiCompressorDXT1::compress(InputFormat inputFormat, AlphaMode alphaMode, uint w, uint h, uint d, void * data, const CompressionOptions::Private & compressionOptions, const OutputOptions::Private & outputOptions)
|
||||
{
|
||||
nvDebugCheck(d == 1);
|
||||
|
||||
// Init source texture
|
||||
ATI_TC_Texture srcTexture;
|
||||
srcTexture.dwSize = sizeof(srcTexture);
|
||||
srcTexture.dwWidth = w;
|
||||
srcTexture.dwHeight = h;
|
||||
if (inputFormat == InputFormat_BGRA_8UB)
|
||||
{
|
||||
srcTexture.dwPitch = w * 4;
|
||||
srcTexture.format = ATI_TC_FORMAT_ARGB_8888;
|
||||
}
|
||||
else
|
||||
{
|
||||
// @@ Floating point input is not swizzled.
|
||||
srcTexture.dwPitch = w * 16;
|
||||
srcTexture.format = ATI_TC_FORMAT_ARGB_32F;
|
||||
}
|
||||
srcTexture.dwDataSize = ATI_TC_CalculateBufferSize(&srcTexture);
|
||||
srcTexture.pData = (ATI_TC_BYTE*) data;
|
||||
|
||||
// Init dest texture
|
||||
ATI_TC_Texture destTexture;
|
||||
destTexture.dwSize = sizeof(destTexture);
|
||||
destTexture.dwWidth = w;
|
||||
destTexture.dwHeight = h;
|
||||
destTexture.dwPitch = 0;
|
||||
destTexture.format = ATI_TC_FORMAT_DXT1;
|
||||
destTexture.dwDataSize = ATI_TC_CalculateBufferSize(&destTexture);
|
||||
destTexture.pData = (ATI_TC_BYTE*) mem::malloc(destTexture.dwDataSize);
|
||||
|
||||
ATI_TC_CompressOptions options;
|
||||
options.dwSize = sizeof(options);
|
||||
options.bUseChannelWeighting = false;
|
||||
options.bUseAdaptiveWeighting = false;
|
||||
options.bDXT1UseAlpha = false;
|
||||
options.nCompressionSpeed = ATI_TC_Speed_Normal;
|
||||
options.bDisableMultiThreading = false;
|
||||
//options.bDisableMultiThreading = true;
|
||||
|
||||
// Compress
|
||||
ATI_TC_ConvertTexture(&srcTexture, &destTexture, &options, NULL, NULL, NULL);
|
||||
|
||||
if (outputOptions.outputHandler != NULL) {
|
||||
outputOptions.outputHandler->writeData(destTexture.pData, destTexture.dwDataSize);
|
||||
}
|
||||
|
||||
mem::free(destTexture.pData);
|
||||
}
|
||||
|
||||
void AtiCompressorDXT5::compress(InputFormat inputFormat, AlphaMode alphaMode, uint w, uint h, uint d, void * data, const CompressionOptions::Private & compressionOptions, const OutputOptions::Private & outputOptions)
|
||||
{
|
||||
nvDebugCheck(d == 1);
|
||||
|
||||
// Init source texture
|
||||
ATI_TC_Texture srcTexture;
|
||||
srcTexture.dwSize = sizeof(srcTexture);
|
||||
srcTexture.dwWidth = w;
|
||||
srcTexture.dwHeight = h;
|
||||
if (inputFormat == InputFormat_BGRA_8UB)
|
||||
{
|
||||
srcTexture.dwPitch = w * 4;
|
||||
srcTexture.format = ATI_TC_FORMAT_ARGB_8888;
|
||||
}
|
||||
else
|
||||
{
|
||||
srcTexture.dwPitch = w * 16;
|
||||
srcTexture.format = ATI_TC_FORMAT_ARGB_32F;
|
||||
}
|
||||
srcTexture.dwDataSize = ATI_TC_CalculateBufferSize(&srcTexture);
|
||||
srcTexture.pData = (ATI_TC_BYTE*) data;
|
||||
|
||||
// Init dest texture
|
||||
ATI_TC_Texture destTexture;
|
||||
destTexture.dwSize = sizeof(destTexture);
|
||||
destTexture.dwWidth = w;
|
||||
destTexture.dwHeight = h;
|
||||
destTexture.dwPitch = 0;
|
||||
destTexture.format = ATI_TC_FORMAT_DXT5;
|
||||
destTexture.dwDataSize = ATI_TC_CalculateBufferSize(&destTexture);
|
||||
destTexture.pData = (ATI_TC_BYTE*) mem::malloc(destTexture.dwDataSize);
|
||||
|
||||
// Compress
|
||||
ATI_TC_ConvertTexture(&srcTexture, &destTexture, NULL, NULL, NULL, NULL);
|
||||
|
||||
if (outputOptions.outputHandler != NULL) {
|
||||
outputOptions.outputHandler->writeData(destTexture.pData, destTexture.dwDataSize);
|
||||
}
|
||||
|
||||
mem::free(destTexture.pData);
|
||||
}
|
||||
|
||||
#endif // defined(HAVE_ATITC)
|
||||
|
||||
#if defined(HAVE_SQUISH)
|
||||
|
||||
//#include "squish/squish.h"
|
||||
#include "squish-1.10/squish.h"
|
||||
|
||||
void SquishCompressorDXT1::compress(InputFormat inputFormat, AlphaMode alphaMode, uint w, uint h, uint d, void * data, const CompressionOptions::Private & compressionOptions, const OutputOptions::Private & outputOptions)
|
||||
{
|
||||
nvDebugCheck(d == 1);
|
||||
nvDebugCheck(false);
|
||||
|
||||
#pragma message(NV_FILE_LINE "TODO: Convert input to fixed point ABGR format instead of ARGB")
|
||||
/*
|
||||
Image img(*image);
|
||||
int count = img.width() * img.height();
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
Color32 c = img.pixel(i);
|
||||
img.pixel(i) = Color32(c.b, c.g, c.r, c.a);
|
||||
}
|
||||
|
||||
int size = squish::GetStorageRequirements(img.width(), img.height(), squish::kDxt1);
|
||||
void * blocks = mem::malloc(size);
|
||||
|
||||
squish::CompressImage((const squish::u8 *)img.pixels(), img.width(), img.height(), blocks, squish::kDxt1 | squish::kColourClusterFit);
|
||||
|
||||
if (outputOptions.outputHandler != NULL) {
|
||||
outputOptions.outputHandler->writeData(blocks, size);
|
||||
}
|
||||
|
||||
mem::free(blocks);
|
||||
*/
|
||||
}
|
||||
|
||||
#endif // defined(HAVE_SQUISH)
|
||||
|
||||
|
||||
#if defined(HAVE_D3DX)
|
||||
|
||||
void D3DXCompressorDXT1::compress(InputFormat inputFormat, AlphaMode alphaMode, uint w, uint h, uint d, void * data, const CompressionOptions::Private & compressionOptions, const OutputOptions::Private & outputOptions)
|
||||
{
|
||||
nvDebugCheck(d == 1);
|
||||
|
||||
IDirect3D9 * d3d = Direct3DCreate9(D3D_SDK_VERSION);
|
||||
|
||||
D3DPRESENT_PARAMETERS presentParams;
|
||||
ZeroMemory(&presentParams, sizeof(presentParams));
|
||||
presentParams.Windowed = TRUE;
|
||||
presentParams.SwapEffect = D3DSWAPEFFECT_COPY;
|
||||
presentParams.BackBufferWidth = 8;
|
||||
presentParams.BackBufferHeight = 8;
|
||||
presentParams.BackBufferFormat = D3DFMT_UNKNOWN;
|
||||
|
||||
HRESULT err;
|
||||
|
||||
IDirect3DDevice9 * device = NULL;
|
||||
err = d3d->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_REF, GetDesktopWindow(), D3DCREATE_SOFTWARE_VERTEXPROCESSING, &presentParams, &device);
|
||||
|
||||
IDirect3DTexture9 * texture = NULL;
|
||||
err = D3DXCreateTexture(device, w, h, 1, 0, D3DFMT_DXT1, D3DPOOL_SYSTEMMEM, &texture);
|
||||
|
||||
IDirect3DSurface9 * surface = NULL;
|
||||
err = texture->GetSurfaceLevel(0, &surface);
|
||||
|
||||
RECT rect;
|
||||
rect.left = 0;
|
||||
rect.top = 0;
|
||||
rect.bottom = h;
|
||||
rect.right = w;
|
||||
|
||||
if (inputFormat == InputFormat_BGRA_8UB)
|
||||
{
|
||||
err = D3DXLoadSurfaceFromMemory(surface, NULL, NULL, data, D3DFMT_A8R8G8B8, w * 4, NULL, &rect, D3DX_DEFAULT, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
err = D3DXLoadSurfaceFromMemory(surface, NULL, NULL, data, D3DFMT_A32B32G32R32F, w * 16, NULL, &rect, D3DX_DEFAULT, 0);
|
||||
}
|
||||
|
||||
if (err != D3DERR_INVALIDCALL && err != D3DXERR_INVALIDDATA)
|
||||
{
|
||||
D3DLOCKED_RECT rect;
|
||||
ZeroMemory(&rect, sizeof(rect));
|
||||
|
||||
err = surface->LockRect(&rect, NULL, D3DLOCK_READONLY);
|
||||
|
||||
if (outputOptions.outputHandler != NULL) {
|
||||
int size = rect.Pitch * ((h + 3) / 4);
|
||||
outputOptions.outputHandler->writeData(rect.pBits, size);
|
||||
}
|
||||
|
||||
err = surface->UnlockRect();
|
||||
}
|
||||
|
||||
surface->Release();
|
||||
device->Release();
|
||||
d3d->Release();
|
||||
}
|
||||
|
||||
#endif // defined(HAVE_D3DX)
|
||||
|
||||
|
||||
#if defined(HAVE_STB)
|
||||
|
||||
#define STB_DEFINE
|
||||
#include "stb/stb_dxt.h"
|
||||
|
||||
void StbCompressorDXT1::compressBlock(ColorBlock & rgba, AlphaMode alphaMode, const CompressionOptions::Private & compressionOptions, void * output)
|
||||
{
|
||||
rgba.swizzle(2, 1, 0, 3); // Swap R and B
|
||||
stb_compress_dxt_block((unsigned char *)output, (unsigned char *)rgba.colors(), 0, 0);
|
||||
}
|
||||
|
||||
#endif // defined(HAVE_STB)
|
||||
|
||||
|
||||
#if defined(HAVE_ETCLIB)
|
||||
#include "Etc.h"
|
||||
|
||||
void EtcLibCompressor::compress(AlphaMode alphaMode, uint w, uint h, uint d, const float * data, TaskDispatcher * dispatcher, const CompressionOptions::Private & compressionOptions, const OutputOptions::Private & outputOptions)
|
||||
{
|
||||
//nvCheck(d == 1); // Encode one layer at a time?
|
||||
|
||||
Etc::Image::Format format;
|
||||
if (compressionOptions.format == Format_ETC1) {
|
||||
format = Etc::Image::Format::ETC1;
|
||||
}
|
||||
else if (compressionOptions.format == Format_ETC2_R) {
|
||||
format = Etc::Image::Format::R11;
|
||||
}
|
||||
else if (compressionOptions.format == Format_ETC2_RG) {
|
||||
format = Etc::Image::Format::RG11;
|
||||
}
|
||||
else if (compressionOptions.format == Format_ETC2_RGB) {
|
||||
format = Etc::Image::Format::RGB8;
|
||||
//format = Etc::Image::Format::SRGB8;
|
||||
}
|
||||
else if (compressionOptions.format == Format_ETC2_RGBA) {
|
||||
format = Etc::Image::Format::RGBA8;
|
||||
//format = Etc::Image::Format::SRGBA8;
|
||||
}
|
||||
else if (compressionOptions.format == Format_ETC2_RGB_A1) {
|
||||
format = Etc::Image::Format::RGB8A1;
|
||||
//format = Etc::Image::Format::SRGB8A1;
|
||||
}
|
||||
else {
|
||||
nvCheck(false);
|
||||
return;
|
||||
}
|
||||
|
||||
Etc::ErrorMetric error_metric = Etc::ErrorMetric::RGBA;
|
||||
|
||||
// @@ Use normal compression metric for normals?
|
||||
//if (compressionOptions.)
|
||||
|
||||
// @@ Adjust based on quality.
|
||||
int effort = ETCCOMP_DEFAULT_EFFORT_LEVEL;
|
||||
|
||||
// @@ What are the defaults?
|
||||
uint jobs = 4;
|
||||
uint max_jobs = 4;
|
||||
|
||||
uint8 * out_data = NULL;
|
||||
uint out_size = 0;
|
||||
uint out_width = 0;
|
||||
uint out_height = 0;
|
||||
int out_time = 0;
|
||||
|
||||
// Swizzle color data.
|
||||
nv::Array<float> tmp;
|
||||
uint count = w * h;
|
||||
tmp.resize(4 * count);
|
||||
for (uint i = 0; i < count; i++) {
|
||||
tmp[4*i+0] = data[count*0 + i];
|
||||
tmp[4*i+1] = data[count*1 + i];
|
||||
tmp[4*i+2] = data[count*2 + i];
|
||||
tmp[4*i+3] = data[count*3 + i];
|
||||
}
|
||||
|
||||
Etc::Encode(tmp.buffer(), w, h, format, error_metric, effort, jobs, max_jobs, &out_data, &out_size, &out_width, &out_height, &out_time);
|
||||
|
||||
if (outputOptions.outputHandler != NULL) {
|
||||
outputOptions.outputHandler->writeData(out_data, I32(out_size));
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_RGETC)
|
||||
#include "rg_etc1.h"
|
||||
|
||||
NV_AT_STARTUP(rg_etc1::pack_etc1_block_init());
|
||||
|
||||
void RgEtcCompressor::compressBlock(ColorBlock & rgba, AlphaMode alphaMode, const CompressionOptions::Private & compressionOptions, void * output)
|
||||
{
|
||||
rg_etc1::etc1_pack_params pack_params;
|
||||
|
||||
pack_params.m_quality = rg_etc1::cMediumQuality;
|
||||
if (compressionOptions.quality == Quality_Fastest) pack_params.m_quality = rg_etc1::cLowQuality;
|
||||
else if (compressionOptions.quality == Quality_Production) pack_params.m_quality = rg_etc1::cHighQuality;
|
||||
else if (compressionOptions.quality == Quality_Highest) pack_params.m_quality = rg_etc1::cHighQuality;
|
||||
else if (compressionOptions.quality == Quality_Normal) pack_params.m_quality = rg_etc1::cMediumQuality;
|
||||
|
||||
rgba.swizzle(2, 1, 0, 3);
|
||||
rg_etc1::pack_etc1_block(output, (uint *)rgba.colors(), pack_params);
|
||||
|
||||
//Vector4 result[16];
|
||||
//nv::decompress_etc(output, result);
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#if defined(HAVE_PVRTEXTOOL)
|
||||
|
||||
#include <PVRTextureUtilities.h> // for CPVRTexture, CPVRTextureHeader, PixelType, Transcode
|
||||
|
||||
#include "nvmath/Color.inl"
|
||||
|
||||
void CompressorPVR::compress(AlphaMode alphaMode, uint w, uint h, uint d, const float * data, TaskDispatcher * dispatcher, const CompressionOptions::Private & compressionOptions, const OutputOptions::Private & outputOptions)
|
||||
{
|
||||
EPVRTColourSpace color_space = ePVRTCSpacelRGB;
|
||||
|
||||
//pvrtexture::PixelType src_pixel_type = pvrtexture::PixelType('b','g','r','a',8,8,8,8);
|
||||
pvrtexture::PixelType src_pixel_type = pvrtexture::PixelType('r','g','b',0,8,8,8,0);
|
||||
pvrtexture::CPVRTextureHeader header(src_pixel_type.PixelTypeID, w, h, d, 1/*num mips*/, 1/*num array*/, 1/*num faces*/, color_space, ePVRTVarTypeUnsignedByteNorm);
|
||||
|
||||
/*
|
||||
uint count = w * h * d;
|
||||
Array<Color32> tmp;
|
||||
tmp.resize(count);
|
||||
|
||||
for (uint i = 0; i < count; i++) {
|
||||
tmp[i] = toColor32(Vector4(data[0*count + i], data[1*count + i], data[2*count + i], data[3*count + i]));
|
||||
}
|
||||
*/
|
||||
|
||||
uint count = w * h * d;
|
||||
Array<uint8> tmp;
|
||||
tmp.resize(3 * count);
|
||||
|
||||
for (uint i = 0; i < count; i++) {
|
||||
tmp[3*i+0] = data[0*count + i] * 255.0f;
|
||||
tmp[3*i+1] = data[1*count + i] * 255.0f;
|
||||
tmp[3*i+2] = data[2*count + i] * 255.0f;
|
||||
}
|
||||
|
||||
pvrtexture::CPVRTexture texture(header, tmp.buffer());
|
||||
|
||||
pvrtexture::PixelType dst_pixel_type = pvrtexture::PixelType(ePVRTPF_PVRTCI_2bpp_RGB);
|
||||
|
||||
if (compressionOptions.format == Format_PVR_2BPP_RGB) dst_pixel_type = pvrtexture::PixelType(ePVRTPF_PVRTCI_2bpp_RGB);
|
||||
else if (compressionOptions.format == Format_PVR_4BPP_RGB) dst_pixel_type = pvrtexture::PixelType(ePVRTPF_PVRTCI_4bpp_RGB);
|
||||
else if (compressionOptions.format == Format_PVR_2BPP_RGBA) dst_pixel_type = pvrtexture::PixelType(ePVRTPF_PVRTCI_2bpp_RGBA);
|
||||
else if (compressionOptions.format == Format_PVR_4BPP_RGBA) dst_pixel_type = pvrtexture::PixelType(ePVRTPF_PVRTCI_4bpp_RGBA);
|
||||
|
||||
bool success = pvrtexture::Transcode(texture, dst_pixel_type, ePVRTVarTypeUnsignedByteNorm, color_space, pvrtexture::ePVRTCNormal, false);
|
||||
|
||||
if (success) {
|
||||
uint size = 0;
|
||||
if (compressionOptions.format == Format_PVR_2BPP_RGB || compressionOptions.format == Format_PVR_2BPP_RGBA) {
|
||||
// 2 bpp
|
||||
const uint bpp = 2u;
|
||||
const uint block_size = 8u * 4u;
|
||||
const uint size_factor=(block_size*bpp)>>3u;
|
||||
const uint block_width=nv::max((w>>3u), 2u);
|
||||
const uint block_height=nv::max((h>>2u), 2u);
|
||||
size = d * block_width * block_height * size_factor;
|
||||
}
|
||||
else {
|
||||
// 4 bpp
|
||||
const uint bpp = 4u;
|
||||
const uint block_size = 4u * 4u;
|
||||
const uint size_factor = (block_size*bpp) >> 3u;
|
||||
const uint block_width = max((w>>2u), 2u);
|
||||
const uint block_height = max((h>>2u), 2u);
|
||||
size = d * block_width * block_height * size_factor;
|
||||
}
|
||||
|
||||
if (outputOptions.outputHandler != NULL) {
|
||||
outputOptions.outputHandler->writeData(texture.getDataPtr(), I32(size));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
@ -27,7 +27,6 @@
|
||||
|
||||
#include "Compressor.h"
|
||||
|
||||
|
||||
namespace nv
|
||||
{
|
||||
struct ColorBlock;
|
||||
@ -45,10 +44,150 @@ namespace nv
|
||||
{
|
||||
virtual void compress(nvtt::AlphaMode alphaMode, uint w, uint h, uint d, const float * rgba, nvtt::TaskDispatcher * dispatcher, const nvtt::CompressionOptions::Private & compressionOptions, const nvtt::OutputOptions::Private & outputOptions);
|
||||
|
||||
virtual void compressBlock(const Vector4 colors[16], const float weights[16], const nvtt::CompressionOptions::Private & compressionOptions, void * output) = 0;
|
||||
virtual uint blockSize() const = 0;
|
||||
virtual void compressBlock(Vector4 colors[16], float weights[16], const nvtt::CompressionOptions::Private & compressionOptions, void * output) = 0;
|
||||
virtual uint blockSize(const nvtt::CompressionOptions::Private & compressionOptions) const = 0;
|
||||
};
|
||||
|
||||
|
||||
// BC1
|
||||
struct FastCompressorDXT1 : public FloatColorCompressor
|
||||
{
|
||||
virtual void compressBlock(Vector4 colors[16], float weights[16], const nvtt::CompressionOptions::Private & compressionOptions, void * output);
|
||||
virtual uint blockSize(const nvtt::CompressionOptions::Private &) const { return 8; }
|
||||
};
|
||||
struct CompressorDXT1 : public FloatColorCompressor
|
||||
{
|
||||
virtual void compressBlock(Vector4 colors[16], float weights[16], const nvtt::CompressionOptions::Private & compressionOptions, void * output);
|
||||
virtual uint blockSize(const nvtt::CompressionOptions::Private &) const { return 8; }
|
||||
};
|
||||
|
||||
// BC3
|
||||
struct CompressorBC3_RGBM : public FloatColorCompressor
|
||||
{
|
||||
virtual void compressBlock(Vector4 colors[16], float weights[16], const nvtt::CompressionOptions::Private & compressionOptions, void * output);
|
||||
virtual uint blockSize(const nvtt::CompressionOptions::Private &) const { return 16; }
|
||||
};
|
||||
|
||||
|
||||
// ETC
|
||||
struct CompressorETC1 : public FloatColorCompressor
|
||||
{
|
||||
virtual void compressBlock(Vector4 colors[16], float weights[16], const nvtt::CompressionOptions::Private & compressionOptions, void * output);
|
||||
virtual uint blockSize(const nvtt::CompressionOptions::Private &) const { return 8; }
|
||||
};
|
||||
struct CompressorETC2_R : public FloatColorCompressor
|
||||
{
|
||||
virtual void compressBlock(Vector4 colors[16], float weights[16], const nvtt::CompressionOptions::Private & compressionOptions, void * output);
|
||||
virtual uint blockSize(const nvtt::CompressionOptions::Private & ) const { return 8; }
|
||||
};
|
||||
struct CompressorETC2_RG : public FloatColorCompressor
|
||||
{
|
||||
virtual void compressBlock(Vector4 colors[16], float weights[16], const nvtt::CompressionOptions::Private & compressionOptions, void * output);
|
||||
virtual uint blockSize(const nvtt::CompressionOptions::Private & ) const { return 16; }
|
||||
};
|
||||
struct CompressorETC2_RGB : public FloatColorCompressor
|
||||
{
|
||||
virtual void compressBlock(Vector4 colors[16], float weights[16], const nvtt::CompressionOptions::Private & compressionOptions, void * output);
|
||||
virtual uint blockSize(const nvtt::CompressionOptions::Private & ) const { return 8; }
|
||||
};
|
||||
struct CompressorETC2_RGBA : public FloatColorCompressor
|
||||
{
|
||||
virtual void compressBlock(Vector4 colors[16], float weights[16], const nvtt::CompressionOptions::Private & compressionOptions, void * output);
|
||||
virtual uint blockSize(const nvtt::CompressionOptions::Private & ) const { return 16; }
|
||||
};
|
||||
struct CompressorETC2_RGBM : public FloatColorCompressor
|
||||
{
|
||||
virtual void compressBlock(Vector4 colors[16], float weights[16], const nvtt::CompressionOptions::Private & compressionOptions, void * output);
|
||||
virtual uint blockSize(const nvtt::CompressionOptions::Private &) const { return 16; }
|
||||
};
|
||||
|
||||
|
||||
// External compressors.
|
||||
#if defined(HAVE_ATITC)
|
||||
struct AtiCompressorDXT1 : public CompressorInterface
|
||||
{
|
||||
virtual void compress(nvtt::InputFormat inputFormat, nvtt::AlphaMode alphaMode, uint w, uint h, uint d, void * data, const nvtt::CompressionOptions::Private & compressionOptions, const nvtt::OutputOptions::Private & outputOptions);
|
||||
};
|
||||
|
||||
struct AtiCompressorDXT5 : public CompressorInterface
|
||||
{
|
||||
virtual void compress(nvtt::InputFormat inputFormat, nvtt::AlphaMode alphaMode, uint w, uint h, uint d, void * data, const nvtt::CompressionOptions::Private & compressionOptions, const nvtt::OutputOptions::Private & outputOptions);
|
||||
};
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_SQUISH)
|
||||
struct SquishCompressorDXT1 : public CompressorInterface
|
||||
{
|
||||
virtual void compress(nvtt::InputFormat inputFormat, nvtt::AlphaMode alphaMode, uint w, uint h, uint d, void * data, const nvtt::CompressionOptions::Private & compressionOptions, const nvtt::OutputOptions::Private & outputOptions);
|
||||
};
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_D3DX)
|
||||
struct D3DXCompressorDXT1 : public CompressorInterface
|
||||
{
|
||||
virtual void compress(nvtt::InputFormat inputFormat, nvtt::AlphaMode alphaMode, uint w, uint h, uint d, void * data, const nvtt::CompressionOptions::Private & compressionOptions, const nvtt::OutputOptions::Private & outputOptions);
|
||||
};
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_STB)
|
||||
struct StbCompressorDXT1 : public ColorBlockCompressor
|
||||
{
|
||||
virtual void compressBlock(ColorBlock & rgba, nvtt::AlphaMode alphaMode, const nvtt::CompressionOptions::Private & compressionOptions, void * output);
|
||||
virtual uint blockSize() const { return 8; }
|
||||
};
|
||||
#endif
|
||||
|
||||
#if NV_USE_CRUNCH
|
||||
struct CrunchCompressorETC1 : public CompressorInterface
|
||||
{
|
||||
virtual void compress(nvtt::InputFormat inputFormat, nvtt::AlphaMode alphaMode, uint w, uint h, uint d, void * data, const nvtt::CompressionOptions::Private & compressionOptions, const nvtt::OutputOptions::Private & outputOptions);
|
||||
};
|
||||
#endif
|
||||
|
||||
#if NV_USE_INTEL_ISPC_TC
|
||||
struct IspcCompressorBC1 : public CompressorInterface
|
||||
{
|
||||
virtual void compress(nvtt::InputFormat inputFormat, nvtt::AlphaMode alphaMode, uint w, uint h, uint d, void * data, const nvtt::CompressionOptions::Private & compressionOptions, const nvtt::OutputOptions::Private & outputOptions);
|
||||
};
|
||||
|
||||
struct IspcCompressorBC3 : public CompressorInterface
|
||||
{
|
||||
virtual void compress(nvtt::InputFormat inputFormat, nvtt::AlphaMode alphaMode, uint w, uint h, uint d, void * data, const nvtt::CompressionOptions::Private & compressionOptions, const nvtt::OutputOptions::Private & outputOptions);
|
||||
};
|
||||
|
||||
struct IspcCompressorBC7 : public CompressorInterface
|
||||
{
|
||||
virtual void compress(nvtt::InputFormat inputFormat, nvtt::AlphaMode alphaMode, uint w, uint h, uint d, void * data, const nvtt::CompressionOptions::Private & compressionOptions, const nvtt::OutputOptions::Private & outputOptions);
|
||||
};
|
||||
|
||||
struct IspcCompressorETC1 : public CompressorInterface
|
||||
{
|
||||
virtual void compress(nvtt::InputFormat inputFormat, nvtt::AlphaMode alphaMode, uint w, uint h, uint d, void * data, const nvtt::CompressionOptions::Private & compressionOptions, const nvtt::OutputOptions::Private & outputOptions);
|
||||
};
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_ETCLIB)
|
||||
struct EtcLibCompressor : public CompressorInterface
|
||||
{
|
||||
virtual void compress(nvtt::AlphaMode alphaMode, uint w, uint h, uint d, const float * data, nvtt::TaskDispatcher * dispatcher, const nvtt::CompressionOptions::Private & compressionOptions, const nvtt::OutputOptions::Private & outputOptions);
|
||||
};
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_RGETC)
|
||||
struct RgEtcCompressor : public ColorBlockCompressor
|
||||
{
|
||||
virtual void compressBlock(ColorBlock & rgba, nvtt::AlphaMode alphaMode, const nvtt::CompressionOptions::Private & compressionOptions, void * output);
|
||||
virtual uint blockSize() const { return 8; }
|
||||
};
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_PVRTEXTOOL)
|
||||
struct CompressorPVR : public CompressorInterface
|
||||
{
|
||||
virtual void compress(nvtt::AlphaMode alphaMode, uint w, uint h, uint d, const float * data, nvtt::TaskDispatcher * dispatcher, const nvtt::CompressionOptions::Private & compressionOptions, const nvtt::OutputOptions::Private & outputOptions);
|
||||
};
|
||||
#endif
|
||||
|
||||
} // nv namespace
|
||||
|
||||
|
||||
|
@ -13,6 +13,7 @@ SET(NVTT_SRCS
|
||||
CompressorDX11.h CompressorDX11.cpp
|
||||
CompressorDXT1.h CompressorDXT1.cpp
|
||||
CompressorDXT5_RGBM.h CompressorDXT5_RGBM.cpp
|
||||
CompressorETC.h CompressorETC.cpp
|
||||
CompressorRGB.h CompressorRGB.cpp
|
||||
Context.h Context.cpp
|
||||
QuickCompressDXT.h QuickCompressDXT.cpp
|
||||
@ -38,6 +39,7 @@ IF (CUDA_FOUND)
|
||||
ENDIF (CUDA_FOUND)
|
||||
|
||||
INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR})
|
||||
INCLUDE_DIRECTORIES(${NV_SOURCE_DIR}/extern/rg_etc1_v104)
|
||||
|
||||
ADD_DEFINITIONS(-DNVTT_EXPORTS)
|
||||
|
||||
@ -47,7 +49,7 @@ ELSE(NVTT_SHARED)
|
||||
ADD_LIBRARY(nvtt ${NVTT_SRCS})
|
||||
ENDIF(NVTT_SHARED)
|
||||
|
||||
TARGET_LINK_LIBRARIES(nvtt ${LIBS} nvcore nvimage nvthread squish bc6h bc7 nvmath)
|
||||
TARGET_LINK_LIBRARIES(nvtt ${LIBS} nvcore nvimage nvthread squish bc6h bc7 nvmath rg_etc1)
|
||||
|
||||
INSTALL(TARGETS nvtt
|
||||
RUNTIME DESTINATION bin
|
||||
|
@ -38,79 +38,6 @@ ClusterFit::ClusterFit()
|
||||
{
|
||||
}
|
||||
|
||||
#if 0 // @@ Deprecate. Do not use color set directly.
|
||||
void ClusterFit::setColorSet(const ColorSet * set)
|
||||
{
|
||||
// initialise the best error
|
||||
#if NVTT_USE_SIMD
|
||||
m_besterror = SimdVector( FLT_MAX );
|
||||
Vector3 metric = m_metric.toVector3();
|
||||
#else
|
||||
m_besterror = FLT_MAX;
|
||||
Vector3 metric = m_metric;
|
||||
#endif
|
||||
|
||||
// cache some values
|
||||
m_count = set->colorCount;
|
||||
|
||||
Vector3 values[16];
|
||||
for (uint i = 0; i < m_count; i++)
|
||||
{
|
||||
values[i] = set->colors[i].xyz();
|
||||
}
|
||||
|
||||
Vector3 principal = Fit::computePrincipalComponent_PowerMethod(m_count, values, set->weights, metric);
|
||||
//Vector3 principal = Fit::computePrincipalComponent_EigenSolver(m_count, values, set->weights, metric);
|
||||
|
||||
// build the list of values
|
||||
int order[16];
|
||||
float dps[16];
|
||||
for (uint i = 0; i < m_count; ++i)
|
||||
{
|
||||
dps[i] = dot(values[i], principal);
|
||||
order[i] = i;
|
||||
}
|
||||
|
||||
// stable sort
|
||||
for (uint i = 0; i < m_count; ++i)
|
||||
{
|
||||
for (uint j = i; j > 0 && dps[j] < dps[j - 1]; --j)
|
||||
{
|
||||
swap(dps[j], dps[j - 1]);
|
||||
swap(order[j], order[j - 1]);
|
||||
}
|
||||
}
|
||||
|
||||
// weight all the points
|
||||
#if NVTT_USE_SIMD
|
||||
m_xxsum = SimdVector( 0.0f );
|
||||
m_xsum = SimdVector( 0.0f );
|
||||
#else
|
||||
m_xxsum = Vector3(0.0f);
|
||||
m_xsum = Vector3(0.0f);
|
||||
m_wsum = 0.0f;
|
||||
#endif
|
||||
|
||||
for (uint i = 0; i < m_count; ++i)
|
||||
{
|
||||
int p = order[i];
|
||||
#if NVTT_USE_SIMD
|
||||
NV_ALIGN_16 Vector4 tmp(values[p], 1);
|
||||
m_weighted[i] = SimdVector(tmp.component) * SimdVector(set->weights[p]);
|
||||
m_xxsum += m_weighted[i] * m_weighted[i];
|
||||
m_xsum += m_weighted[i];
|
||||
#else
|
||||
m_weighted[i] = values[p] * set->weights[p];
|
||||
m_xxsum += m_weighted[i] * m_weighted[i];
|
||||
m_xsum += m_weighted[i];
|
||||
m_weights[i] = set->weights[p];
|
||||
m_wsum += m_weights[i];
|
||||
#endif
|
||||
}
|
||||
}
|
||||
#endif // 0
|
||||
|
||||
|
||||
void ClusterFit::setColorSet(const Vector3 * colors, const float * weights, int count)
|
||||
{
|
||||
// initialise the best error
|
||||
@ -412,13 +339,13 @@ bool ClusterFit::compress4( Vector3 * start, Vector3 * end )
|
||||
#else
|
||||
|
||||
inline Vector3 round565(const Vector3 & v) {
|
||||
uint r = ftoi_trunc(v.x * 31.0f);
|
||||
uint r = ftoi_trunc(v.x * 31.0f);
|
||||
float r0 = float(((r+0) << 3) | ((r+0) >> 2));
|
||||
float r1 = float(((r+1) << 3) | ((r+1) >> 2));
|
||||
if (fabs(v.x - r1) < fabs(v.x - r0)) r = min(r+1, 31U);
|
||||
r = (r << 3) | (r >> 2);
|
||||
r = (r << 3) | (r >> 2);
|
||||
|
||||
uint g = ftoi_trunc(v.y * 63.0f);
|
||||
uint g = ftoi_trunc(v.y * 63.0f);
|
||||
float g0 = float(((g+0) << 2) | ((g+0) >> 4));
|
||||
float g1 = float(((g+1) << 2) | ((g+1) >> 4));
|
||||
if (fabs(v.y - g1) < fabs(v.y - g0)) g = min(g+1, 63U);
|
||||
@ -428,8 +355,8 @@ inline Vector3 round565(const Vector3 & v) {
|
||||
float b0 = float(((b+0) << 3) | ((b+0) >> 2));
|
||||
float b1 = float(((b+1) << 3) | ((b+1) >> 2));
|
||||
if (fabs(v.z - b1) < fabs(v.z - b0)) b = min(b+1, 31U);
|
||||
|
||||
b = (b << 3) | (b >> 2);
|
||||
|
||||
b = (b << 3) | (b >> 2);
|
||||
|
||||
return Vector3(float(r)/255, float(g)/255, float(b)/255);
|
||||
}
|
||||
|
@ -50,7 +50,8 @@ void CompressionOptions::reset()
|
||||
m.format = Format_DXT1;
|
||||
m.quality = Quality_Normal;
|
||||
m.colorWeight.set(1.0f, 1.0f, 1.0f, 1.0f);
|
||||
|
||||
m.rgbmThreshold = 0.15f;
|
||||
|
||||
m.bitcount = 32;
|
||||
m.bmask = 0x000000FF;
|
||||
m.gmask = 0x0000FF00;
|
||||
@ -102,6 +103,11 @@ void CompressionOptions::setColorWeights(float red, float green, float blue, flo
|
||||
m.colorWeight.set(red, green, blue, alpha);
|
||||
}
|
||||
|
||||
void CompressionOptions::setRGBMThreshold(float min_m)
|
||||
{
|
||||
m.rgbmThreshold = min_m;
|
||||
}
|
||||
|
||||
|
||||
/// Set color mask to describe the RGB/RGBA format.
|
||||
void CompressionOptions::setPixelFormat(uint bitCount, uint rmask, uint gmask, uint bmask, uint amask)
|
||||
@ -162,7 +168,7 @@ void CompressionOptions::setPixelType(PixelType pixelType)
|
||||
/// Set pitch alignment in bytes.
|
||||
void CompressionOptions::setPitchAlignment(int pitchAlignment)
|
||||
{
|
||||
nvDebugCheck(pitchAlignment > 0 && isPowerOfTwo(pitchAlignment));
|
||||
nvDebugCheck(pitchAlignment > 0 && isPowerOfTwo(U32(pitchAlignment)));
|
||||
m.pitchAlignment = pitchAlignment;
|
||||
}
|
||||
|
||||
@ -194,6 +200,10 @@ void CompressionOptions::setTargetDecoder(Decoder decoder)
|
||||
}
|
||||
|
||||
|
||||
Format CompressionOptions::format() const
|
||||
{
|
||||
return m.format;
|
||||
}
|
||||
|
||||
// Translate to and from D3D formats.
|
||||
unsigned int CompressionOptions::d3d9Format() const
|
||||
@ -246,10 +256,20 @@ unsigned int CompressionOptions::d3d9Format() const
|
||||
FOURCC_ATI2, // Format_BC5
|
||||
FOURCC_DXT1, // Format_DXT1n
|
||||
0, // Format_CTX1
|
||||
MAKEFOURCC('B', 'C', '6', 'H'), // Format_BC6
|
||||
MAKEFOURCC('B', 'C', '7', 'L'), // Format_BC7
|
||||
//FOURCC_ATI2, // Format_BC5_Luma
|
||||
FOURCC_DXT5, // Format_BC3_RGBM
|
||||
FOURCC_BC6H, // Format_BC6
|
||||
FOURCC_BC7L, // Format_BC7
|
||||
FOURCC_DXT5, // Format_BC3_RGBM
|
||||
NV_MAKEFOURCC('E', 'T', 'C', '1'), // Format_ETC1
|
||||
0, // Format_ETC2_R
|
||||
0, // Format_ETC2_RG
|
||||
NV_MAKEFOURCC('E', 'T', 'C', '2'), // Format_ETC2_RGB
|
||||
0, // Format_ETC2_RGBA
|
||||
0, // Format_ETC2_RGB_A1
|
||||
0, // Format_ETC2_RGBM
|
||||
FOURCC_PVR0,
|
||||
FOURCC_PVR1,
|
||||
FOURCC_PVR2,
|
||||
FOURCC_PVR3,
|
||||
};
|
||||
|
||||
NV_COMPILER_CHECK(NV_ARRAY_SIZE(d3d9_formats) == Format_Count);
|
||||
@ -258,12 +278,80 @@ unsigned int CompressionOptions::d3d9Format() const
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
bool CompressionOptions::setDirect3D9Format(unsigned int format)
|
||||
unsigned int CompressionOptions::dxgiFormat() const // @@ Add srgb flag.
|
||||
{
|
||||
if (m.format == Format_RGB) {
|
||||
if (m.pixelType == PixelType_UnsignedNorm) {
|
||||
|
||||
uint bitcount = m.bitcount;
|
||||
uint rmask = m.rmask;
|
||||
uint gmask = m.gmask;
|
||||
uint bmask = m.bmask;
|
||||
uint amask = m.amask;
|
||||
|
||||
if (bitcount == 0) {
|
||||
bitcount = m.rsize + m.gsize + m.bsize + m.asize;
|
||||
rmask = ((1 << m.rsize) - 1) << (m.asize + m.bsize + m.gsize);
|
||||
gmask = ((1 << m.gsize) - 1) << (m.asize + m.bsize);
|
||||
bmask = ((1 << m.bsize) - 1) << m.asize;
|
||||
amask = ((1 << m.asize) - 1) << 0;
|
||||
}
|
||||
|
||||
if (bitcount <= 32) {
|
||||
return nv::findDXGIFormat(bitcount, rmask, gmask, bmask, amask);
|
||||
}
|
||||
else {
|
||||
if (m.rsize == 16 && m.gsize == 16 && m.bsize == 0 && m.asize == 0) return DXGI_FORMAT_R16G16_UNORM;
|
||||
if (m.rsize == 16 && m.gsize == 16 && m.bsize == 16 && m.asize == 16) return DXGI_FORMAT_R16G16B16A16_UNORM;
|
||||
}
|
||||
}
|
||||
else if (m.pixelType == PixelType_Float) {
|
||||
if (m.rsize == 16 && m.gsize == 0 && m.bsize == 0 && m.asize == 0) return DXGI_FORMAT_R16_FLOAT;
|
||||
if (m.rsize == 32 && m.gsize == 0 && m.bsize == 0 && m.asize == 0) return DXGI_FORMAT_R32_FLOAT;
|
||||
if (m.rsize == 16 && m.gsize == 16 && m.bsize == 0 && m.asize == 0) return DXGI_FORMAT_R16G16_FLOAT;
|
||||
if (m.rsize == 32 && m.gsize == 32 && m.bsize == 0 && m.asize == 0) return DXGI_FORMAT_R32G32_FLOAT;
|
||||
if (m.rsize == 16 && m.gsize == 16 && m.bsize == 16 && m.asize == 16) return DXGI_FORMAT_R16G16B16A16_FLOAT;
|
||||
if (m.rsize == 32 && m.gsize == 32 && m.bsize == 32 && m.asize == 32) return DXGI_FORMAT_R32G32B32A32_FLOAT;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
else {
|
||||
uint dxgi_formats[] = {
|
||||
0, // Format_RGB,
|
||||
DXGI_FORMAT_BC1_UNORM, // Format_DXT1
|
||||
DXGI_FORMAT_BC1_UNORM, // Format_DXT1a
|
||||
DXGI_FORMAT_BC2_UNORM, // Format_DXT3
|
||||
DXGI_FORMAT_BC3_UNORM, // Format_DXT5
|
||||
DXGI_FORMAT_BC3_UNORM, // Format_DXT5n
|
||||
DXGI_FORMAT_BC4_UNORM, // Format_BC4
|
||||
DXGI_FORMAT_BC5_UNORM, // Format_BC5
|
||||
DXGI_FORMAT_BC1_UNORM, // Format_DXT1n
|
||||
0, // Format_CTX1
|
||||
DXGI_FORMAT_BC6H_UF16, // Format_BC6
|
||||
DXGI_FORMAT_BC7_UNORM, // Format_BC7
|
||||
DXGI_FORMAT_BC5_UNORM, // Format_BC3_RGBM
|
||||
0, // Format_ETC1
|
||||
0, // Format_ETC2_R
|
||||
0, // Format_ETC2_RG
|
||||
0, // Format_ETC2_RGB
|
||||
0, // Format_ETC2_RGBA
|
||||
0, // Format_ETC2_RGB_A1
|
||||
0, // Format_ETC2_RGBM
|
||||
0, // Format_PVR_2BPP_RGB
|
||||
0, // Format_PVR_4BPP_RGB
|
||||
0, // Format_PVR_2BPP_RGBA
|
||||
0, // Format_PVR_4BPP_RGB
|
||||
};
|
||||
|
||||
NV_COMPILER_CHECK(NV_ARRAY_SIZE(dxgi_formats) == Format_Count);
|
||||
|
||||
return dxgi_formats[m.format];
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int CompressionOptions::dxgiFormat() const
|
||||
/*
|
||||
bool CompressionOptions::setDirect3D9Format(unsigned int format)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -39,7 +39,8 @@ namespace nvtt
|
||||
Quality quality;
|
||||
|
||||
nv::Vector4 colorWeight;
|
||||
|
||||
float rgbmThreshold;
|
||||
|
||||
// Pixel format description.
|
||||
uint bitcount;
|
||||
uint rmask;
|
||||
|
@ -30,6 +30,7 @@
|
||||
|
||||
namespace nv
|
||||
{
|
||||
|
||||
struct CompressorInterface
|
||||
{
|
||||
virtual ~CompressorInterface() {}
|
||||
|
@ -39,7 +39,7 @@ using namespace nv;
|
||||
using namespace nvtt;
|
||||
|
||||
|
||||
void CompressorBC6::compressBlock(const Vector4 colors[16], const float weights[16], const CompressionOptions::Private & compressionOptions, void * output)
|
||||
void CompressorBC6::compressBlock(Vector4 colors[16], float weights[16], const CompressionOptions::Private & compressionOptions, void * output)
|
||||
{
|
||||
// !!!UNDONE: support channel weights
|
||||
// !!!UNDONE: set flags once, not per block (this is especially sketchy since block compression is multithreaded...)
|
||||
@ -77,7 +77,7 @@ void CompressorBC6::compressBlock(const Vector4 colors[16], const float weights[
|
||||
ZOH::compress(zohTile, (char *)output);
|
||||
}
|
||||
|
||||
void CompressorBC7::compressBlock(const Vector4 colors[16], const float weights[16], const CompressionOptions::Private & compressionOptions, void * output)
|
||||
void CompressorBC7::compressBlock(Vector4 colors[16], float weights[16], const CompressionOptions::Private & compressionOptions, void * output)
|
||||
{
|
||||
// !!!UNDONE: support channel weights
|
||||
// !!!UNDONE: set flags once, not per block (this is especially sketchy since block compression is multithreaded...)
|
||||
|
@ -30,14 +30,14 @@ namespace nv
|
||||
{
|
||||
struct CompressorBC6 : public FloatColorCompressor
|
||||
{
|
||||
virtual void compressBlock(const Vector4 colors[16], const float weights[16], const nvtt::CompressionOptions::Private & compressionOptions, void * output);
|
||||
virtual uint blockSize() const { return 16; }
|
||||
virtual void compressBlock(Vector4 colors[16], float weights[16], const nvtt::CompressionOptions::Private & compressionOptions, void * output);
|
||||
virtual uint blockSize(const nvtt::CompressionOptions::Private & ) const { return 16; }
|
||||
};
|
||||
|
||||
struct CompressorBC7 : public FloatColorCompressor
|
||||
{
|
||||
virtual void compressBlock(const Vector4 colors[16], const float weights[16], const nvtt::CompressionOptions::Private & compressionOptions, void * output);
|
||||
virtual uint blockSize() const { return 16; }
|
||||
virtual void compressBlock(Vector4 colors[16], float weights[16], const nvtt::CompressionOptions::Private & compressionOptions, void * output);
|
||||
virtual uint blockSize(const nvtt::CompressionOptions::Private & ) const { return 16; }
|
||||
};
|
||||
|
||||
} // nv namespace
|
||||
|
@ -28,7 +28,7 @@
|
||||
#include "CompressionOptions.h"
|
||||
#include "OutputOptions.h"
|
||||
#include "ClusterFit.h"
|
||||
#include "CompressorDXT1.h"
|
||||
//#include "CompressorDXT1.h"
|
||||
#include "CompressorDXT5_RGBM.h"
|
||||
|
||||
// squish
|
||||
@ -48,45 +48,11 @@
|
||||
|
||||
#include <new> // placement new
|
||||
|
||||
// s3_quant
|
||||
#if defined(HAVE_S3QUANT)
|
||||
#include "s3tc/s3_quant.h"
|
||||
#endif
|
||||
|
||||
// ati tc
|
||||
#if defined(HAVE_ATITC)
|
||||
typedef int BOOL;
|
||||
typedef _W64 unsigned long ULONG_PTR;
|
||||
typedef ULONG_PTR DWORD_PTR;
|
||||
#include "atitc/ATI_Compress.h"
|
||||
#endif
|
||||
|
||||
// squish
|
||||
#if defined(HAVE_SQUISH)
|
||||
//#include "squish/squish.h"
|
||||
#include "squish-1.10/squish.h"
|
||||
#endif
|
||||
|
||||
// d3dx
|
||||
#if defined(HAVE_D3DX)
|
||||
#include <d3dx9.h>
|
||||
#endif
|
||||
|
||||
// stb
|
||||
#if defined(HAVE_STB)
|
||||
#define STB_DEFINE
|
||||
#include "stb/stb_dxt.h"
|
||||
#endif
|
||||
|
||||
using namespace nv;
|
||||
using namespace nvtt;
|
||||
|
||||
|
||||
void FastCompressorDXT1::compressBlock(ColorBlock & rgba, nvtt::AlphaMode alphaMode, const nvtt::CompressionOptions::Private & compressionOptions, void * output)
|
||||
{
|
||||
BlockDXT1 * block = new(output) BlockDXT1;
|
||||
QuickCompress::compressDXT1(rgba, block);
|
||||
}
|
||||
|
||||
void FastCompressorDXT1a::compressBlock(ColorBlock & rgba, nvtt::AlphaMode alphaMode, const nvtt::CompressionOptions::Private & compressionOptions, void * output)
|
||||
{
|
||||
@ -115,39 +81,13 @@ void FastCompressorDXT5n::compressBlock(ColorBlock & rgba, nvtt::AlphaMode alpha
|
||||
}
|
||||
|
||||
|
||||
#if 1
|
||||
|
||||
void CompressorDXT1::compressBlock(const Vector4 colors[16], const float weights[16], const nvtt::CompressionOptions::Private & compressionOptions, void * output)
|
||||
{
|
||||
compress_dxt1(colors, weights, compressionOptions.colorWeight.xyz(), /*three_color_mode*/true, (BlockDXT1 *)output);
|
||||
}
|
||||
|
||||
#else
|
||||
void CompressorDXT1::compressBlock(ColorBlock & rgba, nvtt::AlphaMode alphaMode, const nvtt::CompressionOptions::Private & compressionOptions, void * output)
|
||||
{
|
||||
nvsquish::WeightedClusterFit fit;
|
||||
fit.SetMetric(compressionOptions.colorWeight.x, compressionOptions.colorWeight.y, compressionOptions.colorWeight.z);
|
||||
|
||||
if (rgba.isSingleColor())
|
||||
{
|
||||
BlockDXT1 * block = new(output) BlockDXT1;
|
||||
OptimalCompress::compressDXT1(rgba.color(0), block);
|
||||
}
|
||||
else
|
||||
{
|
||||
nvsquish::ColourSet colours((uint8 *)rgba.colors(), 0);
|
||||
fit.SetColourSet(&colours, nvsquish::kDxt1);
|
||||
fit.Compress(output);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void CompressorDXT1a::compressBlock(ColorBlock & rgba, nvtt::AlphaMode alphaMode, const nvtt::CompressionOptions::Private & compressionOptions, void * output)
|
||||
{
|
||||
uint alphaMask = 0;
|
||||
for (uint i = 0; i < 16; i++)
|
||||
{
|
||||
if (rgba.color(i).a == 0) alphaMask |= (3 << (i * 2)); // Set two bits for each color.
|
||||
if (rgba.color(i).a == 0) alphaMask |= (3U << (i * 2U)); // Set two bits for each color.
|
||||
}
|
||||
|
||||
const bool isSingleColor = rgba.isSingleColor();
|
||||
@ -284,216 +224,6 @@ void CompressorDXT5n::compressBlock(ColorBlock & rgba, nvtt::AlphaMode alphaMode
|
||||
}
|
||||
|
||||
|
||||
void CompressorBC3_RGBM::compressBlock(const Vector4 colors[16], const float weights[16], const nvtt::CompressionOptions::Private & compressionOptions, void * output)
|
||||
{
|
||||
float min_m = 0.25f; // @@ Get from compression options.
|
||||
compress_dxt5_rgbm(colors, weights, min_m, (BlockDXT5 *)output);
|
||||
}
|
||||
|
||||
|
||||
#if defined(HAVE_ATITC)
|
||||
|
||||
void AtiCompressorDXT1::compress(nvtt::InputFormat inputFormat, nvtt::AlphaMode alphaMode, uint w, uint h, uint d, void * data, const nvtt::CompressionOptions::Private & compressionOptions, const nvtt::OutputOptions::Private & outputOptions)
|
||||
{
|
||||
nvDebugCheck(d == 1);
|
||||
|
||||
// Init source texture
|
||||
ATI_TC_Texture srcTexture;
|
||||
srcTexture.dwSize = sizeof(srcTexture);
|
||||
srcTexture.dwWidth = w;
|
||||
srcTexture.dwHeight = h;
|
||||
if (inputFormat == nvtt::InputFormat_BGRA_8UB)
|
||||
{
|
||||
srcTexture.dwPitch = w * 4;
|
||||
srcTexture.format = ATI_TC_FORMAT_ARGB_8888;
|
||||
}
|
||||
else
|
||||
{
|
||||
// @@ Floating point input is not swizzled.
|
||||
srcTexture.dwPitch = w * 16;
|
||||
srcTexture.format = ATI_TC_FORMAT_ARGB_32F;
|
||||
}
|
||||
srcTexture.dwDataSize = ATI_TC_CalculateBufferSize(&srcTexture);
|
||||
srcTexture.pData = (ATI_TC_BYTE*) data;
|
||||
|
||||
// Init dest texture
|
||||
ATI_TC_Texture destTexture;
|
||||
destTexture.dwSize = sizeof(destTexture);
|
||||
destTexture.dwWidth = w;
|
||||
destTexture.dwHeight = h;
|
||||
destTexture.dwPitch = 0;
|
||||
destTexture.format = ATI_TC_FORMAT_DXT1;
|
||||
destTexture.dwDataSize = ATI_TC_CalculateBufferSize(&destTexture);
|
||||
destTexture.pData = (ATI_TC_BYTE*) mem::malloc(destTexture.dwDataSize);
|
||||
|
||||
ATI_TC_CompressOptions options;
|
||||
options.dwSize = sizeof(options);
|
||||
options.bUseChannelWeighting = false;
|
||||
options.bUseAdaptiveWeighting = false;
|
||||
options.bDXT1UseAlpha = false;
|
||||
options.nCompressionSpeed = ATI_TC_Speed_Normal;
|
||||
options.bDisableMultiThreading = false;
|
||||
//options.bDisableMultiThreading = true;
|
||||
|
||||
// Compress
|
||||
ATI_TC_ConvertTexture(&srcTexture, &destTexture, &options, NULL, NULL, NULL);
|
||||
|
||||
if (outputOptions.outputHandler != NULL) {
|
||||
outputOptions.outputHandler->writeData(destTexture.pData, destTexture.dwDataSize);
|
||||
}
|
||||
|
||||
mem::free(destTexture.pData);
|
||||
}
|
||||
|
||||
void AtiCompressorDXT5::compress(nvtt::InputFormat inputFormat, nvtt::AlphaMode alphaMode, uint w, uint h, uint d, void * data, const nvtt::CompressionOptions::Private & compressionOptions, const nvtt::OutputOptions::Private & outputOptions)
|
||||
{
|
||||
nvDebugCheck(d == 1);
|
||||
|
||||
// Init source texture
|
||||
ATI_TC_Texture srcTexture;
|
||||
srcTexture.dwSize = sizeof(srcTexture);
|
||||
srcTexture.dwWidth = w;
|
||||
srcTexture.dwHeight = h;
|
||||
if (inputFormat == nvtt::InputFormat_BGRA_8UB)
|
||||
{
|
||||
srcTexture.dwPitch = w * 4;
|
||||
srcTexture.format = ATI_TC_FORMAT_ARGB_8888;
|
||||
}
|
||||
else
|
||||
{
|
||||
srcTexture.dwPitch = w * 16;
|
||||
srcTexture.format = ATI_TC_FORMAT_ARGB_32F;
|
||||
}
|
||||
srcTexture.dwDataSize = ATI_TC_CalculateBufferSize(&srcTexture);
|
||||
srcTexture.pData = (ATI_TC_BYTE*) data;
|
||||
|
||||
// Init dest texture
|
||||
ATI_TC_Texture destTexture;
|
||||
destTexture.dwSize = sizeof(destTexture);
|
||||
destTexture.dwWidth = w;
|
||||
destTexture.dwHeight = h;
|
||||
destTexture.dwPitch = 0;
|
||||
destTexture.format = ATI_TC_FORMAT_DXT5;
|
||||
destTexture.dwDataSize = ATI_TC_CalculateBufferSize(&destTexture);
|
||||
destTexture.pData = (ATI_TC_BYTE*) mem::malloc(destTexture.dwDataSize);
|
||||
|
||||
// Compress
|
||||
ATI_TC_ConvertTexture(&srcTexture, &destTexture, NULL, NULL, NULL, NULL);
|
||||
|
||||
if (outputOptions.outputHandler != NULL) {
|
||||
outputOptions.outputHandler->writeData(destTexture.pData, destTexture.dwDataSize);
|
||||
}
|
||||
|
||||
mem::free(destTexture.pData);
|
||||
}
|
||||
|
||||
#endif // defined(HAVE_ATITC)
|
||||
|
||||
#if defined(HAVE_SQUISH)
|
||||
|
||||
void SquishCompressorDXT1::compress(nvtt::InputFormat inputFormat, nvtt::AlphaMode alphaMode, uint w, uint h, uint d, void * data, const nvtt::CompressionOptions::Private & compressionOptions, const nvtt::OutputOptions::Private & outputOptions)
|
||||
{
|
||||
nvDebugCheck(d == 1);
|
||||
nvDebugCheck(false);
|
||||
|
||||
#pragma message(NV_FILE_LINE "TODO: Convert input to fixed point ABGR format instead of ARGB")
|
||||
/*
|
||||
Image img(*image);
|
||||
int count = img.width() * img.height();
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
Color32 c = img.pixel(i);
|
||||
img.pixel(i) = Color32(c.b, c.g, c.r, c.a);
|
||||
}
|
||||
|
||||
int size = squish::GetStorageRequirements(img.width(), img.height(), squish::kDxt1);
|
||||
void * blocks = mem::malloc(size);
|
||||
|
||||
squish::CompressImage((const squish::u8 *)img.pixels(), img.width(), img.height(), blocks, squish::kDxt1 | squish::kColourClusterFit);
|
||||
|
||||
if (outputOptions.outputHandler != NULL) {
|
||||
outputOptions.outputHandler->writeData(blocks, size);
|
||||
}
|
||||
|
||||
mem::free(blocks);
|
||||
*/
|
||||
}
|
||||
|
||||
#endif // defined(HAVE_SQUISH)
|
||||
|
||||
|
||||
#if defined(HAVE_D3DX)
|
||||
|
||||
void D3DXCompressorDXT1::compress(nvtt::InputFormat inputFormat, nvtt::AlphaMode alphaMode, uint w, uint h, uint d, void * data, const nvtt::CompressionOptions::Private & compressionOptions, const nvtt::OutputOptions::Private & outputOptions)
|
||||
{
|
||||
nvDebugCheck(d == 1);
|
||||
|
||||
IDirect3D9 * d3d = Direct3DCreate9(D3D_SDK_VERSION);
|
||||
|
||||
D3DPRESENT_PARAMETERS presentParams;
|
||||
ZeroMemory(&presentParams, sizeof(presentParams));
|
||||
presentParams.Windowed = TRUE;
|
||||
presentParams.SwapEffect = D3DSWAPEFFECT_COPY;
|
||||
presentParams.BackBufferWidth = 8;
|
||||
presentParams.BackBufferHeight = 8;
|
||||
presentParams.BackBufferFormat = D3DFMT_UNKNOWN;
|
||||
|
||||
HRESULT err;
|
||||
|
||||
IDirect3DDevice9 * device = NULL;
|
||||
err = d3d->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_REF, GetDesktopWindow(), D3DCREATE_SOFTWARE_VERTEXPROCESSING, &presentParams, &device);
|
||||
|
||||
IDirect3DTexture9 * texture = NULL;
|
||||
err = D3DXCreateTexture(device, w, h, 1, 0, D3DFMT_DXT1, D3DPOOL_SYSTEMMEM, &texture);
|
||||
|
||||
IDirect3DSurface9 * surface = NULL;
|
||||
err = texture->GetSurfaceLevel(0, &surface);
|
||||
|
||||
RECT rect;
|
||||
rect.left = 0;
|
||||
rect.top = 0;
|
||||
rect.bottom = h;
|
||||
rect.right = w;
|
||||
|
||||
if (inputFormat == nvtt::InputFormat_BGRA_8UB)
|
||||
{
|
||||
err = D3DXLoadSurfaceFromMemory(surface, NULL, NULL, data, D3DFMT_A8R8G8B8, w * 4, NULL, &rect, D3DX_DEFAULT, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
err = D3DXLoadSurfaceFromMemory(surface, NULL, NULL, data, D3DFMT_A32B32G32R32F, w * 16, NULL, &rect, D3DX_DEFAULT, 0);
|
||||
}
|
||||
|
||||
if (err != D3DERR_INVALIDCALL && err != D3DXERR_INVALIDDATA)
|
||||
{
|
||||
D3DLOCKED_RECT rect;
|
||||
ZeroMemory(&rect, sizeof(rect));
|
||||
|
||||
err = surface->LockRect(&rect, NULL, D3DLOCK_READONLY);
|
||||
|
||||
if (outputOptions.outputHandler != NULL) {
|
||||
int size = rect.Pitch * ((h + 3) / 4);
|
||||
outputOptions.outputHandler->writeData(rect.pBits, size);
|
||||
}
|
||||
|
||||
err = surface->UnlockRect();
|
||||
}
|
||||
|
||||
surface->Release();
|
||||
device->Release();
|
||||
d3d->Release();
|
||||
}
|
||||
|
||||
#endif // defined(HAVE_D3DX)
|
||||
|
||||
|
||||
#if defined(HAVE_STB)
|
||||
|
||||
void StbCompressorDXT1::compressBlock(ColorBlock & rgba, nvtt::AlphaMode alphaMode, const nvtt::CompressionOptions::Private & compressionOptions, void * output)
|
||||
{
|
||||
rgba.swizzle(2, 1, 0, 3); // Swap R and B
|
||||
stb_compress_dxt_block((unsigned char *)output, (unsigned char *)rgba.colors(), 0, 0);
|
||||
}
|
||||
|
||||
|
||||
#endif // defined(HAVE_STB)
|
||||
|
@ -32,12 +32,6 @@ namespace nv
|
||||
struct ColorBlock;
|
||||
|
||||
// Fast CPU compressors.
|
||||
struct FastCompressorDXT1 : public ColorBlockCompressor
|
||||
{
|
||||
virtual void compressBlock(ColorBlock & rgba, nvtt::AlphaMode alphaMode, const nvtt::CompressionOptions::Private & compressionOptions, void * output);
|
||||
virtual uint blockSize() const { return 8; }
|
||||
};
|
||||
|
||||
struct FastCompressorDXT1a : public ColorBlockCompressor
|
||||
{
|
||||
virtual void compressBlock(ColorBlock & rgba, nvtt::AlphaMode alphaMode, const nvtt::CompressionOptions::Private & compressionOptions, void * output);
|
||||
@ -64,19 +58,6 @@ namespace nv
|
||||
|
||||
|
||||
// Normal CPU compressors.
|
||||
#if 1
|
||||
struct CompressorDXT1 : public FloatColorCompressor
|
||||
{
|
||||
virtual void compressBlock(const Vector4 colors[16], const float weights[16], const nvtt::CompressionOptions::Private & compressionOptions, void * output);
|
||||
virtual uint blockSize() const { return 8; }
|
||||
};
|
||||
#else
|
||||
struct CompressorDXT1 : public ColorBlockCompressor
|
||||
{
|
||||
virtual void compressBlock(ColorBlock & rgba, nvtt::AlphaMode alphaMode, const nvtt::CompressionOptions::Private & compressionOptions, void * output);
|
||||
virtual uint blockSize() const { return 8; }
|
||||
};
|
||||
#endif
|
||||
|
||||
struct CompressorDXT1a : public ColorBlockCompressor
|
||||
{
|
||||
@ -108,47 +89,9 @@ namespace nv
|
||||
virtual uint blockSize() const { return 16; }
|
||||
};
|
||||
|
||||
struct CompressorBC3_RGBM : public FloatColorCompressor
|
||||
{
|
||||
virtual void compressBlock(const Vector4 colors[16], const float weights[16], const nvtt::CompressionOptions::Private & compressionOptions, void * output);
|
||||
virtual uint blockSize() const { return 16; }
|
||||
};
|
||||
|
||||
|
||||
// External compressors.
|
||||
#if defined(HAVE_ATITC)
|
||||
struct AtiCompressorDXT1 : public CompressorInterface
|
||||
{
|
||||
virtual void compress(nvtt::InputFormat inputFormat, nvtt::AlphaMode alphaMode, uint w, uint h, uint d, void * data, const nvtt::CompressionOptions::Private & compressionOptions, const nvtt::OutputOptions::Private & outputOptions);
|
||||
};
|
||||
|
||||
struct AtiCompressorDXT5 : public CompressorInterface
|
||||
{
|
||||
virtual void compress(nvtt::InputFormat inputFormat, nvtt::AlphaMode alphaMode, uint w, uint h, uint d, void * data, const nvtt::CompressionOptions::Private & compressionOptions, const nvtt::OutputOptions::Private & outputOptions);
|
||||
};
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_SQUISH)
|
||||
struct SquishCompressorDXT1 : public CompressorInterface
|
||||
{
|
||||
virtual void compress(nvtt::InputFormat inputFormat, nvtt::AlphaMode alphaMode, uint w, uint h, uint d, void * data, const nvtt::CompressionOptions::Private & compressionOptions, const nvtt::OutputOptions::Private & outputOptions);
|
||||
};
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_D3DX)
|
||||
struct D3DXCompressorDXT1 : public CompressorInterface
|
||||
{
|
||||
virtual void compress(nvtt::InputFormat inputFormat, nvtt::AlphaMode alphaMode, uint w, uint h, uint d, void * data, const nvtt::CompressionOptions::Private & compressionOptions, const nvtt::OutputOptions::Private & outputOptions);
|
||||
};
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_STB)
|
||||
struct StbCompressorDXT1 : public ColorBlockCompressor
|
||||
{
|
||||
virtual void compressBlock(ColorBlock & rgba, nvtt::AlphaMode alphaMode, const nvtt::CompressionOptions::Private & compressionOptions, void * output);
|
||||
virtual uint blockSize() const { return 8; }
|
||||
};
|
||||
#endif
|
||||
|
||||
} // nv namespace
|
||||
|
||||
|
@ -218,13 +218,13 @@ static int evaluate_mse(const Color32 & p, const Color32 & c) {
|
||||
return (square(int(p.r)-c.r) + square(int(p.g)-c.g) + square(int(p.b)-c.b));
|
||||
}
|
||||
|
||||
static float evaluate_mse(const Vector3 palette[4], const Vector3 & c, const Vector3 & w) {
|
||||
/*static float evaluate_mse(const Vector3 palette[4], const Vector3 & c, const Vector3 & w) {
|
||||
float e0 = evaluate_mse(palette[0], c, w);
|
||||
float e1 = evaluate_mse(palette[1], c, w);
|
||||
float e2 = evaluate_mse(palette[2], c, w);
|
||||
float e3 = evaluate_mse(palette[3], c, w);
|
||||
return min(min(e0, e1), min(e2, e3));
|
||||
}
|
||||
}*/
|
||||
|
||||
static int evaluate_mse(const Color32 palette[4], const Color32 & c) {
|
||||
int e0 = evaluate_mse(palette[0], c);
|
||||
@ -245,12 +245,12 @@ static int evaluate_mse(const BlockDXT1 * output, Color32 color, int index) {
|
||||
// Returns weighted MSE error in [0-255] range.
|
||||
static float evaluate_palette_error(Color32 palette[4], const Color32 * colors, const float * weights, int count) {
|
||||
|
||||
float total = 0.0f;
|
||||
for (int i = 0; i < count; i++) {
|
||||
float total = 0.0f;
|
||||
for (int i = 0; i < count; i++) {
|
||||
total += weights[i] * evaluate_mse(palette, colors[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return total;
|
||||
return total;
|
||||
}
|
||||
|
||||
#if 0
|
||||
@ -337,7 +337,7 @@ static void evaluate_palette(Color16 c0, Color16 c1, Vector3 palette[4]) {
|
||||
}
|
||||
}
|
||||
|
||||
static void evaluate_palette3(Color16 c0, Color16 c1, Vector3 palette[4]) {
|
||||
/*static void evaluate_palette3(Color16 c0, Color16 c1, Vector3 palette[4]) {
|
||||
nvDebugCheck(c0.u > c1.u);
|
||||
|
||||
Color32 palette32[4];
|
||||
@ -346,7 +346,7 @@ static void evaluate_palette3(Color16 c0, Color16 c1, Vector3 palette[4]) {
|
||||
for (int i = 0; i < 4; i++) {
|
||||
palette[i] = color_to_vector3(palette32[i]);
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
|
||||
|
||||
@ -355,38 +355,38 @@ static void evaluate_palette3(Color16 c0, Color16 c1, Vector3 palette[4]) {
|
||||
static uint compute_indices4(const Vector4 input_colors[16], const Vector3 & color_weights, const Vector3 palette[4]) {
|
||||
|
||||
uint indices = 0;
|
||||
for (int i = 0; i < 16; i++) {
|
||||
float d0 = evaluate_mse(palette[0], input_colors[i].xyz(), color_weights);
|
||||
float d1 = evaluate_mse(palette[1], input_colors[i].xyz(), color_weights);
|
||||
float d2 = evaluate_mse(palette[2], input_colors[i].xyz(), color_weights);
|
||||
float d3 = evaluate_mse(palette[3], input_colors[i].xyz(), color_weights);
|
||||
|
||||
uint b0 = d0 > d3;
|
||||
uint b1 = d1 > d2;
|
||||
uint b2 = d0 > d2;
|
||||
uint b3 = d1 > d3;
|
||||
uint b4 = d2 > d3;
|
||||
|
||||
uint x0 = b1 & b2;
|
||||
uint x1 = b0 & b3;
|
||||
uint x2 = b0 & b4;
|
||||
|
||||
indices |= (x2 | ((x0 | x1) << 1)) << (2 * i);
|
||||
}
|
||||
for (int i = 0; i < 16; i++) {
|
||||
float d0 = evaluate_mse(palette[0], input_colors[i].xyz(), color_weights);
|
||||
float d1 = evaluate_mse(palette[1], input_colors[i].xyz(), color_weights);
|
||||
float d2 = evaluate_mse(palette[2], input_colors[i].xyz(), color_weights);
|
||||
float d3 = evaluate_mse(palette[3], input_colors[i].xyz(), color_weights);
|
||||
|
||||
return indices;
|
||||
uint b0 = d0 > d3;
|
||||
uint b1 = d1 > d2;
|
||||
uint b2 = d0 > d2;
|
||||
uint b3 = d1 > d3;
|
||||
uint b4 = d2 > d3;
|
||||
|
||||
uint x0 = b1 & b2;
|
||||
uint x1 = b0 & b3;
|
||||
uint x2 = b0 & b4;
|
||||
|
||||
indices |= (x2 | ((x0 | x1) << 1)) << (2 * i);
|
||||
}
|
||||
|
||||
return indices;
|
||||
}
|
||||
|
||||
|
||||
static uint compute_indices(const Vector4 input_colors[16], const Vector3 & color_weights, const Vector3 palette[4]) {
|
||||
|
||||
uint indices = 0;
|
||||
for (int i = 0; i < 16; i++) {
|
||||
float d0 = evaluate_mse(palette[0], input_colors[i].xyz(), color_weights);
|
||||
float d1 = evaluate_mse(palette[1], input_colors[i].xyz(), color_weights);
|
||||
float d2 = evaluate_mse(palette[2], input_colors[i].xyz(), color_weights);
|
||||
float d3 = evaluate_mse(palette[3], input_colors[i].xyz(), color_weights);
|
||||
|
||||
for (int i = 0; i < 16; i++) {
|
||||
float d0 = evaluate_mse(palette[0], input_colors[i].xyz(), color_weights);
|
||||
float d1 = evaluate_mse(palette[1], input_colors[i].xyz(), color_weights);
|
||||
float d2 = evaluate_mse(palette[2], input_colors[i].xyz(), color_weights);
|
||||
float d3 = evaluate_mse(palette[3], input_colors[i].xyz(), color_weights);
|
||||
|
||||
uint index;
|
||||
if (d0 < d1 && d0 < d2 && d0 < d3) index = 0;
|
||||
else if (d1 < d2 && d1 < d3) index = 1;
|
||||
@ -491,7 +491,8 @@ float nv::compress_dxt1_single_color(const Vector3 * colors, const float * weigh
|
||||
|
||||
// Decompress block color.
|
||||
Color32 palette[4];
|
||||
output->evaluatePalette(palette, /*d3d9=*/false);
|
||||
evaluate_palette(output->col0, output->col1, palette);
|
||||
//output->evaluatePalette(palette, /*d3d9=*/false);
|
||||
|
||||
Vector3 block_color = color_to_vector3(palette[output->indices & 0x3]);
|
||||
|
||||
@ -668,7 +669,7 @@ float nv::compress_dxt1(const Vector4 input_colors[16], const float input_weight
|
||||
|
||||
// This is too expensive, even with a low threshold.
|
||||
// If high quality:
|
||||
if (0) {
|
||||
if (/* DISABLES CODE */ (0)) {
|
||||
BlockDXT1 exhaustive_output;
|
||||
float exhaustive_error = compress_dxt1_bounding_box_exhaustive(input_colors, colors, weights, count, color_weights, three_color_mode, 1400, &exhaustive_output);
|
||||
|
||||
@ -720,7 +721,7 @@ float nv::compress_dxt1(const Vector4 input_colors[16], const float input_weight
|
||||
|
||||
|
||||
// Least squares fitting of color end points for the given indices. @@ Take weights into account.
|
||||
static bool optimize_end_points4(uint indices, const Vector3 * colors, const Vector3 * weights, int count, Vector3 * a, Vector3 * b)
|
||||
static bool optimize_end_points4(uint indices, const Vector4 * colors, /*const float * weights,*/ int count, Vector3 * a, Vector3 * b)
|
||||
{
|
||||
float alpha2_sum = 0.0f;
|
||||
float beta2_sum = 0.0f;
|
||||
@ -739,8 +740,8 @@ static bool optimize_end_points4(uint indices, const Vector3 * colors, const Vec
|
||||
alpha2_sum += alpha * alpha;
|
||||
beta2_sum += beta * beta;
|
||||
alphabeta_sum += alpha * beta;
|
||||
alphax_sum += alpha * colors[i];
|
||||
betax_sum += beta * colors[i];
|
||||
alphax_sum += alpha * colors[i].xyz();
|
||||
betax_sum += beta * colors[i].xyz();
|
||||
}
|
||||
|
||||
float denom = alpha2_sum * beta2_sum - alphabeta_sum * alphabeta_sum;
|
||||
@ -756,7 +757,7 @@ static bool optimize_end_points4(uint indices, const Vector3 * colors, const Vec
|
||||
|
||||
|
||||
// Least squares fitting of color end points for the given indices. @@ This does not support black/transparent index. @@ Take weights into account.
|
||||
static bool optimize_end_points3(uint indices, const Vector3 * colors, const Vector3 * weights, int count, Vector3 * a, Vector3 * b)
|
||||
static bool optimize_end_points3(uint indices, const Vector3 * colors, /*const float * weights,*/ int count, Vector3 * a, Vector3 * b)
|
||||
{
|
||||
float alpha2_sum = 0.0f;
|
||||
float beta2_sum = 0.0f;
|
||||
@ -794,6 +795,90 @@ static bool optimize_end_points3(uint indices, const Vector3 * colors, const Vec
|
||||
|
||||
|
||||
|
||||
// find minimum and maximum colors based on bounding box in color space
|
||||
inline static void fit_colors_bbox(const Vector3 * colors, int count, Vector3 * restrict c0, Vector3 * restrict c1)
|
||||
{
|
||||
*c0 = Vector3(0);
|
||||
*c1 = Vector3(255);
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
*c0 = max(*c0, colors[i]);
|
||||
*c1 = min(*c1, colors[i]);
|
||||
}
|
||||
}
|
||||
|
||||
inline static void select_diagonal(const Vector3 * colors, int count, Vector3 * restrict c0, Vector3 * restrict c1)
|
||||
{
|
||||
Vector3 center = (*c0 + *c1) * 0.5f;
|
||||
|
||||
Vector2 covariance = Vector2(0);
|
||||
for (int i = 0; i < count; i++) {
|
||||
Vector3 t = colors[i] - center;
|
||||
covariance += t.xy() * t.z;
|
||||
}
|
||||
|
||||
float x0 = c0->x;
|
||||
float y0 = c0->y;
|
||||
float x1 = c1->x;
|
||||
float y1 = c1->y;
|
||||
|
||||
if (covariance.x < 0) {
|
||||
swap(x0, x1);
|
||||
}
|
||||
if (covariance.y < 0) {
|
||||
swap(y0, y1);
|
||||
}
|
||||
|
||||
c0->set(x0, y0, c0->z);
|
||||
c1->set(x1, y1, c1->z);
|
||||
}
|
||||
|
||||
inline static void inset_bbox(Vector3 * restrict c0, Vector3 * restrict c1)
|
||||
{
|
||||
Vector3 inset = (*c0 - *c1) / 16.0f - (8.0f / 255.0f) / 16.0f;
|
||||
*c0 = clamp(*c0 - inset, 0.0f, 255.0f);
|
||||
*c1 = clamp(*c1 + inset, 0.0f, 255.0f);
|
||||
}
|
||||
|
||||
|
||||
|
||||
float nv::compress_dxt1_fast(const Vector4 input_colors[16], const float input_weights[16], const Vector3 & color_weights, BlockDXT1 * output)
|
||||
{
|
||||
Vector3 colors[16];
|
||||
float weights[16];
|
||||
int count = reduce_colors(input_colors, input_weights, colors, weights);
|
||||
|
||||
if (count == 0) {
|
||||
// Output trivial block.
|
||||
output->col0.u = 0;
|
||||
output->col1.u = 0;
|
||||
output->indices = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
float error = FLT_MAX;
|
||||
error = compress_dxt1_single_color(colors, weights, count, color_weights, output);
|
||||
|
||||
if (error == 0.0f || count == 1) {
|
||||
// Early out.
|
||||
return error;
|
||||
}
|
||||
|
||||
// Quick end point selection.
|
||||
Vector3 c0, c1;
|
||||
fit_colors_bbox(colors, count, &c0, &c1);
|
||||
select_diagonal(colors, count, &c0, &c1);
|
||||
inset_bbox(&c0, &c1);
|
||||
output_block4(input_colors, color_weights, c0, c1, output);
|
||||
|
||||
// Refine color for the selected indices.
|
||||
if (optimize_end_points4(output->indices, input_colors, 16, &c0, &c1)) {
|
||||
output_block4(input_colors, color_weights, c0, c1, output);
|
||||
}
|
||||
|
||||
return evaluate_mse(input_colors, input_weights, color_weights, output);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
@ -13,11 +13,14 @@ namespace nv {
|
||||
float compress_dxt1_single_color_optimal(const Vector3 & color, BlockDXT1 * output);
|
||||
|
||||
float compress_dxt1_single_color(const Vector3 * colors, const float * weights, int count, const Vector3 & color_weights, BlockDXT1 * output);
|
||||
float compress_dxt1_least_squares_fit(const Vector4 input_colors[16], const Vector3 * colors, const float * weights, int count, const Vector3 & color_weights, BlockDXT1 * output);
|
||||
//float compress_dxt1_least_squares_fit(const Vector4 input_colors[16], const Vector3 * colors, const float * weights, int count, const Vector3 & color_weights, BlockDXT1 * output);
|
||||
float compress_dxt1_bounding_box_exhaustive(const Vector4 input_colors[16], const Vector3 * colors, const float * weights, int count, const Vector3 & color_weights, bool three_color_mode, int search_limit, BlockDXT1 * output);
|
||||
void compress_dxt1_cluster_fit(const Vector4 input_colors[16], const Vector3 * colors, const float * weights, int count, const Vector3 & color_weights, bool three_color_mode, BlockDXT1 * output);
|
||||
|
||||
|
||||
// Cluster fit end point selection.
|
||||
float compress_dxt1(const Vector4 input_colors[16], const float input_weights[16], const Vector3 & color_weights, bool three_color_mode, BlockDXT1 * output);
|
||||
|
||||
// Quick end point selection followed by least squares refinement.
|
||||
float compress_dxt1_fast(const Vector4 input_colors[16], const float input_weights[16], const Vector3 & color_weights, BlockDXT1 * output);
|
||||
|
||||
}
|
||||
|
@ -3,6 +3,7 @@
|
||||
|
||||
#include "OptimalCompressDXT.h"
|
||||
#include "QuickCompressDXT.h"
|
||||
#include "CompressorETC.h"
|
||||
|
||||
#include "nvimage/ColorBlock.h"
|
||||
#include "nvimage/BlockDXT.h"
|
||||
@ -17,38 +18,45 @@
|
||||
|
||||
using namespace nv;
|
||||
|
||||
//static uint atomic_counter = 0;
|
||||
|
||||
|
||||
static void convert_to_rgbm(const Vector4 input_colors[16], const float input_weights[16], float min_m, Vector4 rgbm_colors[16], float rgb_weights[16]) {
|
||||
|
||||
float weight_sum = 0;
|
||||
|
||||
for (uint i = 0; i < 16; i++) {
|
||||
const Vector4 & c = input_colors[i];
|
||||
|
||||
float R = saturate(c.x);
|
||||
float G = saturate(c.y);
|
||||
float B = saturate(c.z);
|
||||
|
||||
float M = max(max(R, G), max(B, min_m));
|
||||
float r = R / M;
|
||||
float g = G / M;
|
||||
float b = B / M;
|
||||
float a = (M - min_m) / (1 - min_m);
|
||||
|
||||
rgbm_colors[i] = Vector4(r, g, b, a);
|
||||
rgb_weights[i] = input_weights[i] * M;
|
||||
weight_sum += input_weights[i];
|
||||
}
|
||||
|
||||
if (weight_sum == 0) {
|
||||
for (uint i = 0; i < 16; i++) rgb_weights[i] = 1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
//static uint atomic_counter = 0;
|
||||
|
||||
float nv::compress_dxt5_rgbm(const Vector4 input_colors[16], const float input_weights[16], float min_m, BlockDXT5 * output) {
|
||||
|
||||
// Convert to RGBM.
|
||||
Vector4 input_colors_rgbm[16]; // @@ Write over input_colors?
|
||||
float rgb_weights[16];
|
||||
|
||||
float weight_sum = 0;
|
||||
|
||||
for (uint i = 0; i < 16; i++) {
|
||||
const Vector4 & c = input_colors[i];
|
||||
|
||||
float R = saturate(c.x);
|
||||
float G = saturate(c.y);
|
||||
float B = saturate(c.z);
|
||||
|
||||
float M = max(max(R, G), max(B, min_m));
|
||||
float r = R / M;
|
||||
float g = G / M;
|
||||
float b = B / M;
|
||||
float a = (M - min_m) / (1 - min_m);
|
||||
|
||||
input_colors_rgbm[i] = Vector4(r, g, b, a);
|
||||
rgb_weights[i] = input_weights[i] * M;
|
||||
weight_sum += input_weights[i];
|
||||
}
|
||||
|
||||
if (weight_sum == 0) {
|
||||
for (uint i = 0; i < 16; i++) rgb_weights[i] = 1;
|
||||
}
|
||||
convert_to_rgbm(input_colors, input_weights, min_m, input_colors_rgbm, rgb_weights);
|
||||
|
||||
// Compress RGB.
|
||||
compress_dxt1(input_colors_rgbm, rgb_weights, Vector3(1), /*three_color_mode=*/false, &output->color);
|
||||
@ -138,291 +146,61 @@ float nv::compress_dxt5_rgbm(const Vector4 input_colors[16], const float input_w
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
#if 0
|
||||
|
||||
BlockDXT5 * block = new(output)BlockDXT5;
|
||||
|
||||
// Decompress the color block and find the M values that reproduce the input most closely. This should compensate for some of the DXT errors.
|
||||
|
||||
// Compress the resulting M values optimally.
|
||||
|
||||
// Repeat this several times until compression error does not improve?
|
||||
|
||||
//Vector3 rgb_block[16];
|
||||
//float m_block[16];
|
||||
|
||||
|
||||
// Init RGB/M block.
|
||||
#if 0
|
||||
nvsquish::WeightedClusterFit fit;
|
||||
|
||||
ColorBlock rgba;
|
||||
for (int i = 0; i < 16; i++) {
|
||||
const Vector4 & c = src.color(i);
|
||||
float R = saturate(c.x);
|
||||
float G = saturate(c.y);
|
||||
float B = saturate(c.z);
|
||||
|
||||
float M = max(max(R, G), max(B, min_m));
|
||||
float r = R / M;
|
||||
float g = G / M;
|
||||
float b = B / M;
|
||||
float a = c.w;
|
||||
|
||||
rgba.color(i) = toColor32(Vector4(r, g, b, a));
|
||||
}
|
||||
|
||||
if (rgba.isSingleColor())
|
||||
{
|
||||
OptimalCompress::compressDXT1(rgba.color(0), &block->color);
|
||||
}
|
||||
else
|
||||
{
|
||||
nvsquish::WeightedClusterFit fit;
|
||||
fit.SetMetric(compressionOptions.colorWeight.x, compressionOptions.colorWeight.y, compressionOptions.colorWeight.z);
|
||||
|
||||
int flags = 0;
|
||||
if (alphaMode == nvtt::AlphaMode_Transparency) flags |= nvsquish::kWeightColourByAlpha;
|
||||
|
||||
nvsquish::ColourSet colours((uint8 *)rgba.colors(), flags);
|
||||
fit.SetColourSet(&colours, 0);
|
||||
fit.Compress(&block->color);
|
||||
}
|
||||
#endif
|
||||
#if 1
|
||||
ColorSet rgb;
|
||||
rgb.allocate(4, 4);
|
||||
|
||||
for (uint i = 0; i < 16; i++) {
|
||||
const Vector4 & c = colors[i];
|
||||
|
||||
float R = saturate(c.x);
|
||||
float G = saturate(c.y);
|
||||
float B = saturate(c.z);
|
||||
|
||||
float M = max(max(R, G), max(B, min_m));
|
||||
float r = R / M;
|
||||
float g = G / M;
|
||||
float b = B / M;
|
||||
float a = c.w;
|
||||
|
||||
rgb.colors[i] = Vector4(r, g, b, a);
|
||||
rgb.indices[i] = i;
|
||||
rgb.weights[i] = max(weights[i], 0.001f);// weights[i]; // IC: For some reason 0 weights are causing problems, even if we eliminate the corresponding colors from the set.
|
||||
}
|
||||
|
||||
rgb.createMinimalSet(/*ignoreTransparent=*/true);
|
||||
|
||||
if (rgb.isSingleColor(/*ignoreAlpha=*/true)) {
|
||||
OptimalCompress::compressDXT1(toColor32(rgb.color(0)), &block->color);
|
||||
}
|
||||
else {
|
||||
ClusterFit fit;
|
||||
fit.setColorWeights(compressionOptions.colorWeight);
|
||||
fit.setColorSet(&rgb);
|
||||
|
||||
Vector3 start, end;
|
||||
fit.compress4(&start, &end);
|
||||
|
||||
QuickCompress::outputBlock4(rgb, start, end, &block->color);
|
||||
}
|
||||
#endif
|
||||
|
||||
float nv::compress_etc2_rgbm(Vector4 input_colors[16], float input_weights[16], float min_m, void * output) {
|
||||
|
||||
// Convert to RGBM.
|
||||
Vector4 rgbm_colors[16];
|
||||
float rgb_weights[16];
|
||||
convert_to_rgbm(input_colors, input_weights, min_m, rgbm_colors, rgb_weights);
|
||||
|
||||
void * etc_output = (uint8 *)output + 8;
|
||||
void * eac_output = output;
|
||||
|
||||
// Compress RGB.
|
||||
compress_etc2(rgbm_colors, rgb_weights, Vector3(1), etc_output);
|
||||
|
||||
// Decompress RGB/M block.
|
||||
nv::ColorBlock RGB;
|
||||
block->color.decodeBlock(&RGB);
|
||||
decompress_etc(etc_output, rgbm_colors);
|
||||
|
||||
#if 1
|
||||
AlphaBlock4x4 M;
|
||||
// Compute M values to compensate for RGB's error.
|
||||
for (int i = 0; i < 16; i++) {
|
||||
const Vector4 & c = colors[i];
|
||||
const Vector4 & c = input_colors[i];
|
||||
|
||||
float R = saturate(c.x);
|
||||
float G = saturate(c.y);
|
||||
float B = saturate(c.z);
|
||||
|
||||
float r = RGB.color(i).r / 255.0f;
|
||||
float g = RGB.color(i).g / 255.0f;
|
||||
float b = RGB.color(i).b / 255.0f;
|
||||
|
||||
float m = (R / r + G / g + B / b) / 3.0f;
|
||||
//float m = max((R / r + G / g + B / b) / 3.0f, min_m);
|
||||
//float m = max(max(R / r, G / g), max(B / b, min_m));
|
||||
//float m = max(max(R, G), max(B, min_m));
|
||||
|
||||
float rm = rgbm_colors[i].x;
|
||||
float gm = rgbm_colors[i].y;
|
||||
float bm = rgbm_colors[i].z;
|
||||
|
||||
// compute m such that m * (r/M, g/M, b/M) == RGB
|
||||
|
||||
// Three equations, one unknown:
|
||||
// m * r/M == R
|
||||
// m * g/M == G
|
||||
// m * b/M == B
|
||||
|
||||
// Solve in the least squares sense!
|
||||
|
||||
// m (rm gm bm) (rm gm bm)^T == (rm gm bm) (R G B)^T
|
||||
|
||||
// m == dot(rgb, RGB) / dot(rgb, rgb)
|
||||
|
||||
float m = dot(Vector3(rm, gm, bm), Vector3(R, G, B)) / dot(Vector3(rm, gm, bm), Vector3(rm, gm, bm));
|
||||
if (!isFinite(m)) {
|
||||
m = 1;
|
||||
}
|
||||
|
||||
m = (m - min_m) / (1 - min_m);
|
||||
|
||||
M.alpha[i] = U8(ftoi_round(saturate(m) * 255.0f));
|
||||
M.weights[i] = weights[i];
|
||||
|
||||
// Store M in alpha channel.
|
||||
rgbm_colors[i].w = saturate(m); // @@ What it we don't saturate?
|
||||
}
|
||||
|
||||
// Compress M.
|
||||
if (compressionOptions.quality == Quality_Fastest) {
|
||||
QuickCompress::compressDXT5A(M, &block->alpha);
|
||||
}
|
||||
else {
|
||||
OptimalCompress::compressDXT5A(M, &block->alpha);
|
||||
}
|
||||
#else
|
||||
OptimalCompress::compressDXT5A_RGBM(src, RGB, &block->alpha);
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
// Decompress M.
|
||||
block->alpha.decodeBlock(&M);
|
||||
|
||||
rgb.allocate(src.w, src.h); // @@ Handle smaller blocks.
|
||||
|
||||
for (uint i = 0; i < src.colorCount; i++) {
|
||||
const Vector4 & c = src.color(i);
|
||||
|
||||
float R = saturate(c.x);
|
||||
float G = saturate(c.y);
|
||||
float B = saturate(c.z);
|
||||
|
||||
//float m = max(max(R, G), max(B, min_m));
|
||||
float m = float(M.alpha[i]) / 255.0f * (1 - min_m) + min_m;
|
||||
float r = R / m;
|
||||
float g = G / m;
|
||||
float b = B / m;
|
||||
float a = c.w;
|
||||
|
||||
rgb.colors[i] = Vector4(r, g, b, a);
|
||||
rgb.indices[i] = i;
|
||||
rgb.weights[i] = max(c.w, 0.001f);// src.weights[i]; // IC: For some reason 0 weights are causing problems, even if we eliminate the corresponding colors from the set.
|
||||
}
|
||||
|
||||
rgb.createMinimalSet(/*ignoreTransparent=*/true);
|
||||
|
||||
if (rgb.isSingleColor(/*ignoreAlpha=*/true)) {
|
||||
OptimalCompress::compressDXT1(toColor32(rgb.color(0)), &block->color);
|
||||
}
|
||||
else {
|
||||
ClusterFit fit;
|
||||
fit.setMetric(compressionOptions.colorWeight);
|
||||
fit.setColourSet(&rgb);
|
||||
|
||||
Vector3 start, end;
|
||||
fit.compress4(&start, &end);
|
||||
|
||||
QuickCompress::outputBlock4(rgb, start, end, &block->color);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
block->color.decodeBlock(&RGB);
|
||||
|
||||
//AlphaBlock4x4 M;
|
||||
//M.initWeights(src);
|
||||
|
||||
for (int i = 0; i < 16; i++) {
|
||||
const Vector4 & c = src.color(i);
|
||||
float R = saturate(c.x);
|
||||
float G = saturate(c.y);
|
||||
float B = saturate(c.z);
|
||||
|
||||
float r = RGB.color(i).r / 255.0f;
|
||||
float g = RGB.color(i).g / 255.0f;
|
||||
float b = RGB.color(i).b / 255.0f;
|
||||
|
||||
float m = (R / r + G / g + B / b) / 3.0f;
|
||||
//float m = max((R / r + G / g + B / b) / 3.0f, min_m);
|
||||
//float m = max(max(R / r, G / g), max(B / b, min_m));
|
||||
//float m = max(max(R, G), max(B, min_m));
|
||||
|
||||
m = (m - min_m) / (1 - min_m);
|
||||
|
||||
M.alpha[i] = U8(ftoi_round(saturate(m) * 255.0f));
|
||||
M.weights[i] = src.weights[i];
|
||||
}
|
||||
|
||||
// Compress M.
|
||||
if (compressionOptions.quality == Quality_Fastest) {
|
||||
QuickCompress::compressDXT5A(M, &block->alpha);
|
||||
}
|
||||
else {
|
||||
OptimalCompress::compressDXT5A(M, &block->alpha);
|
||||
}
|
||||
#endif
|
||||
compress_eac(rgbm_colors, input_weights, /*input_channel=*/3, /*search_radius=*/1, /*11bit_mode*/false, eac_output);
|
||||
|
||||
return 0; // @@ Compute error.
|
||||
}
|
||||
|
||||
|
||||
#if 0
|
||||
src.fromRGBM(M, min_m);
|
||||
|
||||
src.createMinimalSet(/*ignoreTransparent=*/true);
|
||||
|
||||
if (src.isSingleColor(/*ignoreAlpha=*/true)) {
|
||||
OptimalCompress::compressDXT1(src.color(0), &block->color);
|
||||
}
|
||||
else {
|
||||
// @@ Use our improved compressor.
|
||||
ClusterFit fit;
|
||||
fit.setMetric(compressionOptions.colorWeight);
|
||||
fit.setColourSet(&src);
|
||||
|
||||
Vector3 start, end;
|
||||
fit.compress4(&start, &end);
|
||||
|
||||
if (fit.compress3(&start, &end)) {
|
||||
QuickCompress::outputBlock3(src, start, end, block->color);
|
||||
}
|
||||
else {
|
||||
QuickCompress::outputBlock4(src, start, end, block->color);
|
||||
}
|
||||
}
|
||||
#endif // 0
|
||||
|
||||
// @@ Decompress color and compute M that best approximates src with these colors? Then compress M again?
|
||||
|
||||
|
||||
|
||||
// RGBM encoding.
|
||||
// Maximize precision.
|
||||
// - Number of possible grey levels:
|
||||
// - Naive: 2^3 = 8
|
||||
// - Better: 2^3 + 2^2 = 12
|
||||
// - How to choose min_m?
|
||||
// - Ideal = Adaptive per block, don't know where to store.
|
||||
// - Adaptive per lightmap. How to compute optimal?
|
||||
// - Fixed: 0.25 in our case. Lightmaps scaled to a fixed [0, 1] range.
|
||||
|
||||
// - Optimal compressor: Interpolation artifacts.
|
||||
|
||||
// - Color transform.
|
||||
// - Measure error in post-tone-mapping color space.
|
||||
// - Assume a simple tone mapping operator. We know minimum and maximum exposure, but don't know exact exposure in game.
|
||||
// - Guess based on average lighmap color? Use fixed exposure, in scaled lightmap space.
|
||||
|
||||
// - Enhanced DXT compressor.
|
||||
// - Typical RGBM encoding as follows:
|
||||
// rgb -> M = max(rgb), RGB=rgb/M -> RGBM
|
||||
// - If we add a compression step (M' = M) and M' < M, then rgb may be greater than 1.
|
||||
// - We could ensure that M' >= M during compression.
|
||||
// - We could clamp RGB anyway.
|
||||
// - We could add a fixed scale value to take into account compression errors and avoid clamping.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Compress color.
|
||||
/*if (rgba.isSingleColor())
|
||||
{
|
||||
OptimalCompress::compressDXT1(rgba.color(0), &block->color);
|
||||
}
|
||||
else
|
||||
{
|
||||
nvsquish::WeightedClusterFit fit;
|
||||
fit.SetMetric(compressionOptions.colorWeight.x, compressionOptions.colorWeight.y, compressionOptions.colorWeight.z);
|
||||
|
||||
int flags = 0;
|
||||
if (alphaMode == nvtt::AlphaMode_Transparency) flags |= nvsquish::kWeightColourByAlpha;
|
||||
|
||||
nvsquish::ColourSet colours((uint8 *)rgba.colors(), flags);
|
||||
fit.SetColourSet(&colours, 0);
|
||||
fit.Compress(&block->color);
|
||||
}*/
|
||||
|
||||
#endif // 0
|
@ -5,5 +5,5 @@ namespace nv {
|
||||
class Vector4;
|
||||
|
||||
float compress_dxt5_rgbm(const Vector4 input_colors[16], const float input_weights[16], float min_m, BlockDXT5 * output);
|
||||
|
||||
float compress_etc2_rgbm(Vector4 input_colors[16], float input_weights[16], float min_m, void * output);
|
||||
}
|
||||
|
2307
src/nvtt/CompressorETC.cpp
Normal file
2307
src/nvtt/CompressorETC.cpp
Normal file
File diff suppressed because it is too large
Load Diff
20
src/nvtt/CompressorETC.h
Normal file
20
src/nvtt/CompressorETC.h
Normal file
@ -0,0 +1,20 @@
|
||||
#include "nvcore/nvcore.h"
|
||||
|
||||
namespace nv {
|
||||
|
||||
class Vector3;
|
||||
class Vector4;
|
||||
|
||||
void decompress_etc(const void * input_block, Vector4 output_colors[16]);
|
||||
void decompress_eac(const void * input_block, Vector4 output_colors[16], int output_channel);
|
||||
void decompress_etc_eac(const void * input_block, Vector4 output_colors[16]);
|
||||
|
||||
float compress_etc1(Vector4 input_colors[16], float input_weights[16], const Vector3 & color_weights, void * output);
|
||||
float compress_etc2(Vector4 input_colors[16], float input_weights[16], const Vector3 & color_weights, void * output);
|
||||
float compress_etc2_a1(Vector4 input_colors[16], float input_weights[16], const Vector3 & color_weights, void * output);
|
||||
float compress_eac(Vector4 input_colors[16], float input_weights[16], int input_channel, int search_radius, bool use_11bit_mode, void * output);
|
||||
float compress_etc2_eac(Vector4 input_colors[16], float input_weights[16], const Vector3 & color_weights, void * output);
|
||||
|
||||
}
|
||||
|
||||
|
@ -250,6 +250,8 @@ namespace
|
||||
|
||||
// Compute shared exponent.
|
||||
int exp_shared_p = max(-B-1, ftoi_floor(log2f(max_c))) + 1 + B;
|
||||
nvDebugCheck(exp_shared_p <= Emax);
|
||||
nvDebugCheck(exp_shared_p >= 0);
|
||||
|
||||
int max_s = ftoi_round(max_c / (1 << (exp_shared_p - B - N)));
|
||||
|
||||
@ -279,7 +281,7 @@ namespace
|
||||
{
|
||||
float v = max3(r, g, b);
|
||||
|
||||
uint rgbe;
|
||||
uint rgbe = 0;
|
||||
|
||||
if (v < 1e-32) {
|
||||
rgbe = 0;
|
||||
@ -534,6 +536,7 @@ void PixelFormatConverter::compress(nvtt::AlphaMode /*alphaMode*/, uint w, uint
|
||||
}
|
||||
else if (compressionOptions.pixelType == nvtt::PixelType_SignedNorm) {
|
||||
// @@
|
||||
ir = ig = ib = ia = 0;
|
||||
}
|
||||
else if (compressionOptions.pixelType == nvtt::PixelType_UnsignedInt) {
|
||||
ir = iround(clamp(r, 0.0f, 65535.0f));
|
||||
@ -543,6 +546,11 @@ void PixelFormatConverter::compress(nvtt::AlphaMode /*alphaMode*/, uint w, uint
|
||||
}
|
||||
else if (compressionOptions.pixelType == nvtt::PixelType_SignedInt) {
|
||||
// @@
|
||||
ir = ig = ib = ia = 0;
|
||||
}
|
||||
else {
|
||||
// @@
|
||||
ir = ig = ib = ia = 0;
|
||||
}
|
||||
|
||||
uint p = 0;
|
||||
|
@ -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,230 @@ 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]);
|
||||
// Output each face from the largest mipmap to the smallest.
|
||||
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.packNormals();
|
||||
}
|
||||
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.kaiserAlpha, inputOptions.kaiserStretch };
|
||||
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.expandNormals();
|
||||
img.normalizeNormalMap();
|
||||
img.packNormals();
|
||||
}
|
||||
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.kaiserAlpha, inputOptions.kaiserStretch };
|
||||
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.expandNormals();
|
||||
img.normalizeNormalMap();
|
||||
img.packNormals();
|
||||
}
|
||||
tmp = img;
|
||||
}
|
||||
else {
|
||||
tmp = img;
|
||||
tmp.toGamma(inputOptions.outputGamma);
|
||||
}
|
||||
|
||||
quantize(tmp, compressionOptions);
|
||||
compress(tmp, f, m, compressionOptions, outputOptions);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// KTX files expect face mipmaps to be interleaved.
|
||||
Array<nvtt::Surface> images(faceCount);
|
||||
Array<bool> mipChainBroken(faceCount);
|
||||
|
||||
int w = width;
|
||||
int h = height;
|
||||
int d = depth;
|
||||
|
||||
// https://www.khronos.org/opengles/sdk/tools/KTX/file_format_spec/#2.16
|
||||
uint imageSize = estimateSize(w, h, 1, 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);
|
||||
}
|
||||
|
||||
static const 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
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -673,6 +803,131 @@ 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;
|
||||
else /*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_ETC1) {
|
||||
header.glInternalFormat = outputOptions.srgb ? KTX_INTERNAL_COMPRESSED_SRGB_ETC1 : KTX_INTERNAL_COMPRESSED_RGB_ETC1;
|
||||
header.glBaseInternalFormat = KTX_BASE_INTERNAL_RGB;
|
||||
}
|
||||
else if (compressionOptions.format == Format_ETC2_R) {
|
||||
header.glInternalFormat = KTX_INTERNAL_COMPRESSED_RED_EAC;
|
||||
header.glBaseInternalFormat = KTX_BASE_INTERNAL_RED;
|
||||
}
|
||||
else if (compressionOptions.format == Format_ETC2_RG) {
|
||||
header.glInternalFormat = KTX_INTERNAL_COMPRESSED_RG_EAC;
|
||||
header.glBaseInternalFormat = KTX_BASE_INTERNAL_RG;
|
||||
}
|
||||
else if (compressionOptions.format == Format_ETC2_RGB) {
|
||||
header.glInternalFormat = outputOptions.srgb ? KTX_INTERNAL_COMPRESSED_SRGB_ETC2 : KTX_INTERNAL_COMPRESSED_RGB_ETC2;
|
||||
header.glBaseInternalFormat = KTX_BASE_INTERNAL_RGB;
|
||||
}
|
||||
else if (compressionOptions.format == Format_ETC2_RGBA) {
|
||||
header.glInternalFormat = outputOptions.srgb ? KTX_INTERNAL_COMPRESSED_SRGB_ALPHA_ETC2_EAC : KTX_INTERNAL_COMPRESSED_RGBA_ETC2_EAC;
|
||||
header.glBaseInternalFormat = KTX_BASE_INTERNAL_RGBA;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
const uint headerSize = 64;
|
||||
nvStaticCheck(sizeof(KtxHeader) == 64);
|
||||
|
||||
bool writeSucceed = outputOptions.writeData(&header, headerSize);
|
||||
if (!writeSucceed)
|
||||
{
|
||||
outputOptions.error(Error_FileWrite);
|
||||
}
|
||||
|
||||
return writeSucceed;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -788,15 +1043,34 @@ CompressorInterface * Compressor::Private::chooseCpuCompressor(const Compression
|
||||
{
|
||||
return new CompressorBC7;
|
||||
}
|
||||
/*else if (compressionOptions.format == Format_BC5_Luma)
|
||||
{
|
||||
return new ProductionCompressorBC5_Luma;
|
||||
}*/
|
||||
else if (compressionOptions.format == Format_BC3_RGBM)
|
||||
{
|
||||
return new CompressorBC3_RGBM;
|
||||
}
|
||||
|
||||
else if (compressionOptions.format >= Format_ETC1 && compressionOptions.format <= Format_ETC2_RGB_A1)
|
||||
{
|
||||
#if defined(HAVE_RGETC)
|
||||
if (compressionOptions.format == Format_ETC1 && compressionOptions.externalCompressor == "rg_etc") return new RgEtcCompressor;
|
||||
#endif
|
||||
#if defined(HAVE_ETCLIB)
|
||||
if (compressionOptions.externalCompressor == "etclib") return new EtcLibCompressor;
|
||||
#endif
|
||||
if (compressionOptions.format == Format_ETC1) return new CompressorETC1;
|
||||
else if (compressionOptions.format == Format_ETC2_R) return new CompressorETC2_R;
|
||||
//else if (compressionOptions.format == Format_ETC2_RG) return new CompressorETC2_RG;
|
||||
else if (compressionOptions.format == Format_ETC2_RGB) return new CompressorETC2_RGB;
|
||||
else if (compressionOptions.format == Format_ETC2_RGBA) return new CompressorETC2_RGBA;
|
||||
}
|
||||
else if (compressionOptions.format == Format_ETC2_RGBM)
|
||||
{
|
||||
return new CompressorETC2_RGBM;
|
||||
}
|
||||
else if (compressionOptions.format >= Format_PVR_2BPP_RGB && compressionOptions.format <= Format_PVR_4BPP_RGBA)
|
||||
{
|
||||
#if defined(HAVE_PVRTEXTOOL)
|
||||
return new CompressorPVR;
|
||||
#endif
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@ -860,3 +1134,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;
|
||||
|
@ -34,61 +34,61 @@
|
||||
namespace nvtt
|
||||
{
|
||||
|
||||
struct DefaultOutputHandler : public nvtt::OutputHandler
|
||||
{
|
||||
DefaultOutputHandler(const char * fileName) : stream(fileName) {}
|
||||
struct DefaultOutputHandler : public nvtt::OutputHandler
|
||||
{
|
||||
DefaultOutputHandler(const char * fileName) : stream(fileName) {}
|
||||
DefaultOutputHandler(FILE * fp) : stream(fp, false) {}
|
||||
|
||||
virtual ~DefaultOutputHandler() {}
|
||||
|
||||
virtual void beginImage(int size, int width, int height, int depth, int face, int miplevel)
|
||||
{
|
||||
// ignore.
|
||||
}
|
||||
|
||||
// Output data.
|
||||
virtual bool writeData(const void * data, int size)
|
||||
{
|
||||
stream.serialize(const_cast<void *>(data), size);
|
||||
|
||||
//return !stream.isError();
|
||||
return true;
|
||||
}
|
||||
virtual ~DefaultOutputHandler() {}
|
||||
|
||||
virtual void endImage()
|
||||
{
|
||||
// ignore.
|
||||
}
|
||||
virtual void beginImage(int size, int width, int height, int depth, int face, int miplevel)
|
||||
{
|
||||
// ignore.
|
||||
}
|
||||
|
||||
nv::StdOutputStream stream;
|
||||
};
|
||||
// Output data.
|
||||
virtual bool writeData(const void * data, int size)
|
||||
{
|
||||
stream.serialize(const_cast<void *>(data), size);
|
||||
|
||||
//return !stream.isError();
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual void endImage()
|
||||
{
|
||||
// ignore.
|
||||
}
|
||||
|
||||
nv::StdOutputStream stream;
|
||||
};
|
||||
|
||||
|
||||
struct OutputOptions::Private
|
||||
{
|
||||
nv::Path fileName;
|
||||
struct OutputOptions::Private
|
||||
{
|
||||
nv::Path fileName;
|
||||
FILE * fileHandle;
|
||||
|
||||
OutputHandler * outputHandler;
|
||||
ErrorHandler * errorHandler;
|
||||
|
||||
bool outputHeader;
|
||||
Container container;
|
||||
OutputHandler * outputHandler;
|
||||
ErrorHandler * errorHandler;
|
||||
|
||||
bool outputHeader;
|
||||
Container container;
|
||||
int version;
|
||||
bool srgb;
|
||||
bool deleteOutputHandler;
|
||||
|
||||
void * wrapperProxy; // For the C/C# wrapper.
|
||||
|
||||
bool hasValidOutputHandler() const;
|
||||
|
||||
void beginImage(int size, int width, int height, int depth, int face, int miplevel) const;
|
||||
bool writeData(const void * data, int size) const;
|
||||
bool hasValidOutputHandler() const;
|
||||
|
||||
void beginImage(int size, int width, int height, int depth, int face, int miplevel) const;
|
||||
bool writeData(const void * data, int size) const;
|
||||
void endImage() const;
|
||||
void error(Error e) const;
|
||||
};
|
||||
void error(Error e) const;
|
||||
};
|
||||
|
||||
|
||||
|
||||
} // nvtt namespace
|
||||
|
||||
|
||||
|
@ -39,21 +39,21 @@ namespace nv
|
||||
struct AlphaBlockDXT5;
|
||||
class Vector3;
|
||||
|
||||
namespace QuickCompress
|
||||
{
|
||||
void compressDXT1(const ColorBlock & src, BlockDXT1 * dst);
|
||||
void compressDXT1a(const ColorBlock & src, BlockDXT1 * dst);
|
||||
|
||||
void compressDXT3(const ColorBlock & src, BlockDXT3 * dst);
|
||||
|
||||
void compressDXT5A(const ColorBlock & src, AlphaBlockDXT5 * dst, int iterationCount=8);
|
||||
void compressDXT5A(const AlphaBlock4x4 & src, AlphaBlockDXT5 * dst, int iterationCount=8);
|
||||
namespace QuickCompress
|
||||
{
|
||||
void compressDXT1(const ColorBlock & src, BlockDXT1 * dst);
|
||||
void compressDXT1a(const ColorBlock & src, BlockDXT1 * dst);
|
||||
|
||||
void compressDXT5(const ColorBlock & src, BlockDXT5 * dst, int iterationCount=8);
|
||||
void compressDXT3(const ColorBlock & src, BlockDXT3 * dst);
|
||||
|
||||
void compressDXT5A(const ColorBlock & src, AlphaBlockDXT5 * dst, int iterationCount=8);
|
||||
void compressDXT5A(const AlphaBlock4x4 & src, AlphaBlockDXT5 * dst, int iterationCount=8);
|
||||
|
||||
void compressDXT5(const ColorBlock & src, BlockDXT5 * dst, int iterationCount=8);
|
||||
|
||||
void outputBlock4(const ColorSet & set, const Vector3 & start, const Vector3 & end, BlockDXT1 * block);
|
||||
void outputBlock3(const ColorSet & set, const Vector3 & start, const Vector3 & end, BlockDXT1 * block);
|
||||
}
|
||||
}
|
||||
} // nv namespace
|
||||
|
||||
#endif // NV_TT_QUICKCOMPRESSDXT_H
|
||||
|
@ -23,12 +23,14 @@
|
||||
// OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
#include "Surface.h"
|
||||
#include "CompressorETC.h" // for ETC decoder.
|
||||
|
||||
#include "nvmath/Vector.inl"
|
||||
#include "nvmath/Matrix.inl"
|
||||
#include "nvmath/Color.h"
|
||||
#include "nvmath/Half.h"
|
||||
#include "nvmath/ftoi.h"
|
||||
#include "nvmath/PackedFloat.h"
|
||||
|
||||
#include "nvimage/Filter.h"
|
||||
#include "nvimage/ImageIO.h"
|
||||
@ -39,8 +41,13 @@
|
||||
#include "nvimage/ErrorMetric.h"
|
||||
#include "nvimage/DirectDrawSurface.h"
|
||||
|
||||
#include "nvthread/ParallelFor.h"
|
||||
|
||||
#include "nvcore/Array.inl"
|
||||
|
||||
#include <float.h>
|
||||
#include <string.h> // memset, memcpy
|
||||
//#include <stdio.h> // printf?
|
||||
|
||||
#if NV_CC_GNUC
|
||||
#include <math.h> // exp2f and log2f
|
||||
@ -123,6 +130,18 @@ namespace
|
||||
else if (format == Format_BC7) {
|
||||
return 16;
|
||||
}
|
||||
else if (format == Format_ETC1 || format == Format_ETC2_R || format == Format_ETC2_RGB) {
|
||||
return 8;
|
||||
}
|
||||
else if (format == Format_ETC2_RG || format == Format_ETC2_RGBA || format == Format_ETC2_RGBM) {
|
||||
return 16;
|
||||
}
|
||||
else if (format == Format_PVR_2BPP_RGB || format == Format_PVR_2BPP_RGBA) {
|
||||
return 4;
|
||||
}
|
||||
else if (format == Format_PVR_4BPP_RGB || format == Format_PVR_4BPP_RGBA) {
|
||||
return 8;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -197,7 +216,7 @@ uint nv::computeImageSize(uint w, uint h, uint d, uint bitCount, uint pitchAlign
|
||||
}
|
||||
}
|
||||
|
||||
void nv::getTargetExtent(int * width, int * height, int * depth, int maxExtent, RoundMode roundMode, TextureType textureType) {
|
||||
void nv::getTargetExtent(int * width, int * height, int * depth, int maxExtent, RoundMode roundMode, TextureType textureType, nvtt::ShapeRestriction shapeRestriction /*= nvtt::ShapeRestriction_None*/) {
|
||||
nvDebugCheck(width != NULL && *width > 0);
|
||||
nvDebugCheck(height != NULL && *height > 0);
|
||||
nvDebugCheck(depth != NULL && *depth > 0);
|
||||
@ -234,21 +253,21 @@ void nv::getTargetExtent(int * width, int * height, int * depth, int maxExtent,
|
||||
// Round to power of two.
|
||||
if (roundMode == RoundMode_ToNextPowerOfTwo)
|
||||
{
|
||||
w = nextPowerOfTwo(w);
|
||||
h = nextPowerOfTwo(h);
|
||||
d = nextPowerOfTwo(d);
|
||||
w = nextPowerOfTwo(U32(w));
|
||||
h = nextPowerOfTwo(U32(h));
|
||||
d = nextPowerOfTwo(U32(d));
|
||||
}
|
||||
else if (roundMode == RoundMode_ToNearestPowerOfTwo)
|
||||
{
|
||||
w = nearestPowerOfTwo(w);
|
||||
h = nearestPowerOfTwo(h);
|
||||
d = nearestPowerOfTwo(d);
|
||||
w = nearestPowerOfTwo(U32(w));
|
||||
h = nearestPowerOfTwo(U32(h));
|
||||
d = nearestPowerOfTwo(U32(d));
|
||||
}
|
||||
else if (roundMode == RoundMode_ToPreviousPowerOfTwo)
|
||||
{
|
||||
w = previousPowerOfTwo(w);
|
||||
h = previousPowerOfTwo(h);
|
||||
d = previousPowerOfTwo(d);
|
||||
w = previousPowerOfTwo(U32(w));
|
||||
h = previousPowerOfTwo(U32(h));
|
||||
d = previousPowerOfTwo(U32(d));
|
||||
}
|
||||
else if (roundMode == RoundMode_ToNextMultipleOfFour)
|
||||
{
|
||||
@ -269,6 +288,38 @@ void nv::getTargetExtent(int * width, int * height, int * depth, int maxExtent,
|
||||
d = previousMultipleOfFour(d);
|
||||
}
|
||||
|
||||
if(shapeRestriction == ShapeRestriction_Square)
|
||||
{
|
||||
if (textureType == TextureType_2D)
|
||||
{
|
||||
int md = nv::min(w,h);
|
||||
w = md;
|
||||
h = md;
|
||||
d = 1;
|
||||
}
|
||||
else if (textureType == TextureType_3D)
|
||||
{
|
||||
int md = nv::min(nv::min(w,h),d);
|
||||
w = md;
|
||||
h = md;
|
||||
d = md;
|
||||
}
|
||||
else if (textureType == TextureType_Cube)
|
||||
{
|
||||
int md = nv::min(w, h);
|
||||
w = md;
|
||||
h = md;
|
||||
d = 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (textureType == TextureType_2D || textureType == TextureType_Cube)
|
||||
{
|
||||
d = 1;
|
||||
}
|
||||
}
|
||||
|
||||
*width = w;
|
||||
*height = h;
|
||||
*depth = d;
|
||||
@ -509,8 +560,8 @@ void Surface::range(int channel, float * rangeMin, float * rangeMax, int alpha_c
|
||||
}
|
||||
}
|
||||
|
||||
*rangeMin = range.x;
|
||||
*rangeMax = range.y;
|
||||
if (rangeMin) *rangeMin = range.x;
|
||||
if (rangeMax) *rangeMax = range.y;
|
||||
}
|
||||
|
||||
bool Surface::load(const char * fileName, bool * hasAlpha/*= NULL*/)
|
||||
@ -583,7 +634,7 @@ bool Surface::load(const char * fileName, bool * hasAlpha/*= NULL*/)
|
||||
}
|
||||
|
||||
// @@ Have loadFloat allocate the image with the desired number of channels.
|
||||
img->resizeChannelCount(4);
|
||||
//img->resizeChannelCount(4);
|
||||
|
||||
delete m->image;
|
||||
m->image = img.release();
|
||||
@ -601,7 +652,8 @@ bool Surface::save(const char * fileName, bool hasAlpha/*=0*/, bool hdr/*=0*/) c
|
||||
return ImageIO::saveFloat(fileName, m->image, 0, 4);
|
||||
}
|
||||
else {
|
||||
AutoPtr<Image> image(m->image->createImage(0, 4));
|
||||
uint c = min<uint>(m->image->componentCount(), 4);
|
||||
AutoPtr<Image> image(m->image->createImage(0, c));
|
||||
nvCheck(image != NULL);
|
||||
|
||||
if (hasAlpha) {
|
||||
@ -829,16 +881,35 @@ bool Surface::setImage(InputFormat format, int w, int h, int d, const void * r,
|
||||
return true;
|
||||
}
|
||||
|
||||
#if defined(HAVE_PVRTEXTOOL)
|
||||
#include <PVRTDecompress.h>
|
||||
#endif
|
||||
|
||||
// @@ Add support for compressed 3D textures.
|
||||
bool Surface::setImage2D(Format format, Decoder decoder, int w, int h, const void * data)
|
||||
{
|
||||
if (format != nvtt::Format_BC1 &&
|
||||
format != nvtt::Format_BC2 &&
|
||||
format != nvtt::Format_BC3 &&
|
||||
format != nvtt::Format_BC3n &&
|
||||
format != nvtt::Format_BC3_RGBM &&
|
||||
format != nvtt::Format_BC4 &&
|
||||
format != nvtt::Format_BC5 &&
|
||||
format != nvtt::Format_BC6 &&
|
||||
format != nvtt::Format_BC7)
|
||||
format != nvtt::Format_BC7 &&
|
||||
format != nvtt::Format_ETC1 &&
|
||||
format != nvtt::Format_ETC2_R &&
|
||||
format != nvtt::Format_ETC2_RG &&
|
||||
format != nvtt::Format_ETC2_RGB &&
|
||||
format != nvtt::Format_ETC2_RGBA &&
|
||||
format != nvtt::Format_ETC2_RGBM
|
||||
#if defined(HAVE_PVRTEXTOOL)
|
||||
&& format != nvtt::Format_PVR_2BPP_RGB
|
||||
&& format != nvtt::Format_PVR_4BPP_RGB
|
||||
&& format != nvtt::Format_PVR_2BPP_RGBA
|
||||
&& format != nvtt::Format_PVR_4BPP_RGBA
|
||||
#endif
|
||||
)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@ -851,7 +922,7 @@ bool Surface::setImage2D(Format format, Decoder decoder, int w, int h, const voi
|
||||
m->image->allocate(4, w, h, 1);
|
||||
m->type = TextureType_2D;
|
||||
|
||||
const int bw = (w + 3) / 4;
|
||||
const int bw = (w + 3) / 4; // @@ Not if PVR 2bpp!
|
||||
const int bh = (h + 3) / 4;
|
||||
|
||||
const uint bs = blockSize(format);
|
||||
@ -859,130 +930,166 @@ bool Surface::setImage2D(Format format, Decoder decoder, int w, int h, const voi
|
||||
const uint8 * ptr = (const uint8 *)data;
|
||||
|
||||
TRY {
|
||||
if (format == nvtt::Format_BC6)
|
||||
{
|
||||
// BC6 format - decode directly to float
|
||||
#if defined(HAVE_PVRTEXTOOL)
|
||||
if (format >= nvtt::Format_PVR_2BPP_RGB && format <= nvtt::Format_PVR_4BPP_RGBA)
|
||||
{
|
||||
bool two_bit_mode = (format == nvtt::Format_PVR_2BPP_RGB || format == nvtt::Format_PVR_2BPP_RGBA);
|
||||
|
||||
for (int y = 0; y < bh; y++)
|
||||
{
|
||||
for (int x = 0; x < bw; x++)
|
||||
{
|
||||
Vector3 colors[16];
|
||||
const BlockBC6 * block = (const BlockBC6 *)ptr;
|
||||
block->decodeBlock(colors);
|
||||
uint8 * output = new uint8[4 * w * h];
|
||||
|
||||
for (int yy = 0; yy < 4; yy++)
|
||||
{
|
||||
for (int xx = 0; xx < 4; xx++)
|
||||
{
|
||||
Vector3 rgb = colors[yy*4 + xx];
|
||||
PVRTDecompressPVRTC(ptr, two_bit_mode, w, h, output);
|
||||
|
||||
if (x * 4 + xx < w && y * 4 + yy < h)
|
||||
{
|
||||
m->image->pixel(0, x*4 + xx, y*4 + yy, 0) = rgb.x;
|
||||
m->image->pixel(1, x*4 + xx, y*4 + yy, 0) = rgb.y;
|
||||
m->image->pixel(2, x*4 + xx, y*4 + yy, 0) = rgb.z;
|
||||
m->image->pixel(3, x*4 + xx, y*4 + yy, 0) = 1.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (int y = 0; y < h; y++) {
|
||||
for (int x = 0; x < w; x++) {
|
||||
m->image->pixel(0, x, y, 0) = output[4*(y*w + x) + 0] / 255.0f;
|
||||
m->image->pixel(1, x, y, 0) = output[4*(y*w + x) + 1] / 255.0f;
|
||||
m->image->pixel(2, x, y, 0) = output[4*(y*w + x) + 2] / 255.0f;
|
||||
m->image->pixel(3, x, y, 0) = output[4*(y*w + x) + 3] / 255.0f;
|
||||
}
|
||||
}
|
||||
|
||||
ptr += bs;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Non-BC6 - decode to 8-bit, then convert to float
|
||||
delete [] output;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
if (format == nvtt::Format_BC6 || (format >= nvtt::Format_ETC1 && format <= nvtt::Format_ETC2_RGBM))
|
||||
{
|
||||
// Some formats we decode directly to float:
|
||||
|
||||
for (int y = 0; y < bh; y++)
|
||||
{
|
||||
for (int x = 0; x < bw; x++)
|
||||
{
|
||||
ColorBlock colors;
|
||||
for (int y = 0; y < bh; y++) {
|
||||
for (int x = 0; x < bw; x++) {
|
||||
Vector4 colors[16];
|
||||
|
||||
if (format == nvtt::Format_BC1)
|
||||
{
|
||||
const BlockDXT1 * block = (const BlockDXT1 *)ptr;
|
||||
if (format == nvtt::Format_BC6) {
|
||||
const BlockBC6 * block = (const BlockBC6 *)ptr;
|
||||
block->decodeBlock(colors);
|
||||
}
|
||||
else if (format == nvtt::Format_ETC1 || format == nvtt::Format_ETC2_RGB) {
|
||||
nv::decompress_etc(ptr, colors);
|
||||
}
|
||||
else if (format == nvtt::Format_ETC2_RGBA || format == nvtt::Format_ETC2_RGBM) {
|
||||
nv::decompress_etc_eac(ptr, colors);
|
||||
}
|
||||
else if (format == nvtt::Format_ETC2_R) {
|
||||
// @@ Not implemented.
|
||||
//nv::decompress_eac(ptr, colors);
|
||||
}
|
||||
else if (format == nvtt::Format_ETC2_RG) {
|
||||
// @@ Not implemented.
|
||||
//nv::decompress_eac(ptr, colors);
|
||||
}
|
||||
else if (format == nvtt::Format_ETC2_RGB_A1) {
|
||||
// @@ Not implemented?
|
||||
//nv::decompress_etc(ptr, colors);
|
||||
}
|
||||
|
||||
if (decoder == Decoder_D3D10) {
|
||||
block->decodeBlock(&colors, false);
|
||||
}
|
||||
else if (decoder == Decoder_D3D9) {
|
||||
block->decodeBlock(&colors, false);
|
||||
}
|
||||
else if (decoder == Decoder_NV5x) {
|
||||
block->decodeBlockNV5x(&colors);
|
||||
}
|
||||
}
|
||||
else if (format == nvtt::Format_BC2)
|
||||
{
|
||||
const BlockDXT3 * block = (const BlockDXT3 *)ptr;
|
||||
for (int yy = 0; yy < 4; yy++) {
|
||||
for (int xx = 0; xx < 4; xx++) {
|
||||
Vector4 c = colors[yy*4 + xx];
|
||||
|
||||
if (decoder == Decoder_D3D10) {
|
||||
block->decodeBlock(&colors, false);
|
||||
}
|
||||
else if (decoder == Decoder_D3D9) {
|
||||
block->decodeBlock(&colors, false);
|
||||
}
|
||||
else if (decoder == Decoder_NV5x) {
|
||||
block->decodeBlockNV5x(&colors);
|
||||
}
|
||||
}
|
||||
else if (format == nvtt::Format_BC3)
|
||||
{
|
||||
const BlockDXT5 * block = (const BlockDXT5 *)ptr;
|
||||
if (x * 4 + xx < w && y * 4 + yy < h) {
|
||||
m->image->pixel(0, x*4 + xx, y*4 + yy, 0) = c.x;
|
||||
m->image->pixel(1, x*4 + xx, y*4 + yy, 0) = c.y;
|
||||
m->image->pixel(2, x*4 + xx, y*4 + yy, 0) = c.z;
|
||||
m->image->pixel(3, x*4 + xx, y*4 + yy, 0) = c.w;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (decoder == Decoder_D3D10) {
|
||||
block->decodeBlock(&colors, false);
|
||||
}
|
||||
else if (decoder == Decoder_D3D9) {
|
||||
block->decodeBlock(&colors, false);
|
||||
}
|
||||
else if (decoder == Decoder_NV5x) {
|
||||
block->decodeBlockNV5x(&colors);
|
||||
}
|
||||
}
|
||||
else if (format == nvtt::Format_BC4)
|
||||
{
|
||||
const BlockATI1 * block = (const BlockATI1 *)ptr;
|
||||
block->decodeBlock(&colors, decoder == Decoder_D3D9);
|
||||
}
|
||||
else if (format == nvtt::Format_BC5)
|
||||
{
|
||||
const BlockATI2 * block = (const BlockATI2 *)ptr;
|
||||
block->decodeBlock(&colors, decoder == Decoder_D3D9);
|
||||
}
|
||||
else if (format == nvtt::Format_BC7)
|
||||
{
|
||||
const BlockBC7 * block = (const BlockBC7 *)ptr;
|
||||
block->decodeBlock(&colors);
|
||||
}
|
||||
else
|
||||
{
|
||||
nvDebugCheck(false);
|
||||
}
|
||||
ptr += bs;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Others, we decode to 8-bit, then convert to float
|
||||
|
||||
for (int yy = 0; yy < 4; yy++)
|
||||
{
|
||||
for (int xx = 0; xx < 4; xx++)
|
||||
{
|
||||
Color32 c = colors.color(xx, yy);
|
||||
for (int y = 0; y < bh; y++) {
|
||||
for (int x = 0; x < bw; x++) {
|
||||
ColorBlock colors;
|
||||
|
||||
if (x * 4 + xx < w && y * 4 + yy < h)
|
||||
{
|
||||
m->image->pixel(0, x*4 + xx, y*4 + yy, 0) = float(c.r) * 1.0f/255.0f;
|
||||
m->image->pixel(1, x*4 + xx, y*4 + yy, 0) = float(c.g) * 1.0f/255.0f;
|
||||
m->image->pixel(2, x*4 + xx, y*4 + yy, 0) = float(c.b) * 1.0f/255.0f;
|
||||
m->image->pixel(3, x*4 + xx, y*4 + yy, 0) = float(c.a) * 1.0f/255.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (format == nvtt::Format_BC1)
|
||||
{
|
||||
const BlockDXT1 * block = (const BlockDXT1 *)ptr;
|
||||
|
||||
ptr += bs;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (decoder == Decoder_D3D10) {
|
||||
block->decodeBlock(&colors, false);
|
||||
}
|
||||
else if (decoder == Decoder_D3D9) {
|
||||
block->decodeBlock(&colors, false);
|
||||
}
|
||||
else if (decoder == Decoder_NV5x) {
|
||||
block->decodeBlockNV5x(&colors);
|
||||
}
|
||||
}
|
||||
else if (format == nvtt::Format_BC2)
|
||||
{
|
||||
const BlockDXT3 * block = (const BlockDXT3 *)ptr;
|
||||
|
||||
if (decoder == Decoder_D3D10) {
|
||||
block->decodeBlock(&colors, false);
|
||||
}
|
||||
else if (decoder == Decoder_D3D9) {
|
||||
block->decodeBlock(&colors, false);
|
||||
}
|
||||
else if (decoder == Decoder_NV5x) {
|
||||
block->decodeBlockNV5x(&colors);
|
||||
}
|
||||
}
|
||||
else if (format == nvtt::Format_BC3 || format == nvtt::Format_BC3n || format == nvtt::Format_BC3_RGBM)
|
||||
{
|
||||
const BlockDXT5 * block = (const BlockDXT5 *)ptr;
|
||||
|
||||
if (decoder == Decoder_D3D10) {
|
||||
block->decodeBlock(&colors, false);
|
||||
}
|
||||
else if (decoder == Decoder_D3D9) {
|
||||
block->decodeBlock(&colors, false);
|
||||
}
|
||||
else if (decoder == Decoder_NV5x) {
|
||||
block->decodeBlockNV5x(&colors);
|
||||
}
|
||||
}
|
||||
else if (format == nvtt::Format_BC4)
|
||||
{
|
||||
const BlockATI1 * block = (const BlockATI1 *)ptr;
|
||||
block->decodeBlock(&colors, decoder == Decoder_D3D9);
|
||||
}
|
||||
else if (format == nvtt::Format_BC5)
|
||||
{
|
||||
const BlockATI2 * block = (const BlockATI2 *)ptr;
|
||||
block->decodeBlock(&colors, decoder == Decoder_D3D9);
|
||||
}
|
||||
else if (format == nvtt::Format_BC7)
|
||||
{
|
||||
const BlockBC7 * block = (const BlockBC7 *)ptr;
|
||||
block->decodeBlock(&colors);
|
||||
}
|
||||
else
|
||||
{
|
||||
nvDebugCheck(false);
|
||||
}
|
||||
|
||||
for (int yy = 0; yy < 4; yy++)
|
||||
{
|
||||
for (int xx = 0; xx < 4; xx++)
|
||||
{
|
||||
Color32 c = colors.color(xx, yy);
|
||||
|
||||
if (x * 4 + xx < w && y * 4 + yy < h)
|
||||
{
|
||||
m->image->pixel(0, x*4 + xx, y*4 + yy, 0) = float(c.r) * 1.0f/255.0f;
|
||||
m->image->pixel(1, x*4 + xx, y*4 + yy, 0) = float(c.g) * 1.0f/255.0f;
|
||||
m->image->pixel(2, x*4 + xx, y*4 + yy, 0) = float(c.b) * 1.0f/255.0f;
|
||||
m->image->pixel(3, x*4 + xx, y*4 + yy, 0) = float(c.a) * 1.0f/255.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ptr += bs;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
CATCH {
|
||||
return false;
|
||||
@ -1092,7 +1199,7 @@ void Surface::resize(int w, int h, int d, ResizeFilter filter, float filterWidth
|
||||
m->image = img;
|
||||
}
|
||||
|
||||
void Surface::resize_make_square(int maxExtent, RoundMode roundMode, ResizeFilter filter)
|
||||
void Surface::resizeMakeSquare(int maxExtent, RoundMode roundMode, ResizeFilter filter)
|
||||
{
|
||||
if (isNull()) return;
|
||||
|
||||
@ -1104,27 +1211,17 @@ void Surface::resize_make_square(int maxExtent, RoundMode roundMode, ResizeFilte
|
||||
int h = m->image->height();
|
||||
int d = m->image->depth();
|
||||
|
||||
getTargetExtent(&w, &h, &d, maxExtent, roundMode, m->type);
|
||||
getTargetExtent(&w, &h, &d, maxExtent, roundMode, m->type, nvtt::ShapeRestriction_Square);
|
||||
|
||||
if (m->type == TextureType_2D)
|
||||
{
|
||||
nvDebugCheck(d==1);
|
||||
int md = nv::min(w,h);
|
||||
w = md;
|
||||
h = md;
|
||||
}
|
||||
else if (m->type == TextureType_Cube)
|
||||
{
|
||||
nvDebugCheck(d==1);
|
||||
nvDebugCheck(w==h);
|
||||
}
|
||||
else if (m->type == TextureType_3D)
|
||||
{
|
||||
int md = nv::min(nv::min(w,h),d);
|
||||
w = md;
|
||||
h = md;
|
||||
d = md;
|
||||
}
|
||||
|
||||
resize(w, h, d, filter, filterWidth, params);
|
||||
}
|
||||
@ -1151,6 +1248,63 @@ void Surface::resize(int maxExtent, RoundMode roundMode, ResizeFilter filter, fl
|
||||
resize(w, h, d, filter, filterWidth, params);
|
||||
}
|
||||
|
||||
|
||||
float rmsBilinearError(nvtt::Surface original, nvtt::Surface resized) {
|
||||
return nv::rmsBilinearColorError(original.m->image, resized.m->image, (FloatImage::WrapMode)original.wrapMode(), original.alphaMode() == AlphaMode_Transparency);
|
||||
}
|
||||
|
||||
|
||||
void Surface::autoResize(float errorTolerance, RoundMode mode, ResizeFilter filter)
|
||||
{
|
||||
Surface original = *this;
|
||||
Surface resized = original;
|
||||
|
||||
int w = width();
|
||||
int h = height();
|
||||
int d = depth();
|
||||
|
||||
w = (w + 1) / 2;
|
||||
h = (h + 1) / 2;
|
||||
d = (d + 1) / 2;
|
||||
|
||||
while (w >= 4 && h >= 4 && d >= 1) {
|
||||
|
||||
// Resize always from original? This is more expensive, but should produce higher quality.
|
||||
//resized = original;
|
||||
|
||||
resized.resize(w, h, d, filter);
|
||||
|
||||
#if 0
|
||||
// Scale back up to original size. @@ Upscaling not implemented!
|
||||
Surface restored = resized;
|
||||
restored.resize(original.width(), original.height(), original.depth(), ResizeFilter_Triangle);
|
||||
|
||||
float error;
|
||||
if (isNormalMap()) {
|
||||
error = nvtt::angularError(original, restored);
|
||||
}
|
||||
else {
|
||||
error = nvtt::rmsError(original, restored);
|
||||
}
|
||||
#else
|
||||
float error = rmsBilinearError(original, resized);
|
||||
#endif
|
||||
|
||||
if (error < errorTolerance) {
|
||||
*this = resized;
|
||||
nvDebug("image resized %dx%d -> %dx%d (error=%f)\n", original.width(), original.height(), w, h, error);
|
||||
}
|
||||
else {
|
||||
nvDebug("image can't be resized further (error=%f)\n", error);
|
||||
break;
|
||||
}
|
||||
|
||||
w = (w + 1) / 2;
|
||||
h = (h + 1) / 2;
|
||||
d = (d + 1) / 2;
|
||||
}
|
||||
}
|
||||
|
||||
bool Surface::canMakeNextMipmap(int min_size /*= 1*/)
|
||||
{
|
||||
if (isNull()) return false;
|
||||
@ -1196,7 +1350,7 @@ bool Surface::buildNextMipmap(MipmapFilter filter, float filterWidth, const floa
|
||||
{
|
||||
nvDebugCheck(filter == MipmapFilter_Kaiser);
|
||||
KaiserFilter filter(filterWidth);
|
||||
if (params != NULL) filter.setParameters(params[0], params[1]);
|
||||
if (params != NULL) filter.setParameters(/*alpha=*/params[0], /*stretch=*/params[1]);
|
||||
img = img->downSample(filter, wrapMode, 3);
|
||||
}
|
||||
}
|
||||
@ -1357,8 +1511,9 @@ void Surface::toSrgb()
|
||||
for (uint c = 0; c < 3; c++) {
|
||||
float * channel = img->channel(c);
|
||||
for (uint i = 0; i < count; i++) {
|
||||
//parallel_for(count, 128, [=](int i) {
|
||||
channel[i] = ::toSrgb(channel[i]);
|
||||
}
|
||||
}//);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1382,8 +1537,9 @@ void Surface::toLinearFromSrgb()
|
||||
for (uint c = 0; c < 3; c++) {
|
||||
float * channel = img->channel(c);
|
||||
for (uint i = 0; i < count; i++) {
|
||||
//parallel_for(count, 128, [=](int i) {
|
||||
channel[i] = ::fromSrgb(channel[i]);
|
||||
}
|
||||
}//);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2827,6 +2983,78 @@ Surface Surface::createSubImage(int x0, int x1, int y0, int y1, int z0, int z1)
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
Surface Surface::warp(int w, int h, WarpFunction * warp_function) const
|
||||
{
|
||||
Surface s;
|
||||
|
||||
FloatImage * img = s.m->image = new FloatImage;
|
||||
|
||||
const int C = m->image->componentCount();
|
||||
img->allocate(C, w, h, 1);
|
||||
|
||||
#define USE_PARALLEL_FOR 0
|
||||
#if USE_PARALLEL_FOR
|
||||
nv::parallel_for(h, 1, [=](int y) {
|
||||
#else
|
||||
for (int y = 0; y < h; y++) {
|
||||
#endif
|
||||
for (int x = 0; x < w; x++) {
|
||||
float fx = (float(x) + 0.0f) / w;
|
||||
float fy = (float(y) + 0.0f) / h;
|
||||
float fz = 0;
|
||||
|
||||
warp_function(fx, fy, fz);
|
||||
|
||||
for (int c = 0; c < C; c++) {
|
||||
img->pixel(c, x, y, 0) = m->image->sampleLinearClamp(c, fx, fy);
|
||||
}
|
||||
}
|
||||
}
|
||||
#if USE_PARALLEL_FOR
|
||||
);
|
||||
#endif
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
Surface Surface::warp(int w, int h, int d, WarpFunction * warp_function) const
|
||||
{
|
||||
Surface s;
|
||||
|
||||
FloatImage * img = s.m->image = new FloatImage;
|
||||
|
||||
const int C = m->image->componentCount();
|
||||
img->allocate(C, w, h, d);
|
||||
|
||||
for (int z = 0; z < d; z++) {
|
||||
#define USE_PARALLEL_FOR 0
|
||||
#if USE_PARALLEL_FOR
|
||||
nv::parallel_for(h, 1, [=](int y) {
|
||||
#else
|
||||
for (int y = 0; y < h; y++) {
|
||||
#endif
|
||||
for (int x = 0; x < w; x++) {
|
||||
float fx = (float(x) + 0.0f) / w;
|
||||
float fy = (float(y) + 0.0f) / h;
|
||||
float fz = (float(z) + 0.0f) / d;
|
||||
|
||||
warp_function(fx, fy, fz);
|
||||
|
||||
for (int c = 0; c < C; c++) {
|
||||
img->pixel(c, x, y, z) = m->image->sampleLinearClamp(c, fx, fy, fz); // @@ 2D only.
|
||||
}
|
||||
}
|
||||
}
|
||||
#if USE_PARALLEL_FOR
|
||||
);
|
||||
#endif
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
bool Surface::copyChannel(const Surface & srcImage, int srcChannel)
|
||||
{
|
||||
return copyChannel(srcImage, srcChannel, srcChannel);
|
||||
@ -2953,7 +3181,7 @@ void Surface::setAtlasBorder(int aw, int ah, float r, float g, float b, float a)
|
||||
}
|
||||
|
||||
// Vertical lines:
|
||||
for (uint i = 0, x = 0; i < uint(ah); i++, x += tile_width)
|
||||
for (uint i = 0, x = 0; i < uint(aw); i++, x += tile_width)
|
||||
{
|
||||
for (uint y = 0; y < h; y++)
|
||||
{
|
||||
@ -3083,9 +3311,9 @@ Surface nvtt::histogram(const Surface & img, int width, int height)
|
||||
return histogram(img, /*minRange*/0, maxRange, width, height);
|
||||
}
|
||||
|
||||
#include "nvcore/Array.inl"
|
||||
#include "nvmath/PackedFloat.h"
|
||||
#include <stdio.h>
|
||||
//#include "nvcore/Array.inl"
|
||||
//#include "nvmath/PackedFloat.h"
|
||||
//#include <stdio.h>
|
||||
|
||||
nvtt::Surface nvtt::histogram(const Surface & img, float minRange, float maxRange, int width, int height)
|
||||
{
|
||||
@ -3234,7 +3462,7 @@ nvtt::Surface nvtt::histogram(const Surface & img, float minRange, float maxRang
|
||||
maxh = nv::max(maxh, nv::max3(buckets[i].x, buckets[i].y, buckets[i].z));
|
||||
}
|
||||
|
||||
printf("maxh = %f\n", maxh);
|
||||
//printf("maxh = %f\n", maxh);
|
||||
//maxh = 80;
|
||||
maxh = 256;
|
||||
|
||||
|
@ -83,7 +83,7 @@ namespace nv {
|
||||
uint countMipmaps(uint w, uint h, uint d);
|
||||
uint countMipmapsWithMinSize(uint w, uint h, uint d, uint min_size);
|
||||
uint computeImageSize(uint w, uint h, uint d, uint bitCount, uint alignmentInBytes, nvtt::Format format);
|
||||
void getTargetExtent(int * w, int * h, int * d, int maxExtent, nvtt::RoundMode roundMode, nvtt::TextureType textureType);
|
||||
void getTargetExtent(int * w, int * h, int * d, int maxExtent, nvtt::RoundMode roundMode, nvtt::TextureType textureType, nvtt::ShapeRestriction shapeRestriction = nvtt::ShapeRestriction_None);
|
||||
}
|
||||
|
||||
|
||||
|
@ -10,8 +10,8 @@
|
||||
// Gran Central Dispatch (GCD/libdispatch)
|
||||
// http://developer.apple.com/mac/library/documentation/Performance/Reference/GCD_libdispatch_Ref/Reference/reference.html
|
||||
#if NV_OS_DARWIN && defined(HAVE_DISPATCH_H)
|
||||
#define HAVE_GCD 1
|
||||
#include <dispatch/dispatch.h>
|
||||
//#define HAVE_GCD 1
|
||||
//#include <dispatch/dispatch.h>
|
||||
#endif
|
||||
|
||||
// Parallel Patterns Library (PPL) is part of Microsoft's concurrency runtime:
|
||||
@ -64,7 +64,7 @@ namespace nvtt {
|
||||
|
||||
#endif
|
||||
|
||||
#if NV_OS_DARWIN && defined(HAVE_DISPATCH_H)
|
||||
#if HAVE_GCD
|
||||
|
||||
// Task dispatcher using Apple's Grand Central Dispatch.
|
||||
struct AppleTaskDispatcher : public TaskDispatcher
|
||||
|
@ -47,9 +47,9 @@ const char * nvtt::errorString(Error e)
|
||||
return "Error writing through output handler";
|
||||
case Error_UnsupportedOutputFormat:
|
||||
return "The container file does not support the selected output format";
|
||||
default:
|
||||
return "Invalid error";
|
||||
}
|
||||
|
||||
return "Invalid error";
|
||||
}
|
||||
|
||||
// Return NVTT version.
|
||||
|
@ -105,7 +105,21 @@ namespace nvtt
|
||||
Format_BC6,
|
||||
Format_BC7,
|
||||
|
||||
Format_BC3_RGBM, //
|
||||
Format_BC3_RGBM,
|
||||
|
||||
Format_ETC1,
|
||||
Format_ETC2_R,
|
||||
Format_ETC2_RG,
|
||||
Format_ETC2_RGB,
|
||||
Format_ETC2_RGBA,
|
||||
Format_ETC2_RGB_A1,
|
||||
|
||||
Format_ETC2_RGBM,
|
||||
|
||||
Format_PVR_2BPP_RGB, // Using PVR textools.
|
||||
Format_PVR_4BPP_RGB,
|
||||
Format_PVR_2BPP_RGBA,
|
||||
Format_PVR_4BPP_RGBA,
|
||||
|
||||
Format_Count
|
||||
};
|
||||
@ -155,6 +169,7 @@ namespace nvtt
|
||||
NVTT_API void setFormat(Format format);
|
||||
NVTT_API void setQuality(Quality quality);
|
||||
NVTT_API void setColorWeights(float red, float green, float blue, float alpha = 1.0f);
|
||||
NVTT_API void setRGBMThreshold(float min_m);
|
||||
|
||||
NVTT_API void setExternalCompressor(const char * name);
|
||||
|
||||
@ -173,9 +188,10 @@ namespace nvtt
|
||||
NVTT_API void setTargetDecoder(Decoder decoder);
|
||||
|
||||
// Translate to and from D3D formats.
|
||||
NVTT_API Format format() const;
|
||||
NVTT_API unsigned int d3d9Format() const;
|
||||
NVTT_API unsigned int dxgiFormat() const;
|
||||
//NVTT_API bool setD3D9Format(unsigned int format);
|
||||
//NVTT_API unsigned int dxgiFormat() const;
|
||||
//NVTT_API bool setDxgiFormat(unsigned int format);
|
||||
};
|
||||
|
||||
@ -253,6 +269,14 @@ namespace nvtt
|
||||
AlphaMode_Premultiplied,
|
||||
};
|
||||
|
||||
// Extents shape restrictions
|
||||
enum ShapeRestriction
|
||||
{
|
||||
ShapeRestriction_None,
|
||||
ShapeRestriction_Square,
|
||||
};
|
||||
|
||||
|
||||
// Input options. Specify format and layout of the input texture. (Deprecated in NVTT 2.1)
|
||||
struct InputOptions
|
||||
{
|
||||
@ -344,7 +368,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
|
||||
};
|
||||
|
||||
@ -439,6 +463,9 @@ namespace nvtt
|
||||
ToneMapper_Lightmap,
|
||||
};
|
||||
|
||||
// Transform the given x,y coordinates.
|
||||
typedef void WarpFunction(float & x, float & y, float & d);
|
||||
|
||||
|
||||
// A surface is one level of a 2D or 3D texture. (New in NVTT 2.1)
|
||||
// @@ It would be nice to add support for texture borders for correct resizing of tiled textures and constrained DXT compression.
|
||||
@ -486,7 +513,8 @@ namespace nvtt
|
||||
NVTT_API void resize(int w, int h, int d, ResizeFilter filter, float filterWidth, const float * params = 0);
|
||||
NVTT_API void resize(int maxExtent, RoundMode mode, ResizeFilter filter);
|
||||
NVTT_API void resize(int maxExtent, RoundMode mode, ResizeFilter filter, float filterWidth, const float * params = 0);
|
||||
NVTT_API void resize_make_square(int maxExtent, RoundMode roundMode, ResizeFilter filter);
|
||||
NVTT_API void resizeMakeSquare(int maxExtent, RoundMode roundMode, ResizeFilter filter);
|
||||
NVTT_API void autoResize(float errorTolerance, RoundMode mode, ResizeFilter filter);
|
||||
|
||||
NVTT_API bool buildNextMipmap(MipmapFilter filter, int min_size = 1);
|
||||
NVTT_API bool buildNextMipmap(MipmapFilter filter, float filterWidth, const float * params = 0, int min_size = 1);
|
||||
@ -554,6 +582,10 @@ namespace nvtt
|
||||
NVTT_API void flipZ();
|
||||
NVTT_API Surface createSubImage(int x0, int x1, int y0, int y1, int z0, int z1) const;
|
||||
|
||||
NVTT_API Surface warp(int w, int h, WarpFunction * f) const;
|
||||
NVTT_API Surface warp(int w, int h, int d, WarpFunction * f) const;
|
||||
|
||||
|
||||
// Copy image data.
|
||||
NVTT_API bool copyChannel(const Surface & srcImage, int srcChannel);
|
||||
NVTT_API bool copyChannel(const Surface & srcImage, int srcChannel, int dstChannel);
|
||||
|
@ -146,9 +146,16 @@ static const char * s_witnessImageSet[] = {
|
||||
};
|
||||
|
||||
static const char * s_witnessLmapImageSet[] = {
|
||||
"specruin.dds",
|
||||
"cottage.dds",
|
||||
"hallway.dds",
|
||||
"windmill.dds",
|
||||
"tunnel.dds",
|
||||
"theater.dds",
|
||||
"tower.dds",
|
||||
"hub.dds",
|
||||
"mine.dds",
|
||||
"archway.dds",
|
||||
"hut.dds",
|
||||
"shaft.dds",
|
||||
};
|
||||
|
||||
static const char * s_normalMapImageSet[] = {
|
||||
@ -187,8 +194,14 @@ enum Mode {
|
||||
Mode_BC5_Normal_Paraboloid,
|
||||
Mode_BC5_Normal_Quartic,
|
||||
//Mode_BC5_Normal_DualParaboloid,
|
||||
Mode_BC6,
|
||||
Mode_BC7,
|
||||
Mode_BC6,
|
||||
Mode_BC7,
|
||||
Mode_ETC1_IC,
|
||||
Mode_ETC1_EtcLib,
|
||||
Mode_ETC2_EtcLib,
|
||||
Mode_ETC1_RgEtc,
|
||||
Mode_ETC2_RGBM,
|
||||
Mode_PVR,
|
||||
Mode_Count
|
||||
};
|
||||
static const char * s_modeNames[] = {
|
||||
@ -207,8 +220,14 @@ static const char * s_modeNames[] = {
|
||||
"BC5-Normal-Paraboloid", // Mode_BC5_Normal_Paraboloid,
|
||||
"BC5-Normal-Quartic", // Mode_BC5_Normal_Quartic,
|
||||
//"BC5-Normal-DualParaboloid", // Mode_BC5_Normal_DualParaboloid,
|
||||
"BC6", // Mode_BC6,
|
||||
"BC7", // Mode_BC7,
|
||||
"BC6", // Mode_BC6,
|
||||
"BC7", // Mode_BC7,
|
||||
"ETC1-IC",
|
||||
"ETC1-EtcLib",
|
||||
"ETC2-EtcLib",
|
||||
"ETC1-RgEtc",
|
||||
"ETC2-RGBM",
|
||||
"PVR",
|
||||
};
|
||||
nvStaticCheck(NV_ARRAY_SIZE(s_modeNames) == Mode_Count);
|
||||
|
||||
@ -218,14 +237,16 @@ struct Test {
|
||||
Mode modes[6];
|
||||
};
|
||||
static Test s_imageTests[] = {
|
||||
{"Color", 3, {Mode_BC1, Mode_BC3_YCoCg, Mode_BC3_RGBM, /*Mode_BC3_LUVW*/}},
|
||||
{"Alpha", 3, {Mode_BC1_Alpha, Mode_BC2_Alpha, Mode_BC3_Alpha}},
|
||||
//{"Normal", 3, {Mode_BC1_Normal, Mode_BC3_Normal, Mode_BC5_Normal}},
|
||||
{"Normal", 4, {Mode_BC5_Normal, Mode_BC5_Normal_Stereographic, Mode_BC5_Normal_Paraboloid, Mode_BC5_Normal_Quartic}},
|
||||
{"Lightmap", 4, {Mode_BC1, Mode_BC3_YCoCg, Mode_BC3_RGBM, Mode_BC3_RGBS}},
|
||||
{"HDR", 2, {Mode_BC3_RGBM, Mode_BC6}},
|
||||
{"BC6", 1, {Mode_BC6}},
|
||||
{"BC7", 1, {Mode_BC7}},
|
||||
/*0*/ {"Color", 3, {Mode_BC1, Mode_BC3_YCoCg, Mode_BC3_RGBM, /*Mode_BC3_LUVW*/}},
|
||||
/*1*/ {"Alpha", 3, {Mode_BC1_Alpha, Mode_BC2_Alpha, Mode_BC3_Alpha}},
|
||||
/*2*/ {"Normal", 4, {Mode_BC5_Normal, Mode_BC5_Normal_Stereographic, Mode_BC5_Normal_Paraboloid, Mode_BC5_Normal_Quartic}},
|
||||
/*3*/ {"Lightmap", 4, {Mode_BC1, Mode_BC3_YCoCg, Mode_BC3_RGBM, Mode_BC3_RGBS}},
|
||||
/*4*/ {"HDR", 3, {Mode_ETC2_RGBM, Mode_BC3_RGBM, Mode_BC6}},
|
||||
/*5*/ {"BC6", 1, {Mode_BC6}},
|
||||
/*6*/ {"BC7", 1, {Mode_BC7}},
|
||||
/*7*/ {"ETC", 3, {Mode_ETC1_IC, Mode_ETC1_RgEtc, Mode_ETC2_EtcLib}},
|
||||
/*8*/ {"Color Mobile", 4, {Mode_PVR, Mode_ETC1_IC, Mode_ETC2_EtcLib, Mode_BC1}},
|
||||
/*9*/ //{"ETC-Lightmap", 2, {Mode_BC3_RGBM, Mode_ETC_RGBM}},
|
||||
};
|
||||
const int s_imageTestCount = ARRAY_SIZE(s_imageTests);
|
||||
|
||||
@ -404,10 +425,10 @@ int main(int argc, char *argv[])
|
||||
i++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("Warning: unrecognized option \"%s\"\n", argv[i]);
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("Warning: unrecognized option \"%s\"\n", argv[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// Validate inputs.
|
||||
@ -462,7 +483,8 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
else
|
||||
{
|
||||
compressionOptions.setQuality(nvtt::Quality_Production);
|
||||
compressionOptions.setQuality(nvtt::Quality_Normal);
|
||||
//compressionOptions.setQuality(nvtt::Quality_Production);
|
||||
}
|
||||
//compressionOptions.setExternalCompressor("ati");
|
||||
//compressionOptions.setExternalCompressor("squish");
|
||||
@ -515,13 +537,13 @@ int main(int argc, char *argv[])
|
||||
|
||||
// Labels on the left side.
|
||||
if (errorMode == ErrorMode_RMSE) {
|
||||
graphWriter << "&chxr=0,1," << set.fileCount << ",1|1,0,0.05,0.01";
|
||||
graphWriter << "&chxr=0,1," << set.fileCount << ",1|1,0,0.03,0.01";
|
||||
}
|
||||
else if (errorMode == ErrorMode_CieLab) {
|
||||
graphWriter << "&chxr=0,1," << set.fileCount << ",1|1,4,22,1";
|
||||
}
|
||||
else if (errorMode == ErrorMode_AngularRMSE) {
|
||||
graphWriter << "&chxr=0,1," << set.fileCount << ",1|1,0,0.05,0.01";
|
||||
graphWriter << "&chxr=0,1," << set.fileCount << ",1|1,0,0.2,0.02"; // 0.05,0.01
|
||||
}
|
||||
|
||||
// Labels at the bottom.
|
||||
@ -581,7 +603,6 @@ int main(int argc, char *argv[])
|
||||
else if (errorMode == ErrorMode_AngularRMSE) {
|
||||
graphWriter << "&chtt=" << set.name << "%20-%20" << test.name << "%20-%20Angular RMSE";
|
||||
}
|
||||
|
||||
|
||||
|
||||
Timer timer;
|
||||
@ -590,7 +611,7 @@ int main(int argc, char *argv[])
|
||||
|
||||
nvtt::Surface img;
|
||||
|
||||
printf("Running Test: %s with Set: %s\n", test.name, set.name);
|
||||
printf("Running test '%s' with set '%s'\n", test.name, set.name);
|
||||
|
||||
graphWriter << "&chd=t:";
|
||||
|
||||
@ -602,10 +623,11 @@ int main(int argc, char *argv[])
|
||||
Mode mode = test.modes[t];
|
||||
|
||||
nvtt::Format format;
|
||||
const char * compressor_name = NULL;
|
||||
if (mode == Mode_BC1 || mode == Mode_BC1_Alpha || mode == Mode_BC1_Normal || mode == Mode_BC3_RGBS) {
|
||||
format = nvtt::Format_BC1;
|
||||
}
|
||||
else if (mode == Mode_BC3_Alpha || mode == Mode_BC3_YCoCg || mode == Mode_BC3_RGBM || mode == Mode_BC3_LUVW) {
|
||||
else if (mode == Mode_BC3_Alpha || mode == Mode_BC3_YCoCg || mode == Mode_BC3_LUVW) {
|
||||
format = nvtt::Format_BC3;
|
||||
}
|
||||
else if (mode == Mode_BC3_Normal) {
|
||||
@ -614,20 +636,51 @@ int main(int argc, char *argv[])
|
||||
else if (mode == Mode_BC5_Normal || mode == Mode_BC5_Normal_Stereographic || mode == Mode_BC5_Normal_Paraboloid || mode == Mode_BC5_Normal_Quartic) {
|
||||
format = nvtt::Format_BC5;
|
||||
}
|
||||
else if (mode == Mode_BC6)
|
||||
{
|
||||
format = nvtt::Format_BC6;
|
||||
}
|
||||
else if (mode == Mode_BC7)
|
||||
{
|
||||
format = nvtt::Format_BC7;
|
||||
}
|
||||
else
|
||||
{
|
||||
nvDebugCheck(false);
|
||||
}
|
||||
else if (mode == Mode_BC3_RGBM) {
|
||||
format = nvtt::Format_BC3_RGBM;
|
||||
}
|
||||
else if (mode == Mode_BC6)
|
||||
{
|
||||
format = nvtt::Format_BC6;
|
||||
}
|
||||
else if (mode == Mode_BC7)
|
||||
{
|
||||
format = nvtt::Format_BC7;
|
||||
}
|
||||
else if (mode == Mode_ETC1_IC)
|
||||
{
|
||||
format = nvtt::Format_ETC1;
|
||||
}
|
||||
else if (mode == Mode_ETC1_EtcLib)
|
||||
{
|
||||
format = nvtt::Format_ETC1;
|
||||
compressor_name = "etclib";
|
||||
}
|
||||
else if (mode == Mode_ETC2_EtcLib)
|
||||
{
|
||||
format = nvtt::Format_ETC2_RGB;
|
||||
compressor_name = "etclib";
|
||||
}
|
||||
else if (mode == Mode_ETC1_RgEtc)
|
||||
{
|
||||
format = nvtt::Format_ETC1;
|
||||
compressor_name = "rg_etc";
|
||||
}
|
||||
else if (mode == Mode_ETC2_RGBM)
|
||||
{
|
||||
format = nvtt::Format_ETC2_RGBM;
|
||||
}
|
||||
else if (mode == Mode_PVR)
|
||||
{
|
||||
format = nvtt::Format_PVR_4BPP_RGB;
|
||||
}
|
||||
else
|
||||
{
|
||||
nvUnreachable();
|
||||
}
|
||||
|
||||
compressionOptions.setFormat(format);
|
||||
if (compressor_name) compressionOptions.setExternalCompressor(compressor_name);
|
||||
|
||||
if (set.type == ImageType_RGBA) {
|
||||
img.setAlphaMode(nvtt::AlphaMode_Transparency);
|
||||
@ -653,6 +706,7 @@ int main(int argc, char *argv[])
|
||||
printf("Input image '%s' not found.\n", set.fileNames[i]);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
float color_range = 0.0f;
|
||||
|
||||
if (img.isNormalMap()) {
|
||||
img.normalizeNormalMap();
|
||||
@ -693,16 +747,34 @@ int main(int argc, char *argv[])
|
||||
tmp.clamp(2);
|
||||
tmp.clamp(3);
|
||||
}
|
||||
else if (mode == Mode_BC3_RGBM) {
|
||||
tmp.setAlphaMode(nvtt::AlphaMode_None);
|
||||
if (set.type == ImageType_HDR) {
|
||||
// Transform to gamma-2.0 space before applying RGBM - helps a lot with banding in the darks.
|
||||
tmp.toGamma(2.0f);
|
||||
tmp.toRGBM(3.0f); // range of 3.0 in gamma-2.0 space == range of 9.0 in linear space
|
||||
else if (mode == Mode_BC3_RGBM || mode == Mode_ETC2_RGBM) {
|
||||
float r, g, b;
|
||||
tmp.range(0, NULL, &r);
|
||||
tmp.range(1, NULL, &g);
|
||||
tmp.range(2, NULL, &b);
|
||||
color_range = max3(r, g, b);
|
||||
printf("color range = %f\n", color_range);
|
||||
|
||||
tmp.setAlphaMode(nvtt::AlphaMode_Transparency);
|
||||
|
||||
const float max_color_range = 16.0f;
|
||||
|
||||
if (color_range > max_color_range) {
|
||||
color_range = max_color_range;
|
||||
}
|
||||
else {
|
||||
tmp.toRGBM();
|
||||
|
||||
for (int i = 0; i < 3; i++) {
|
||||
tmp.scaleBias(i, 1.0f / color_range, 0.0f);
|
||||
}
|
||||
tmp.toneMap(nvtt::ToneMapper_Linear, /*parameters=*/NULL); // Clamp without changing the hue.
|
||||
|
||||
// Clamp alpha.
|
||||
tmp.clamp(3);
|
||||
|
||||
// To gamma.
|
||||
tmp.toGamma(2);
|
||||
|
||||
compressionOptions.setRGBMThreshold(0.2f);
|
||||
}
|
||||
else if (mode == Mode_BC3_LUVW) {
|
||||
tmp.setAlphaMode(nvtt::AlphaMode_None);
|
||||
@ -781,14 +853,25 @@ int main(int argc, char *argv[])
|
||||
}*/
|
||||
}
|
||||
}
|
||||
else if (mode == Mode_BC3_RGBM) {
|
||||
if (set.type == ImageType_HDR) {
|
||||
img_out.fromRGBM(3.0f);
|
||||
img_out.toLinear(2.0f);
|
||||
else if (mode == Mode_BC3_RGBM || mode == Mode_ETC2_RGBM) {
|
||||
/*if (set.type == ImageType_HDR) {
|
||||
//img_out.fromRGBM(3.0f);
|
||||
img_out.fromRGBM(range);
|
||||
img_out.toLinear(2.0f);
|
||||
}
|
||||
else {
|
||||
img_out.fromRGBM();
|
||||
}*/
|
||||
|
||||
img_out.fromRGBM(1.0f, 0.2f);
|
||||
img_out.toLinear(2);
|
||||
|
||||
for (int i = 0; i < 3; i++) {
|
||||
img_out.scaleBias(i, color_range, 0.0f);
|
||||
}
|
||||
|
||||
img_out.copyChannel(img, 3); // Copy alpha channel from source.
|
||||
img_out.setAlphaMode(nvtt::AlphaMode_Transparency);
|
||||
}
|
||||
else if (mode == Mode_BC3_LUVW) {
|
||||
if (set.type == ImageType_HDR) {
|
||||
|
@ -61,6 +61,9 @@ struct MyAssertHandler : public nv::AssertHandler {
|
||||
virtual int assertion( const char *exp, const char *file, int line, const char *func, const char *msg, va_list arg ) {
|
||||
fprintf(stderr, "Assertion failed: %s\nIn %s:%d\n", exp, file, line);
|
||||
nv::debug::dumpInfo();
|
||||
if (nv::debug::isDebuggerPresent()) {
|
||||
return NV_ABORT_DEBUG;
|
||||
}
|
||||
exit(1);
|
||||
}
|
||||
};
|
||||
|
@ -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;
|
||||
@ -285,6 +287,31 @@ int main(int argc, char *argv[])
|
||||
format = nvtt::Format_BC3_RGBM;
|
||||
rgbm = true;
|
||||
}
|
||||
else if (strcmp("-etc1", argv[i]) == 0)
|
||||
{
|
||||
format = nvtt::Format_ETC1;
|
||||
}
|
||||
else if (strcmp("-etc2", argv[i]) == 0 || strcmp("-etc2_rgb", argv[i]) == 0)
|
||||
{
|
||||
format = nvtt::Format_ETC2_RGB;
|
||||
}
|
||||
else if (strcmp("-etc2_eac", argv[i]) == 0 || strcmp("-etc2_rgba", argv[i]) == 0)
|
||||
{
|
||||
format = nvtt::Format_ETC2_RGBA;
|
||||
}
|
||||
else if (strcmp("-eac", argv[i]) == 0 || strcmp("-etc2_r", argv[i]) == 0)
|
||||
{
|
||||
format = nvtt::Format_ETC2_R;
|
||||
}
|
||||
else if (strcmp("-etc2_rg", argv[i]) == 0)
|
||||
{
|
||||
format = nvtt::Format_ETC2_R;
|
||||
}
|
||||
else if (strcmp("-etc2_rgbm", argv[i]) == 0)
|
||||
{
|
||||
format = nvtt::Format_ETC2_RGBM;
|
||||
rgbm = true;
|
||||
}
|
||||
|
||||
// Undocumented option. Mainly used for testing.
|
||||
else if (strcmp("-ext", argv[i]) == 0)
|
||||
@ -309,7 +336,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,15 +356,23 @@ 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;
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("Warning: unrecognized option \"%s\"\n", argv[i]);
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("Warning: unrecognized option \"%s\"\n", argv[i]);
|
||||
}
|
||||
}
|
||||
|
||||
const uint version = nvtt::version();
|
||||
@ -380,7 +423,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;
|
||||
}
|
||||
@ -398,7 +443,7 @@ int main(int argc, char *argv[])
|
||||
bool useSurface = false; // @@ use Surface API in all cases!
|
||||
nvtt::Surface image;
|
||||
|
||||
if (format == nvtt::Format_BC3_RGBM || rgbm) {
|
||||
if (format == nvtt::Format_BC3_RGBM || format == nvtt::Format_ETC2_RGBM || rgbm) {
|
||||
useSurface = true;
|
||||
|
||||
if (!image.load(input.str())) {
|
||||
@ -440,7 +485,7 @@ int main(int argc, char *argv[])
|
||||
// To gamma.
|
||||
image.toGamma(2);
|
||||
|
||||
if (format != nvtt::Format_BC3_RGBM) {
|
||||
if (format != nvtt::Format_BC3_RGBM || format != nvtt::Format_ETC2_RGBM) {
|
||||
image.setAlphaMode(nvtt::AlphaMode_None);
|
||||
image.toRGBM(1, 0.15f);
|
||||
}
|
||||
@ -494,7 +539,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();
|
||||
@ -569,11 +614,12 @@ int main(int argc, char *argv[])
|
||||
inputOptions.setAlphaMode(nvtt::AlphaMode_None);
|
||||
}
|
||||
|
||||
// IC: Do not enforce D3D9 restrictions anymore.
|
||||
// Block compressed textures with mipmaps must be powers of two.
|
||||
if (!noMipmaps && format != nvtt::Format_RGB)
|
||||
/*if (!noMipmaps && format != nvtt::Format_RGB)
|
||||
{
|
||||
inputOptions.setRoundMode(nvtt::RoundMode_ToPreviousPowerOfTwo);
|
||||
}
|
||||
}*/
|
||||
|
||||
if (normal)
|
||||
{
|
||||
@ -720,15 +766,27 @@ int main(int argc, char *argv[])
|
||||
outputOptions.setOutputHandler(&outputHandler);
|
||||
outputOptions.setErrorHandler(&errorHandler);
|
||||
|
||||
// Automatically use dds10 if compressing to BC6 or BC7
|
||||
if (format == nvtt::Format_BC6 || format == nvtt::Format_BC7)
|
||||
{
|
||||
dds10 = true;
|
||||
}
|
||||
|
||||
if (dds10)
|
||||
if (ktx)
|
||||
{
|
||||
outputOptions.setContainer(nvtt::Container_DDS10);
|
||||
outputOptions.setContainer(nvtt::Container_KTX);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Automatically use dds10 if compressing to BC6 or BC7
|
||||
if (format == nvtt::Format_BC6 || format == nvtt::Format_BC7) {
|
||||
dds10 = true;
|
||||
}
|
||||
|
||||
if (dds10) {
|
||||
outputOptions.setContainer(nvtt::Container_DDS10);
|
||||
}
|
||||
else {
|
||||
outputOptions.setContainer(nvtt::Container_DDS);
|
||||
}
|
||||
}
|
||||
|
||||
if (srgb) {
|
||||
outputOptions.setSrgbFlag(true);
|
||||
}
|
||||
|
||||
// printf("Press ENTER.\n");
|
||||
|
@ -99,8 +99,8 @@ int main(int argc, char *argv[])
|
||||
return 1;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (input.isNull() || output.isNull())
|
||||
@ -136,21 +136,21 @@ int main(int argc, char *argv[])
|
||||
nv::FloatImage fimage(&image);
|
||||
fimage.toLinear(0, 3, gamma);
|
||||
|
||||
uint thumbW, thumbH;
|
||||
if (image.width() > image.height())
|
||||
{
|
||||
thumbW = size;
|
||||
thumbH = uint ((float (image.height()) / float (image.width())) * size);
|
||||
}
|
||||
else
|
||||
{
|
||||
thumbW = uint ((float (image.width()) / float (image.height())) * size);
|
||||
thumbH = size;
|
||||
}
|
||||
nv::AutoPtr<nv::FloatImage> fresult(fimage.resize(nv::BoxFilter(), thumbW, thumbH, nv::FloatImage::WrapMode_Clamp));
|
||||
uint thumbW, thumbH;
|
||||
if (image.width() > image.height())
|
||||
{
|
||||
thumbW = size;
|
||||
thumbH = uint ((float (image.height()) / float (image.width())) * size);
|
||||
}
|
||||
else
|
||||
{
|
||||
thumbW = uint ((float (image.width()) / float (image.height())) * size);
|
||||
thumbH = size;
|
||||
}
|
||||
nv::AutoPtr<nv::FloatImage> fresult(fimage.resize(nv::BoxFilter(), thumbW, thumbH, nv::FloatImage::WrapMode_Clamp));
|
||||
|
||||
nv::AutoPtr<nv::Image> result(fresult->createImageGammaCorrect(gamma));
|
||||
result->setFormat(nv::Image::Format_ARGB);
|
||||
nv::AutoPtr<nv::Image> result(fresult->createImageGammaCorrect(gamma));
|
||||
result->setFormat(nv::Image::Format_ARGB);
|
||||
|
||||
nv::StdOutputStream stream(output.str());
|
||||
nv::ImageIO::save(output.str(), stream, result.ptr(), metaData.buffer());
|
||||
@ -160,7 +160,7 @@ int main(int argc, char *argv[])
|
||||
nv::StdOutputStream stream(output.str());
|
||||
nv::ImageIO::save(output.str(), stream, &image, metaData.buffer());
|
||||
}
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user