diff --git a/src/nvimage/Filter.cpp b/src/nvimage/Filter.cpp index ba56506..b1be2b4 100644 --- a/src/nvimage/Filter.cpp +++ b/src/nvimage/Filter.cpp @@ -243,7 +243,7 @@ SincFilter::SincFilter(float w) : Filter(w) {} float SincFilter::evaluate(float x) const { - return 0.0f; + return sincf(PI * x); } @@ -540,12 +540,17 @@ void Kernel2::initBlendedSobel(const Vector4 & scale) PolyphaseKernel::PolyphaseKernel(const Filter & f, uint srcLength, uint dstLength, int samples/*= 32*/) { - nvCheck(srcLength >= dstLength); // @@ Upsampling not implemented! nvDebugCheck(samples > 0); - - const float scale = float(dstLength) / float(srcLength); + + float scale = float(dstLength) / float(srcLength); const float iscale = 1.0f / scale; + if (scale > 1) { + // Upsampling. + samples = 1; + scale = 1; + } + m_length = dstLength; m_width = f.width() * iscale; m_windowSize = (int)ceilf(m_width * 2) + 1; diff --git a/src/nvimage/FloatImage.cpp b/src/nvimage/FloatImage.cpp index 7bb191d..7530bdf 100644 --- a/src/nvimage/FloatImage.cpp +++ b/src/nvimage/FloatImage.cpp @@ -592,73 +592,18 @@ FloatImage * FloatImage::fastDownSample() const return dst_image.release(); } -/* -/// Downsample applying a 1D kernel separately in each dimension. -FloatImage * FloatImage::downSample(const Kernel1 & kernel, WrapMode wm) const -{ - const uint w = max(1, m_width / 2); - const uint h = max(1, m_height / 2); - - return downSample(kernel, w, h, wm); -} - - -/// Downsample applying a 1D kernel separately in each dimension. -FloatImage * FloatImage::downSample(const Kernel1 & kernel, uint w, uint h, WrapMode wm) const -{ - nvCheck(!(kernel.windowSize() & 1)); // Make sure that kernel m_width is even. - - AutoPtr tmp_image( new FloatImage() ); - tmp_image->allocate(m_componentNum, w, m_height); - - AutoPtr dst_image( new FloatImage() ); - dst_image->allocate(m_componentNum, w, h); - - const float xscale = float(m_width) / float(w); - const float yscale = float(m_height) / float(h); - - for(uint c = 0; c < m_componentNum; c++) { - float * tmp_channel = tmp_image->channel(c); - - for(uint y = 0; y < m_height; y++) { - for(uint x = 0; x < w; x++) { - - float sum = this->applyKernelHorizontal(&kernel, uint(x*xscale), y, c, wm); - - const uint tmp_index = tmp_image->index(x, y); - tmp_channel[tmp_index] = sum; - } - } - - float * dst_channel = dst_image->channel(c); - - for(uint y = 0; y < h; y++) { - for(uint x = 0; x < w; x++) { - - float sum = tmp_image->applyKernelVertical(&kernel, uint(x*xscale), uint(y*yscale), c, wm); - - const uint dst_index = dst_image->index(x, y); - dst_channel[dst_index] = sum; - } - } - } - - return dst_image.release(); -} -*/ - /// Downsample applying a 1D kernel separately in each dimension. FloatImage * FloatImage::downSample(const Filter & filter, WrapMode wm) const { const uint w = max(1, m_width / 2); const uint h = max(1, m_height / 2); - return downSample(filter, w, h, wm); + return resize(filter, w, h, wm); } /// Downsample applying a 1D kernel separately in each dimension. -FloatImage * FloatImage::downSample(const Filter & filter, uint w, uint h, WrapMode wm) const +FloatImage * FloatImage::resize(const Filter & filter, uint w, uint h, WrapMode wm) const { // @@ Use monophase filters when frac(m_width / w) == 0 diff --git a/src/nvimage/FloatImage.h b/src/nvimage/FloatImage.h index 933d3e5..c6d02e1 100644 --- a/src/nvimage/FloatImage.h +++ b/src/nvimage/FloatImage.h @@ -73,7 +73,7 @@ public: NVIMAGE_API FloatImage * fastDownSample() const; NVIMAGE_API FloatImage * downSample(const Filter & filter, WrapMode wm) const; - NVIMAGE_API FloatImage * downSample(const Filter & filter, uint w, uint h, WrapMode wm) const; + NVIMAGE_API FloatImage * resize(const Filter & filter, uint w, uint h, WrapMode wm) const; //NVIMAGE_API FloatImage * downSample(const Kernel1 & filter, WrapMode wm) const; //NVIMAGE_API FloatImage * downSample(const Kernel1 & filter, uint w, uint h, WrapMode wm) const; @@ -239,11 +239,15 @@ inline uint FloatImage::indexRepeat(int x, int y) const inline uint FloatImage::indexMirror(int x, int y) const { + if (m_width == 1) x = 0; + x = abs(x); while (x >= m_width) { x = abs(m_width + m_width - x - 2); } + if (m_height == 1) y = 0; + y = abs(y); while (y >= m_height) { y = abs(m_height + m_height - y - 2); diff --git a/src/nvimage/Quantize.cpp b/src/nvimage/Quantize.cpp index b67f132..56812bd 100644 --- a/src/nvimage/Quantize.cpp +++ b/src/nvimage/Quantize.cpp @@ -14,12 +14,12 @@ http://www.efg2.com/Lab/Library/ImageProcessing/DHALF.TXT #include #include +#include #include #include // swap -#include // memset using namespace nv; @@ -51,94 +51,20 @@ void nv::Quantize::BinaryAlpha( Image * image, int alpha_threshold /*= 127*/ ) // Simple quantization. void nv::Quantize::RGB16( Image * image ) { - nvCheck(image != NULL); - - const uint w = image->width(); - const uint h = image->height(); - - for(uint y = 0; y < h; y++) { - for(uint x = 0; x < w; x++) { - - Color32 pixel32 = image->pixel(x, y); - - // Convert to 16 bit and back to 32 using regular bit expansion. - Color32 pixel16 = toColor32( toColor16(pixel32) ); - - // Store color. - image->pixel(x, y) = pixel16; - } - } + Truncate(image, 5, 6, 5, 8); } // Alpha quantization. void nv::Quantize::Alpha4( Image * image ) { - nvCheck(image != NULL); - - const uint w = image->width(); - const uint h = image->height(); - - for(uint y = 0; y < h; y++) { - for(uint x = 0; x < w; x++) { - - Color32 pixel = image->pixel(x, y); - - // Convert to 4 bit using regular bit expansion. - pixel.a = (pixel.a & 0xF0) | ((pixel.a & 0xF0) >> 4); - - // Store color. - image->pixel(x, y) = pixel; - } - } + Truncate(image, 8, 8, 8, 4); } // Error diffusion. Floyd Steinberg. void nv::Quantize::FloydSteinberg_RGB16( Image * image ) { - nvCheck(image != NULL); - - const uint w = image->width(); - const uint h = image->height(); - - // @@ Use fixed point? - Vector3 * row0 = new Vector3[w+2]; - Vector3 * row1 = new Vector3[w+2]; - memset(row0, 0, sizeof(Vector3)*(w+2)); - memset(row1, 0, sizeof(Vector3)*(w+2)); - - for(uint y = 0; y < h; y++) { - for(uint x = 0; x < w; x++) { - - Color32 pixel32 = image->pixel(x, y); - - // Add error. // @@ We shouldn't clamp here! - pixel32.r = clamp(int(pixel32.r) + int(row0[1+x].x()), 0, 255); - pixel32.g = clamp(int(pixel32.g) + int(row0[1+x].y()), 0, 255); - pixel32.b = clamp(int(pixel32.b) + int(row0[1+x].z()), 0, 255); - - // Convert to 16 bit. @@ Use regular clamp? - Color32 pixel16 = toColor32( toColor16(pixel32) ); - - // Store color. - image->pixel(x, y) = pixel16; - - // Compute new error. - Vector3 diff(float(pixel32.r - pixel16.r), float(pixel32.g - pixel16.g), float(pixel32.b - pixel16.b)); - - // Propagate new error. - row0[1+x+1] += 7.0f / 16.0f * diff; - row1[1+x-1] += 3.0f / 16.0f * diff; - row1[1+x+0] += 5.0f / 16.0f * diff; - row1[1+x+1] += 1.0f / 16.0f * diff; - } - - swap(row0, row1); - memset(row1, 0, sizeof(Vector3)*(w+2)); - } - - delete [] row0; - delete [] row1; + FloydSteinberg(image, 5, 6, 5, 8); } @@ -192,34 +118,90 @@ void nv::Quantize::FloydSteinberg_BinaryAlpha( Image * image, int alpha_threshol // Error diffusion. Floyd Steinberg. void nv::Quantize::FloydSteinberg_Alpha4( Image * image ) +{ + FloydSteinberg(image, 8, 8, 8, 4); +} + + +void nv::Quantize::Truncate(Image * image, uint rsize, uint gsize, uint bsize, uint asize) { nvCheck(image != NULL); const uint w = image->width(); const uint h = image->height(); - // @@ Use fixed point? - float * row0 = new float[(w+2)]; - float * row1 = new float[(w+2)]; - memset(row0, 0, sizeof(float)*(w+2)); - memset(row1, 0, sizeof(float)*(w+2)); - for(uint y = 0; y < h; y++) { for(uint x = 0; x < w; x++) { Color32 pixel = image->pixel(x, y); - - // Add error. - int alpha = int(pixel.a) + int(row0[1+x]); - - // Convert to 4 bit using regular bit expansion. - pixel.a = (pixel.a & 0xF0) | ((pixel.a & 0xF0) >> 4); - + + // Convert to our desired size, and reconstruct. + pixel.r = PixelFormat::convert(pixel.r, 8, rsize); + pixel.r = PixelFormat::convert(pixel.r, rsize, 8); + + pixel.g = PixelFormat::convert(pixel.g, 8, gsize); + pixel.g = PixelFormat::convert(pixel.g, gsize, 8); + + pixel.b = PixelFormat::convert(pixel.b, 8, bsize); + pixel.b = PixelFormat::convert(pixel.b, bsize, 8); + + pixel.a = PixelFormat::convert(pixel.a, 8, asize); + pixel.a = PixelFormat::convert(pixel.a, asize, 8); + // Store color. image->pixel(x, y) = pixel; + } + } +} + + +// Error diffusion. Floyd Steinberg. +void nv::Quantize::FloydSteinberg(Image * image, uint rsize, uint gsize, uint bsize, uint asize) +{ + nvCheck(image != NULL); + + const uint w = image->width(); + const uint h = image->height(); + + Vector4 * row0 = new Vector4[w+2]; + Vector4 * row1 = new Vector4[w+2]; + memset(row0, 0, sizeof(Vector4)*(w+2)); + memset(row1, 0, sizeof(Vector4)*(w+2)); + + for (uint y = 0; y < h; y++) { + for (uint x = 0; x < w; x++) { + + Color32 pixel = image->pixel(x, y); + + // Add error. + pixel.r = clamp(int(pixel.r) + int(row0[1+x].x()), 0, 255); + pixel.g = clamp(int(pixel.g) + int(row0[1+x].y()), 0, 255); + pixel.b = clamp(int(pixel.b) + int(row0[1+x].z()), 0, 255); + pixel.a = clamp(int(pixel.a) + int(row0[1+x].w()), 0, 255); + + int r = pixel.r; + int g = pixel.g; + int b = pixel.b; + int a = pixel.a; + + // Convert to our desired size, and reconstruct. + r = PixelFormat::convert(r, 8, rsize); + r = PixelFormat::convert(r, rsize, 8); + + g = PixelFormat::convert(g, 8, gsize); + g = PixelFormat::convert(g, gsize, 8); + + b = PixelFormat::convert(b, 8, bsize); + b = PixelFormat::convert(b, bsize, 8); + + a = PixelFormat::convert(a, 8, asize); + a = PixelFormat::convert(a, asize, 8); + + // Store color. + image->pixel(x, y) = Color32(r, g, b, a); // Compute new error. - float diff = float(alpha - pixel.a); + Vector4 diff(float(int(pixel.r) - r), float(int(pixel.g) - g), float(int(pixel.b) - b), float(int(pixel.a) - a)); // Propagate new error. row0[1+x+1] += 7.0f / 16.0f * diff; @@ -229,10 +211,9 @@ void nv::Quantize::FloydSteinberg_Alpha4( Image * image ) } swap(row0, row1); - memset(row1, 0, sizeof(float)*(w+2)); + memset(row1, 0, sizeof(Vector4)*(w+2)); } delete [] row0; delete [] row1; } - diff --git a/src/nvimage/Quantize.h b/src/nvimage/Quantize.h index 0ffa8d7..5b4a955 100644 --- a/src/nvimage/Quantize.h +++ b/src/nvimage/Quantize.h @@ -3,6 +3,9 @@ #ifndef NV_IMAGE_QUANTIZE_H #define NV_IMAGE_QUANTIZE_H +#include + + namespace nv { class Image; @@ -17,6 +20,9 @@ namespace nv void FloydSteinberg_BinaryAlpha(Image * img, int alpha_threshold = 127); void FloydSteinberg_Alpha4(Image * img); + void Truncate(Image * image, uint rsize, uint gsize, uint bsize, uint asize); + void FloydSteinberg(Image * image, uint rsize, uint gsize, uint bsize, uint asize); + // @@ Add palette quantization algorithms! } } diff --git a/src/nvtt/Compressor.cpp b/src/nvtt/Compressor.cpp index 31b7423..ee0164f 100644 --- a/src/nvtt/Compressor.cpp +++ b/src/nvtt/Compressor.cpp @@ -34,6 +34,7 @@ #include #include #include +#include #include #include "Compressor.h" @@ -728,7 +729,7 @@ void Compressor::Private::scaleMipmap(Mipmap & mipmap, const InputOptions::Priva // Resize image. BoxFilter boxFilter; - mipmap.setImage(mipmap.asFloatImage()->downSample(boxFilter, w, h, (FloatImage::WrapMode)inputOptions.wrapMode)); + mipmap.setImage(mipmap.asFloatImage()->resize(boxFilter, w, h, (FloatImage::WrapMode)inputOptions.wrapMode)); } @@ -822,13 +823,6 @@ void Compressor::Private::quantizeMipmap(Mipmap & mipmap, const CompressionOptio { nvDebugCheck(mipmap.asFixedImage() != NULL); - if (compressionOptions.enableColorDithering) - { - if (compressionOptions.format >= Format_DXT1 && compressionOptions.format <= Format_DXT5) - { - Quantize::FloydSteinberg_RGB16(mipmap.asMutableFixedImage()); - } - } if (compressionOptions.binaryAlpha) { if (compressionOptions.enableAlphaDithering) @@ -840,19 +834,50 @@ void Compressor::Private::quantizeMipmap(Mipmap & mipmap, const CompressionOptio Quantize::BinaryAlpha(mipmap.asMutableFixedImage(), compressionOptions.alphaThreshold); } } - else + + if (compressionOptions.enableColorDithering || compressionOptions.enableAlphaDithering) { + uint rsize = 8; + uint gsize = 8; + uint bsize = 8; + uint asize = 8; + + if (compressionOptions.enableColorDithering) + { + if (compressionOptions.format >= Format_DXT1 && compressionOptions.format <= Format_DXT5) + { + rsize = 5; + gsize = 6; + bsize = 5; + } + else if (compressionOptions.format == Format_RGB) + { + uint rshift, gshift, bshift; + PixelFormat::maskShiftAndSize(compressionOptions.rmask, &rshift, &rsize); + PixelFormat::maskShiftAndSize(compressionOptions.gmask, &gshift, &gsize); + PixelFormat::maskShiftAndSize(compressionOptions.bmask, &bshift, &bsize); + } + } + if (compressionOptions.enableAlphaDithering) { if (compressionOptions.format == Format_DXT3) { - Quantize::Alpha4(mipmap.asMutableFixedImage()); + asize = 4; } - else if (compressionOptions.format == Format_DXT1a) + else if (compressionOptions.format == Format_RGB) { - Quantize::BinaryAlpha(mipmap.asMutableFixedImage(), compressionOptions.alphaThreshold); + uint ashift; + PixelFormat::maskShiftAndSize(compressionOptions.amask, &ashift, &asize); } } + + if (compressionOptions.binaryAlpha) + { + asize = 8; // Already quantized. + } + + Quantize::FloydSteinberg(mipmap.asMutableFixedImage(), rsize, gsize, bsize, asize); } } @@ -912,8 +937,8 @@ bool Compressor::Private::compressMipmap(const Mipmap & mipmap, const InputOptio { nvDebugCheck(cudaSupported); cuda->setImage(image, inputOptions.alphaMode); - //cuda->compressDXT1(compressionOptions, outputOptions); - cuda->compressDXT1_Tex(compressionOptions, outputOptions); + cuda->compressDXT1(compressionOptions, outputOptions); + //cuda->compressDXT1_Tex(compressionOptions, outputOptions); } else { diff --git a/src/nvtt/cuda/CudaMath.h b/src/nvtt/cuda/CudaMath.h index 17ed6ff..6d6f393 100644 --- a/src/nvtt/cuda/CudaMath.h +++ b/src/nvtt/cuda/CudaMath.h @@ -231,16 +231,16 @@ inline __device__ bool singleColor(const float3 * colors) inline __device__ void colorSums(const float3 * colors, float3 * sums) { #if __DEVICE_EMULATION__ - float3 color_sum = make_float3(0.0f, 0.0f, 0.0f); - for (int i = 0; i < 16; i++) - { - color_sum += colors[i]; - } + float3 color_sum = make_float3(0.0f, 0.0f, 0.0f); + for (int i = 0; i < 16; i++) + { + color_sum += colors[i]; + } - for (int i = 0; i < 16; i++) - { - sums[i] = color_sum; - } + for (int i = 0; i < 16; i++) + { + sums[i] = color_sum; + } #else const int idx = threadIdx.x; diff --git a/src/nvtt/tools/resize.cpp b/src/nvtt/tools/resize.cpp index c8b8deb..df2fc10 100644 --- a/src/nvtt/tools/resize.cpp +++ b/src/nvtt/tools/resize.cpp @@ -73,10 +73,12 @@ int main(int argc, char *argv[]) float scale = 0.5f; float gamma = 2.2f; - nv::Filter * filter = NULL; + nv::AutoPtr filter; nv::Path input; nv::Path output; + nv::FloatImage::WrapMode wrapMode = nv::FloatImage::WrapMode_Mirror; + // Parse arguments. for (int i = 1; i < argc; i++) { @@ -108,9 +110,18 @@ int main(int argc, char *argv[]) else if (strcmp("lanczos", argv[i]) == 0) filter = new nv::LanczosFilter(); else if (strcmp("kaiser", argv[i]) == 0) { filter = new nv::KaiserFilter(3); - ((nv::KaiserFilter *)filter)->setParameters(4.0f, 1.0f); + ((nv::KaiserFilter *)filter.ptr())->setParameters(4.0f, 1.0f); } } + else if (strcmp("-f", argv[i]) == 0) + { + if (i+1 == argc) break; + i++; + + if (strcmp("mirror", argv[i]) == 0) wrapMode = nv::FloatImage::WrapMode_Mirror; + else if (strcmp("repeat", argv[i]) == 0) wrapMode = nv::FloatImage::WrapMode_Repeat; + else if (strcmp("clamp", argv[i]) == 0) wrapMode = nv::FloatImage::WrapMode_Clamp; + } else if (argv[i][0] != '-') { input = argv[i]; @@ -140,6 +151,10 @@ int main(int argc, char *argv[]) printf(" * mitchell\n"); printf(" * lanczos\n"); printf(" * kaiser\n"); + printf(" -w mode One of the following: (default = 'mirror')\n"); + printf(" * mirror\n"); + printf(" * repeat\n"); + printf(" * clamp\n"); return 1; } @@ -155,15 +170,14 @@ int main(int argc, char *argv[]) nv::FloatImage fimage(&image); fimage.toLinear(0, 3, gamma); - nv::AutoPtr fresult(fimage.downSample(*filter, uint(image.width() * scale), uint(image.height() * scale), nv::FloatImage::WrapMode_Mirror)); + nv::AutoPtr fresult(fimage.resize(*filter, uint(image.width() * scale), uint(image.height() * scale), wrapMode)); nv::AutoPtr result(fresult->createImageGammaCorrect(gamma)); + result->setFormat(nv::Image::Format_ARGB); nv::StdOutputStream stream(output); nv::ImageIO::saveTGA(stream, result.ptr()); // @@ Add generic save function. Add support for png too. - delete filter; - return 0; }