From 27206b10584fd0967a493a5fd442ea4de17a833b Mon Sep 17 00:00:00 2001 From: castano Date: Wed, 4 Jan 2012 02:25:28 +0000 Subject: [PATCH] HDR encoding tests. --- trunk/src/nvtt/ClusterFit.h | 2 +- trunk/src/nvtt/CompressionOptions.cpp | 2 +- trunk/src/nvtt/CompressionOptions.h | 4 +- trunk/src/nvtt/CompressorDX9.cpp | 268 +++++++++---------- trunk/src/nvtt/CompressorDXT.cpp | 8 +- trunk/src/nvtt/CubeSurface.cpp | 1 + trunk/src/nvtt/QuickCompressDXT.cpp | 45 ++-- trunk/src/nvtt/Surface.cpp | 63 ++++- trunk/src/nvtt/nvtt.h | 10 +- trunk/src/nvtt/tests/CMakeLists.txt | 5 +- trunk/src/nvtt/tests/GoogleCharts.h | 164 ++++++++++++ trunk/src/nvtt/tests/hdrtest.cpp | 359 ++++++++++++++++++++++++++ trunk/src/nvtt/tools/cmdline.h | 1 + 13 files changed, 756 insertions(+), 176 deletions(-) create mode 100644 trunk/src/nvtt/tests/GoogleCharts.h create mode 100644 trunk/src/nvtt/tests/hdrtest.cpp diff --git a/trunk/src/nvtt/ClusterFit.h b/trunk/src/nvtt/ClusterFit.h index 6e85217..e8c011c 100644 --- a/trunk/src/nvtt/ClusterFit.h +++ b/trunk/src/nvtt/ClusterFit.h @@ -45,7 +45,7 @@ namespace nv { void setColourSet(const ColorSet * set); - void setMetric(Vector4::Arg w); + void setMetric(const Vector4 & w); float bestError() const; bool compress3(Vector3 * start, Vector3 * end); diff --git a/trunk/src/nvtt/CompressionOptions.cpp b/trunk/src/nvtt/CompressionOptions.cpp index 187ea4e..92cbadf 100644 --- a/trunk/src/nvtt/CompressionOptions.cpp +++ b/trunk/src/nvtt/CompressionOptions.cpp @@ -22,9 +22,9 @@ // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR // OTHER DEALINGS IN THE SOFTWARE. -#include "nvtt.h" #include "CompressionOptions.h" #include "nvimage/DirectDrawSurface.h" +#include "nvmath/Vector.inl" using namespace nv; using namespace nvtt; diff --git a/trunk/src/nvtt/CompressionOptions.h b/trunk/src/nvtt/CompressionOptions.h index c7df221..7612f8f 100644 --- a/trunk/src/nvtt/CompressionOptions.h +++ b/trunk/src/nvtt/CompressionOptions.h @@ -25,9 +25,9 @@ #ifndef NV_TT_COMPRESSIONOPTIONS_H #define NV_TT_COMPRESSIONOPTIONS_H -#include -#include #include "nvtt.h" +#include "nvmath/Vector.h" +#include "nvcore/StrLib.h" namespace nvtt { diff --git a/trunk/src/nvtt/CompressorDX9.cpp b/trunk/src/nvtt/CompressorDX9.cpp index 60e8611..479d2af 100644 --- a/trunk/src/nvtt/CompressorDX9.cpp +++ b/trunk/src/nvtt/CompressorDX9.cpp @@ -35,12 +35,14 @@ #include "nvtt.h" -#include "nvcore/Memory.h" - #include "nvimage/Image.h" #include "nvimage/ColorBlock.h" #include "nvimage/BlockDXT.h" +#include "nvmath/Vector.inl" + +#include "nvcore/Memory.h" + #include // placement new // s3_quant @@ -79,34 +81,34 @@ 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); + BlockDXT1 * block = new(output) BlockDXT1; + QuickCompress::compressDXT1(rgba, block); } void FastCompressorDXT1a::compressBlock(ColorBlock & rgba, nvtt::AlphaMode alphaMode, const nvtt::CompressionOptions::Private & compressionOptions, void * output) { - BlockDXT1 * block = new(output) BlockDXT1; - QuickCompress::compressDXT1a(rgba, block); + BlockDXT1 * block = new(output) BlockDXT1; + QuickCompress::compressDXT1a(rgba, block); } void FastCompressorDXT3::compressBlock(ColorBlock & rgba, nvtt::AlphaMode alphaMode, const nvtt::CompressionOptions::Private & compressionOptions, void * output) { - BlockDXT3 * block = new(output) BlockDXT3; - QuickCompress::compressDXT3(rgba, block); + BlockDXT3 * block = new(output) BlockDXT3; + QuickCompress::compressDXT3(rgba, block); } void FastCompressorDXT5::compressBlock(ColorBlock & rgba, nvtt::AlphaMode alphaMode, const nvtt::CompressionOptions::Private & compressionOptions, void * output) { - BlockDXT5 * block = new(output) BlockDXT5; - QuickCompress::compressDXT5(rgba, block); + BlockDXT5 * block = new(output) BlockDXT5; + QuickCompress::compressDXT5(rgba, block); } void FastCompressorDXT5n::compressBlock(ColorBlock & rgba, nvtt::AlphaMode alphaMode, const nvtt::CompressionOptions::Private & compressionOptions, void * output) { - rgba.swizzle(4, 1, 5, 0); // 0xFF, G, 0, R + rgba.swizzle(4, 1, 5, 0); // 0xFF, G, 0, R - BlockDXT5 * block = new(output) BlockDXT5; - QuickCompress::compressDXT5(rgba, block); + BlockDXT5 * block = new(output) BlockDXT5; + QuickCompress::compressDXT5(rgba, block); } #if 1 @@ -115,189 +117,189 @@ void NormalCompressorDXT1::compressBlock(ColorSet & set, nvtt::AlphaMode alphaMo set.setUniformWeights(); set.createMinimalSet(false); - ClusterFit fit; - fit.setMetric(compressionOptions.colorWeight); + ClusterFit fit; + fit.setMetric(compressionOptions.colorWeight); BlockDXT1 * block = new(output) BlockDXT1; if (set.isSingleColor(true)) - { + { Color32 c; c.r = uint8(clamp(set.colors[0].x, 0.0f, 1.0f) * 255); c.g = uint8(clamp(set.colors[0].y, 0.0f, 1.0f) * 255); c.b = uint8(clamp(set.colors[0].z, 0.0f, 1.0f) * 255); c.a = 255; - OptimalCompress::compressDXT1(c, block); - } - else - { - fit.setColourSet(&set); - + OptimalCompress::compressDXT1(c, block); + } + else + { + fit.setColourSet(&set); + Vector3 start, end; - + fit.compress4(&start, &end); QuickCompress::outputBlock4(set, start, end, block); if (fit.compress3(&start, &end)) { QuickCompress::outputBlock3(set, start, end, block); } - } + } } #else void NormalCompressorDXT1::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); + 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); - } + 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 NormalCompressorDXT1a::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. - } + for (uint i = 0; i < 16; i++) + { + if (rgba.color(i).a == 0) alphaMask |= (3 << (i * 2)); // Set two bits for each color. + } - const bool isSingleColor = rgba.isSingleColor(); - - if (isSingleColor) - { - BlockDXT1 * block = new(output) BlockDXT1; + const bool isSingleColor = rgba.isSingleColor(); + + if (isSingleColor) + { + BlockDXT1 * block = new(output) BlockDXT1; OptimalCompress::compressDXT1a(rgba.color(0), alphaMask, block); - } - else - { - nvsquish::WeightedClusterFit fit; - fit.SetMetric(compressionOptions.colorWeight.x, compressionOptions.colorWeight.y, compressionOptions.colorWeight.z); + } + else + { + nvsquish::WeightedClusterFit fit; + fit.SetMetric(compressionOptions.colorWeight.x, compressionOptions.colorWeight.y, compressionOptions.colorWeight.z); - int flags = nvsquish::kDxt1; - if (alphaMode == nvtt::AlphaMode_Transparency) flags |= nvsquish::kWeightColourByAlpha; + int flags = nvsquish::kDxt1; + if (alphaMode == nvtt::AlphaMode_Transparency) flags |= nvsquish::kWeightColourByAlpha; - nvsquish::ColourSet colours((uint8 *)rgba.colors(), flags); - fit.SetColourSet(&colours, nvsquish::kDxt1); + nvsquish::ColourSet colours((uint8 *)rgba.colors(), flags); + fit.SetColourSet(&colours, nvsquish::kDxt1); - fit.Compress(output); - } + fit.Compress(output); + } } void NormalCompressorDXT3::compressBlock(ColorBlock & rgba, nvtt::AlphaMode alphaMode, const nvtt::CompressionOptions::Private & compressionOptions, void * output) { - BlockDXT3 * block = new(output) BlockDXT3; + BlockDXT3 * block = new(output) BlockDXT3; - // Compress explicit alpha. - OptimalCompress::compressDXT3A(rgba, &block->alpha); + // Compress explicit alpha. + OptimalCompress::compressDXT3A(rgba, &block->alpha); - // 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); + // 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; + 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); - } + nvsquish::ColourSet colours((uint8 *)rgba.colors(), flags); + fit.SetColourSet(&colours, 0); + fit.Compress(&block->color); + } } void NormalCompressorDXT5::compressBlock(ColorBlock & rgba, nvtt::AlphaMode alphaMode, const nvtt::CompressionOptions::Private & compressionOptions, void * output) { - BlockDXT5 * block = new(output) BlockDXT5; + BlockDXT5 * block = new(output) BlockDXT5; - // Compress alpha. - if (compressionOptions.quality == Quality_Highest) - { - OptimalCompress::compressDXT5A(rgba, &block->alpha); - } - else - { - QuickCompress::compressDXT5A(rgba, &block->alpha); - } + // Compress alpha. + if (compressionOptions.quality == Quality_Highest) + { + OptimalCompress::compressDXT5A(rgba, &block->alpha); + } + else + { + QuickCompress::compressDXT5A(rgba, &block->alpha); + } - // 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); + // 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; + 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); - } + nvsquish::ColourSet colours((uint8 *)rgba.colors(), flags); + fit.SetColourSet(&colours, 0); + fit.Compress(&block->color); + } } void NormalCompressorDXT5n::compressBlock(ColorBlock & rgba, nvtt::AlphaMode alphaMode, const nvtt::CompressionOptions::Private & compressionOptions, void * output) { - BlockDXT5 * block = new(output) BlockDXT5; + BlockDXT5 * block = new(output) BlockDXT5; - // Compress Y. - if (compressionOptions.quality == Quality_Highest) - { - OptimalCompress::compressDXT1G(rgba, &block->color); - } - else - { - if (rgba.isSingleColor(Color32(0, 0xFF, 0, 0))) // Mask all but green channel. - { - OptimalCompress::compressDXT1G(rgba.color(0).g, &block->color); - } - else - { + // Compress Y. + if (compressionOptions.quality == Quality_Highest) + { + OptimalCompress::compressDXT1G(rgba, &block->color); + } + else + { + if (rgba.isSingleColor(Color32(0, 0xFF, 0, 0))) // Mask all but green channel. + { + OptimalCompress::compressDXT1G(rgba.color(0).g, &block->color); + } + else + { ColorBlock tile = rgba; tile.swizzle(4, 1, 5, 3); // leave alpha in alpha channel. - nvsquish::WeightedClusterFit fit; - fit.SetMetric(0, 1, 0); + nvsquish::WeightedClusterFit fit; + fit.SetMetric(0, 1, 0); - int flags = 0; - if (alphaMode == nvtt::AlphaMode_Transparency) flags |= nvsquish::kWeightColourByAlpha; + int flags = 0; + if (alphaMode == nvtt::AlphaMode_Transparency) flags |= nvsquish::kWeightColourByAlpha; - nvsquish::ColourSet colours((uint8 *)tile.colors(), flags); - fit.SetColourSet(&colours, 0); - fit.Compress(&block->color); - } - } + nvsquish::ColourSet colours((uint8 *)tile.colors(), flags); + fit.SetColourSet(&colours, 0); + fit.Compress(&block->color); + } + } - rgba.swizzle(4, 1, 5, 0); // 1, G, 0, R + rgba.swizzle(4, 1, 5, 0); // 1, G, 0, R - // Compress X. - if (compressionOptions.quality == Quality_Highest) - { - OptimalCompress::compressDXT5A(rgba, &block->alpha); - } - else - { - QuickCompress::compressDXT5A(rgba, &block->alpha); - } + // Compress X. + if (compressionOptions.quality == Quality_Highest) + { + OptimalCompress::compressDXT5A(rgba, &block->alpha); + } + else + { + QuickCompress::compressDXT5A(rgba, &block->alpha); + } } diff --git a/trunk/src/nvtt/CompressorDXT.cpp b/trunk/src/nvtt/CompressorDXT.cpp index 10c9100..c614bd7 100644 --- a/trunk/src/nvtt/CompressorDXT.cpp +++ b/trunk/src/nvtt/CompressorDXT.cpp @@ -24,16 +24,16 @@ #include "CompressorDXT.h" #include "OutputOptions.h" - -#include "nvtt.h" #include "TaskDispatcher.h" -#include "nvcore/Memory.h" - #include "nvimage/Image.h" #include "nvimage/ColorBlock.h" #include "nvimage/BlockDXT.h" +#include "nvmath/Vector.inl" + +#include "nvcore/Memory.h" + #include // placement new diff --git a/trunk/src/nvtt/CubeSurface.cpp b/trunk/src/nvtt/CubeSurface.cpp index 24574a0..79cdb55 100644 --- a/trunk/src/nvtt/CubeSurface.cpp +++ b/trunk/src/nvtt/CubeSurface.cpp @@ -31,6 +31,7 @@ #include "nvcore/Array.h" #include "nvcore/StrLib.h" +#include // FLT_MAX using namespace nv; using namespace nvtt; diff --git a/trunk/src/nvtt/QuickCompressDXT.cpp b/trunk/src/nvtt/QuickCompressDXT.cpp index 214f66d..2833636 100644 --- a/trunk/src/nvtt/QuickCompressDXT.cpp +++ b/trunk/src/nvtt/QuickCompressDXT.cpp @@ -716,38 +716,39 @@ void QuickCompress::outputBlock4(const ColorSet & set, const Vector3 & start, co { Vector3 minColor = start * 255; Vector3 maxColor = end * 255; - uint16 color0 = roundAndExpand(&maxColor); - uint16 color1 = roundAndExpand(&minColor); + uint16 color0 = roundAndExpand(&maxColor); + uint16 color1 = roundAndExpand(&minColor); - if (color0 < color1) - { - swap(maxColor, minColor); - swap(color0, color1); - } + if (color0 < color1) + { + swap(maxColor, minColor); + swap(color0, color1); + } - block->col0 = Color16(color0); - block->col1 = Color16(color1); - block->indices = computeIndices4(set, maxColor / 255, minColor / 255); + block->col0 = Color16(color0); + block->col1 = Color16(color1); + block->indices = computeIndices4(set, maxColor / 255, minColor / 255); - //optimizeEndPoints4(set, block); + //optimizeEndPoints4(set, block); } void QuickCompress::outputBlock3(const ColorSet & set, const Vector3 & start, const Vector3 & end, BlockDXT1 * block) { Vector3 minColor = start * 255; Vector3 maxColor = end * 255; - uint16 color0 = roundAndExpand(&minColor); - uint16 color1 = roundAndExpand(&maxColor); + uint16 color0 = roundAndExpand(&minColor); + uint16 color1 = roundAndExpand(&maxColor); - if (color0 > color1) - { - swap(maxColor, minColor); - swap(color0, color1); - } + if (color0 > color1) + { + swap(maxColor, minColor); + swap(color0, color1); + } - block->col0 = Color16(color0); - block->col1 = Color16(color1); + block->col0 = Color16(color0); + block->col1 = Color16(color1); block->indices = computeIndices3(set, maxColor / 255, minColor / 255); - //optimizeEndPoints3(set, block); -} \ No newline at end of file + //optimizeEndPoints3(set, block); +} + diff --git a/trunk/src/nvtt/Surface.cpp b/trunk/src/nvtt/Surface.cpp index 5a82bc3..6e9cf73 100644 --- a/trunk/src/nvtt/Surface.cpp +++ b/trunk/src/nvtt/Surface.cpp @@ -93,6 +93,11 @@ namespace } return 0; } + + /*static int translateMask(int input) { + if (input > 0) return 1 << input; + return ~input; + }*/ } uint nv::countMipmaps(uint w) @@ -372,19 +377,22 @@ void Surface::histogram(int channel, float rangeMin, float rangeMax, int binCoun } } -void Surface::range(int channel, float * rangeMin, float * rangeMax) +void Surface::range(int channel, float * rangeMin, float * rangeMax) const { Vector2 range(FLT_MAX, -FLT_MAX); FloatImage * img = m->image; - float * c = img->channel(channel); - const uint count = img->pixelCount(); - for (uint p = 0; p < count; p++) { - float f = c[p]; - if (f < range.x) range.x = f; - if (f > range.y) - range.y = f; + if (m->image != NULL) + { + float * c = img->channel(channel); + + const uint count = img->pixelCount(); + for (uint p = 0; p < count; p++) { + float f = c[p]; + if (f < range.x) range.x = f; + if (f > range.y) range.y = f; + } } *rangeMin = range.x; @@ -1863,9 +1871,9 @@ void Surface::toneMap(ToneMapper tm, float * parameters) } else if (tm == ToneMapper_Halo) { for (uint i = 0; i < count; i++) { - r[i] = 1 - expf(-r[i]); - g[i] = 1 - expf(-g[i]); - b[i] = 1 - expf(-b[i]); + r[i] = 1 - exp2f(-r[i]); + g[i] = 1 - exp2f(-g[i]); + b[i] = 1 - exp2f(-b[i]); } } else if (tm == ToneMapper_Lightmap) { @@ -1884,6 +1892,39 @@ void Surface::toneMap(ToneMapper tm, float * parameters) } } +void Surface::toLogScale(int channel, float base) { + if (isNull()) return; + + detach(); + + FloatImage * img = m->image; + float * c = img->channel(channel); + + float scale = 1.0f / log2f(base); + + const uint count = img->pixelCount(); + for (uint i = 0; i < count; i++) { + c[i] = log2f(c[i]) * scale; + } +} + +void Surface::fromLogScale(int channel, float base) { + if (isNull()) return; + + detach(); + + FloatImage * img = m->image; + float * c = img->channel(channel); + + float scale = log2f(base); + + const uint count = img->pixelCount(); + for (uint i = 0; i < count; i++) { + c[i] = exp2f(c[i] * scale); + } +} + + /* void Surface::blockLuminanceScale(float scale) diff --git a/trunk/src/nvtt/nvtt.h b/trunk/src/nvtt/nvtt.h index e72dbb8..b849b77 100644 --- a/trunk/src/nvtt/nvtt.h +++ b/trunk/src/nvtt/nvtt.h @@ -420,6 +420,12 @@ namespace nvtt ToneMapper_Lightmap, }; + /*enum ChannelMask { + R = 0x70000001, + G = 0x70000002, + B = 0x70000004, + A = 0x70000008, + };*/ // A surface is one level of a 2D or 3D texture. // @@ It would be nice to add support for texture borders for correct resizing of tiled textures and constrained DXT compression. @@ -450,7 +456,7 @@ namespace nvtt NVTT_API float average(int channel, int alpha_channel = -1, float gamma = 2.2f) const; NVTT_API const float * data() const; NVTT_API void histogram(int channel, float rangeMin, float rangeMax, int binCount, int * binPtr) const; - NVTT_API void range(int channel, float * rangeMin, float * rangeMax); + NVTT_API void range(int channel, float * rangeMin, float * rangeMax) const; // Texture data. NVTT_API bool load(const char * fileName, bool * hasAlpha = 0); @@ -500,6 +506,8 @@ namespace nvtt NVTT_API void fromLUVW(float range = 1.0f); NVTT_API void abs(int channel); NVTT_API void convolve(int channel, int kernelSize, float * kernelData); + NVTT_API void toLogScale(int channel, float base); + NVTT_API void fromLogScale(int channel, float base); NVTT_API void toneMap(ToneMapper tm, float * parameters); diff --git a/trunk/src/nvtt/tests/CMakeLists.txt b/trunk/src/nvtt/tests/CMakeLists.txt index 6117863..069ccbf 100644 --- a/trunk/src/nvtt/tests/CMakeLists.txt +++ b/trunk/src/nvtt/tests/CMakeLists.txt @@ -25,7 +25,10 @@ TARGET_LINK_LIBRARIES(process_alpha_map nvcore nvmath nvimage nvtt) ADD_EXECUTABLE(cubemaptest cubemaptest.cpp) TARGET_LINK_LIBRARIES(cubemaptest nvcore nvmath nvimage nvtt) -INSTALL(TARGETS nvtestsuite DESTINATION bin) +ADD_EXECUTABLE(nvhdrtest hdrtest.cpp) +TARGET_LINK_LIBRARIES(nvhdrtest nvcore nvmath nvimage nvtt) + +INSTALL(TARGETS nvtestsuite nvhdrtest DESTINATION bin) #include_directories("/usr/include/ffmpeg/") #ADD_EXECUTABLE(nvmpegenc tools/mpegenc.cpp tools/cmdline.h) diff --git a/trunk/src/nvtt/tests/GoogleCharts.h b/trunk/src/nvtt/tests/GoogleCharts.h new file mode 100644 index 0000000..14ea499 --- /dev/null +++ b/trunk/src/nvtt/tests/GoogleCharts.h @@ -0,0 +1,164 @@ + +#include "nvmath/Vector.inl" +#include "nvcore/StrLib.h" + +namespace nv { + +struct GooglePointSet { + GooglePointSet(); + Vector2 min, max; + Array points; + const char * legend; + Vector3 lineColor; + int lineWidth; + bool dashed; + int lineSegmentLength; + int blankSegmentLength; +}; + +struct GoogleLineChart { + GoogleLineChart(); + void autoScale(); + void build(nv::StringBuilder & builder) const; + + const char * title; + int width; + int height; + + int leftMargin; + int rightMargin; + int topMargin; + int bottomMargin; + + int legendPosition; + + Array pointSetArray; +}; + + + +GooglePointSet::GooglePointSet() +{ + min.x = 0; + min.y = 0; + max.x = 1; + max.x = 1; + legend = NULL; + lineColor = Vector3(1, 0, 0); + lineWidth = 2; + dashed = false; + lineSegmentLength = 4; + blankSegmentLength = 1; +} + + +GoogleLineChart::GoogleLineChart() +{ + title = NULL; + width = 440; + height = 220; + + leftMargin = 5; + rightMargin = 5; + topMargin = 5; + bottomMargin = 25; + + legendPosition = 3; // bottom +} + +void GoogleLineChart::autoScale() +{ + Vector2 minimum, maximum; + foreach(i, pointSetArray) { + foreach(p, pointSetArray[i].points) { + Vector2 point = pointSetArray[i].points[p]; + minimum = min(minimum, point); + maximum = max(maximum, point); + } + } + + foreach(i, pointSetArray) { + pointSetArray[i].min = minimum; + pointSetArray[i].max = maximum; + } +} + +void GoogleLineChart::build(nv::StringBuilder & builder) const +{ + const uint lineCount = pointSetArray.count(); + + builder.reset(); + + // Start LineChartXY. + builder.copy("http://chart.apis.google.com/chart?cht=lxy"); + + // Size. + builder.appendFormat("&chs=%dx%d", width, height); + + // Title. + if (title != NULL) builder.appendFormat("&chtt=%s", title); + + // Margins. + builder.appendFormat("&chma=%d,%d,%d,%d", leftMargin, rightMargin, topMargin, bottomMargin); + + // Legend position. + builder.appendFormat("&chdlp=%c", "lrtb"[legendPosition]); + + // Line colors. + builder.append("&chco="); + for (uint i = 0; i < lineCount; i++) { + const GooglePointSet & set = pointSetArray[i]; + builder.appendFormat("%.2X%.2X%.2X", int(255 * set.lineColor.x), int(255 * set.lineColor.y), int(255 * set.lineColor.z)); + if (i != lineCount-1) builder.append(","); + } + + // Legends. + builder.append("&chdl="); + for (uint i = 0; i < lineCount; i++) { + const GooglePointSet & set = pointSetArray[i]; + if (set.legend != NULL) builder.append(set.legend); + if (i != lineCount-1) builder.append("|"); + } + + // Line format. + builder.append("&chls="); + for (uint i = 0; i < lineCount; i++) { + const GooglePointSet & set = pointSetArray[i]; + builder.appendFormat("%d", set.lineWidth); + if (set.dashed) builder.appendFormat(",%d,%d", set.lineSegmentLength, set.blankSegmentLength); + if (i != lineCount-1) builder.append("|"); + } + + // Scaling. + builder.append("&chds="); + + for (uint i = 0; i < lineCount; i++) { + const GooglePointSet & set = pointSetArray[i]; + builder.appendFormat("%f,%f,%f,%f", set.min.x, set.max.x, set.min.y, set.max.y); + if (i != lineCount-1) builder.append(","); + } + + // Data. + builder.append("&chd=t:"); + for (uint i = 0; i < lineCount; i++) { + const GooglePointSet & set = pointSetArray[i]; + + const uint pointCount = set.points.count(); + /*for (uint p = 0; p < pointCount; p++) { + builder.appendFormat("%f", set.points[p].x); + if (p != pointCount-1) builder.append(","); + }*/ + builder.append("-1"); + + builder.append("|"); + + for (uint p = 0; p < pointCount; p++) { + builder.appendFormat("%f", set.points[p].y); + if (p != pointCount-1) builder.append(","); + } + + if (i != lineCount-1) builder.append("|"); + } +} + +} // nv namespace diff --git a/trunk/src/nvtt/tests/hdrtest.cpp b/trunk/src/nvtt/tests/hdrtest.cpp new file mode 100644 index 0000000..09884d8 --- /dev/null +++ b/trunk/src/nvtt/tests/hdrtest.cpp @@ -0,0 +1,359 @@ +// Copyright (c) 2009-2011 Ignacio Castano +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include // free +#include // memcpy +#include // FLT_MAX + +#include "../tools/cmdline.h" + +#include "GoogleCharts.h" + +using namespace nv; +using namespace nvtt; + +static const char * s_hdrImageSet[] = { + "specruin.dds", + "cottage.dds", + "tower.dds", +}; + + +struct MyOutputHandler : public nvtt::OutputHandler +{ + MyOutputHandler() : m_data(NULL), m_ptr(NULL) {} + ~MyOutputHandler() + { + free(m_data); + } + + virtual void beginImage(int size, int width, int height, int depth, int face, int miplevel) + { + m_size = size; + m_width = width; + m_height = height; + free(m_data); + m_data = (unsigned char *)malloc(size); + m_ptr = m_data; + } + + virtual void endImage() + { + } + + virtual bool writeData(const void * data, int size) + { + memcpy(m_ptr, data, size); + m_ptr += size; + return true; + } + + nvtt::Surface decompress(nvtt::Format format, nvtt::Decoder decoder) + { + nvtt::Surface img; + img.setImage2D(format, decoder, m_width, m_height, m_data); + return img; + } + + int m_size; + int m_width; + int m_height; + unsigned char * m_data; + unsigned char * m_ptr; +}; + + + +// Compare two HDR surfaces tone mapped at the given exposure. +float compare(const Surface & hdr0, const Surface & hdr1, float exposure) +{ + Surface ldr0 = hdr0; + ldr0.scaleBias(0, exposure, 0); + ldr0.scaleBias(1, exposure, 0); + ldr0.scaleBias(2, exposure, 0); + ldr0.toneMap(nvtt::ToneMapper_Halo, NULL); + ldr0.toSrgb(); + + Surface ldr1 = hdr1; + ldr1.scaleBias(0, exposure, 0); + ldr1.scaleBias(1, exposure, 0); + ldr1.scaleBias(2, exposure, 0); + ldr1.toneMap(nvtt::ToneMapper_Halo, NULL); + ldr1.toSrgb(); + + return nvtt::rmsError(ldr0, ldr1); +} + +// Compare two HDR images at different exposures. +void compare(const Surface & hdr0, const Surface & hdr1, const Array & exposures, Array & errors) +{ + const uint exposureCount = exposures.count(); + + errors.resize(exposureCount); + + for (uint i = 0; i < exposureCount; i++) { + errors[i] = compare(hdr0, hdr1, exposures[i]); + } +} + + +void updatePointSet(const Array & exposures, Array & errors, GooglePointSet & pointSet) +{ + uint pointCount = exposures.count(); + nvDebugCheck(pointCount == errors.count()); + + pointSet.points.resize(pointCount); + + for (uint i = 0; i < pointCount; i++) { + pointSet.points[i].x = exposures[i]; + pointSet.points[i].y = errors[i]; + } +} + +Surface loadInput(const char * fileName) { + Surface src; + src.load(fileName); + src.clamp(0, 0, FLT_MAX); + src.clamp(1, 0, FLT_MAX); + src.clamp(2, 0, FLT_MAX); + return src; +} + +Surface process(const Surface & src, int method) { + Surface dst; + + float rMin, rMax, gMin, gMax, bMin, bMax; + src.range(0, &rMin, &rMax); + src.range(1, &gMin, &gMax); + src.range(2, &bMin, &bMax); + + if (method == 0) { + // Only clamp. + dst = src; + + dst.scaleBias(0, 1.0f / 4, 0); + dst.scaleBias(1, 1.0f / 4, 0); + dst.scaleBias(2, 1.0f / 4, 0); + + dst.clamp(0, 0, 1); + dst.clamp(1, 0, 1); + dst.clamp(2, 0, 1); + + dst.toGamma(0, 2); + dst.toGamma(1, 2); + dst.toGamma(2, 2); + + dst.quantize(0, 10, /*exactEndPoints*/true, false); + dst.quantize(1, 10, /*exactEndPoints*/true, false); + dst.quantize(2, 10, /*exactEndPoints*/true, false); + + dst.toLinear(0, 2); + dst.toLinear(1, 2); + dst.toLinear(2, 2); + + dst.scaleBias(0, 4, 0); + dst.scaleBias(1, 4, 0); + dst.scaleBias(2, 4, 0); + } + else if (method == 1) { + // Scale and bias. Use full range. + dst = src; + + float gamma = 3; + + dst.scaleBias(0, 1.0f / rMax, 0); + dst.scaleBias(1, 1.0f / gMax, 0); + dst.scaleBias(2, 1.0f / bMax, 0); + + dst.clamp(0, 0, 1); + dst.clamp(1, 0, 1); + dst.clamp(2, 0, 1); + + dst.toGamma(0, gamma); + dst.toGamma(1, gamma); + dst.toGamma(2, gamma); + + dst.quantize(0, 10, /*exactEndPoints*/true, false); + dst.quantize(1, 10, /*exactEndPoints*/true, false); + dst.quantize(2, 10, /*exactEndPoints*/true, false); + + dst.toLinear(0, gamma); + dst.toLinear(1, gamma); + dst.toLinear(2, gamma); + + dst.scaleBias(0, rMax, 0); + dst.scaleBias(1, gMax, 0); + dst.scaleBias(2, bMax, 0); + } + else if (method == 2) { + // Scale and bias. Use full range. + dst = src; + + // @@ Experiment with log/exp transform! + float gamma = 2.2; + + dst.scaleBias(0, 1.0f / rMax, 0); + dst.scaleBias(1, 1.0f / gMax, 0); + dst.scaleBias(2, 1.0f / bMax, 0); + + dst.clamp(0, 0, 1); + dst.clamp(1, 0, 1); + dst.clamp(2, 0, 1); + + //dst.toGamma(0, gamma); + //dst.toGamma(1, gamma); + //dst.toGamma(2, gamma); + dst.toLogScale(0, 2); + dst.toLogScale(1, 2); + dst.toLogScale(2, 2); + + dst.quantize(0, 10, /*exactEndPoints*/true, false); + dst.quantize(1, 10, /*exactEndPoints*/true, false); + dst.quantize(2, 10, /*exactEndPoints*/true, false); + + dst.fromLogScale(0, 2); + dst.fromLogScale(1, 2); + dst.fromLogScale(2, 2); + + //dst.toLinear(0, gamma); + //dst.toLinear(1, gamma); + //dst.toLinear(2, gamma); + + dst.scaleBias(0, rMax, 0); + dst.scaleBias(1, gMax, 0); + dst.scaleBias(2, bMax, 0); + } + else if (method == 3) { + // Scale and bias. Use full range. + dst = src; + + // @@ Experiment with log/exp transform! + float gamma = 0.5; + + dst.scaleBias(0, 1.0f / rMax, 0); + dst.scaleBias(1, 1.0f / gMax, 0); + dst.scaleBias(2, 1.0f / bMax, 0); + + dst.clamp(0, 0, 1); + dst.clamp(1, 0, 1); + dst.clamp(2, 0, 1); + + dst.toGamma(0, gamma); + dst.toGamma(1, gamma); + dst.toGamma(2, gamma); + + dst.toLogScale(0, 2); + dst.toLogScale(1, 2); + dst.toLogScale(2, 2); + + dst.quantize(0, 8, /*exactEndPoints*/true, false); + dst.quantize(1, 8, /*exactEndPoints*/true, false); + dst.quantize(2, 8, /*exactEndPoints*/true, false); + + dst.fromLogScale(0, 2); + dst.fromLogScale(1, 2); + dst.fromLogScale(2, 2); + + dst.toLinear(0, gamma); + dst.toLinear(1, gamma); + dst.toLinear(2, gamma); + + dst.scaleBias(0, rMax, 0); + dst.scaleBias(1, gMax, 0); + dst.scaleBias(2, bMax, 0); + } + + return dst; +} + +void printImageInfo(const Surface & img) { + float rMin, rMax, gMin, gMax, bMin, bMax; + img.range(0, &rMin, &rMax); + img.range(1, &gMin, &gMax); + img.range(2, &bMin, &bMax); + + printf("R: %f %f\n", rMin, rMax); + printf("G: %f %f\n", gMin, gMax); + printf("B: %f %f\n", bMin, bMax); +} + +int main(int argc, char *argv[]) +{ + MyAssertHandler assertHandler; + MyMessageHandler messageHandler; + + GoogleLineChart chart; + chart.pointSetArray.resize(2); + + Array errors; + Array exposures; + for (int i = 0; i < 48; i++) { + //exposures.append(8 * float(i)/63); + exposures.append(lerp(0.22, 22, float(i)/47)); + } + + Surface src = loadInput("hdr/34017_03.dds"); + //Surface src = loadInput("hdr/49002_1F.dds"); + if (src.isNull()) { + printf("Error loading image.\n"); + return EXIT_FAILURE; + } + + printImageInfo(src); + + //compare(src, process(src, 0), exposures, errors); + //updatePointSet(exposures, errors, chart.pointSetArray[0]); + //chart.pointSetArray[0].legend = "Clamp"; + compare(src, process(src, 0), exposures, errors); + updatePointSet(exposures, errors, chart.pointSetArray[0]); + chart.pointSetArray[0].legend = "Default"; + + compare(src, process(src, 3), exposures, errors); + updatePointSet(exposures, errors, chart.pointSetArray[1]); + chart.pointSetArray[1].legend = "Log + Gamma 2.2"; + chart.pointSetArray[1].lineColor = Vector3(0.19, 0.45, 0.95); + + + chart.autoScale(); + + StringBuilder builder; + chart.build(builder); + + printf("%s\n", builder.str()); + + return EXIT_SUCCESS; +} + diff --git a/trunk/src/nvtt/tools/cmdline.h b/trunk/src/nvtt/tools/cmdline.h index b2f7697..ead747d 100644 --- a/trunk/src/nvtt/tools/cmdline.h +++ b/trunk/src/nvtt/tools/cmdline.h @@ -51,6 +51,7 @@ struct MyMessageHandler : public nv::MessageHandler { struct MyAssertHandler : public nv::AssertHandler { MyAssertHandler() { nv::debug::setAssertHandler( this ); + nv::debug::enableSigHandler(); } ~MyAssertHandler() { nv::debug::resetAssertHandler();