Compare commits
51 Commits
Author | SHA1 | Date | |
---|---|---|---|
624c5bd316 | |||
b2d6122769 | |||
cd59058fc2 | |||
db14e048e1 | |||
0c36fcf626 | |||
68be24bf00 | |||
b284669993 | |||
2f6e885ced | |||
1957120c26 | |||
d7ddcb9263 | |||
f5f6e88585 | |||
13e2d2e447 | |||
0b13b6d0d9 | |||
ad85b0fcbe | |||
0515d9a0a0 | |||
16adf94635 | |||
e9002a7d86 | |||
3161fca9d9 | |||
bb5b02df0e | |||
1941e27148 | |||
02c3abb394 | |||
86ef67bbfa | |||
79529f994f | |||
c2508d9eeb | |||
b1cd916105 | |||
96655b3e7c | |||
529c0075e1 | |||
c70e5d6121 | |||
7394644719 | |||
d5055300e2 | |||
b2e7d717c2 | |||
756f12c994 | |||
206bfcf0f3 | |||
15cfd1c06b | |||
8b26ecc865 | |||
488f3c8f42 | |||
458b8814a7 | |||
c08acc8a71 | |||
45f7244f20 | |||
f412ec8efb | |||
a1a34f546f | |||
7ef88c6f7e | |||
3368f9039b | |||
870a3fe438 | |||
82bed4ac9a | |||
b8a9395117 | |||
65f769160d | |||
524ebbec8c | |||
ce85eaff3e | |||
6befe3505c | |||
ff6f7f0506 |
@ -1,3 +1,9 @@
|
|||||||
|
NVIDIA Texture Tools version 2.0.4
|
||||||
|
* Fix error in RGB format output; reported by jonsoh. See issue 49.
|
||||||
|
* Added support RGB format dithering by jonsoh. Fixes issue 50 and 51.
|
||||||
|
* Prevent infinite loop in indexMirror when width equal 1. Fixes issue 65.
|
||||||
|
* Implement general scale filter, including upsampling.
|
||||||
|
|
||||||
NVIDIA Texture Tools version 2.0.3
|
NVIDIA Texture Tools version 2.0.3
|
||||||
* More accurate DXT3 compressor. Fixes issue 38.
|
* More accurate DXT3 compressor. Fixes issue 38.
|
||||||
* Remove legacy compressors. Fix issue 34.
|
* Remove legacy compressors. Fix issue 34.
|
||||||
|
@ -355,6 +355,10 @@
|
|||||||
RelativePath="..\..\..\src\nvimage\nvimage.h"
|
RelativePath="..\..\..\src\nvimage\nvimage.h"
|
||||||
>
|
>
|
||||||
</File>
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath="..\..\..\src\nvimage\PixelFormat.h"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
<File
|
<File
|
||||||
RelativePath="..\..\..\src\nvimage\PsdFile.h"
|
RelativePath="..\..\..\src\nvimage\PsdFile.h"
|
||||||
>
|
>
|
||||||
|
@ -53,8 +53,8 @@ END
|
|||||||
//
|
//
|
||||||
|
|
||||||
VS_VERSION_INFO VERSIONINFO
|
VS_VERSION_INFO VERSIONINFO
|
||||||
FILEVERSION 2,0,3,0
|
FILEVERSION 2,0,4,0
|
||||||
PRODUCTVERSION 2,0,3,0
|
PRODUCTVERSION 2,0,4,0
|
||||||
FILEFLAGSMASK 0x17L
|
FILEFLAGSMASK 0x17L
|
||||||
#ifdef _DEBUG
|
#ifdef _DEBUG
|
||||||
FILEFLAGS 0x1L
|
FILEFLAGS 0x1L
|
||||||
@ -71,12 +71,12 @@ BEGIN
|
|||||||
BEGIN
|
BEGIN
|
||||||
VALUE "CompanyName", "NVIDIA Corporation"
|
VALUE "CompanyName", "NVIDIA Corporation"
|
||||||
VALUE "FileDescription", "NVIDIA Texture Tools Dynamic Link Library"
|
VALUE "FileDescription", "NVIDIA Texture Tools Dynamic Link Library"
|
||||||
VALUE "FileVersion", "2, 0, 3, 0"
|
VALUE "FileVersion", "2, 0, 4, 0"
|
||||||
VALUE "InternalName", "nvtt"
|
VALUE "InternalName", "nvtt"
|
||||||
VALUE "LegalCopyright", "Copyright (C) 2007"
|
VALUE "LegalCopyright", "Copyright (C) 2007"
|
||||||
VALUE "OriginalFilename", "nvtt.dll"
|
VALUE "OriginalFilename", "nvtt.dll"
|
||||||
VALUE "ProductName", "NVIDIA Texture Tools Dynamic Link Library"
|
VALUE "ProductName", "NVIDIA Texture Tools Dynamic Link Library"
|
||||||
VALUE "ProductVersion", "2, 0, 3, 0"
|
VALUE "ProductVersion", "2, 0, 4, 0"
|
||||||
END
|
END
|
||||||
END
|
END
|
||||||
BLOCK "VarFileInfo"
|
BLOCK "VarFileInfo"
|
||||||
|
@ -532,7 +532,7 @@ DDSHeader::DDSHeader()
|
|||||||
|
|
||||||
// Store version information on the reserved header attributes.
|
// Store version information on the reserved header attributes.
|
||||||
this->reserved[9] = MAKEFOURCC('N', 'V', 'T', 'T');
|
this->reserved[9] = MAKEFOURCC('N', 'V', 'T', 'T');
|
||||||
this->reserved[10] = (2 << 16) | (0 << 8) | (3); // major.minor.revision
|
this->reserved[10] = (2 << 16) | (0 << 8) | (4); // major.minor.revision
|
||||||
|
|
||||||
this->pf.size = 32;
|
this->pf.size = 32;
|
||||||
this->pf.flags = 0;
|
this->pf.flags = 0;
|
||||||
|
@ -26,10 +26,10 @@
|
|||||||
* http://www.xmission.com/~legalize/zoom.html
|
* http://www.xmission.com/~legalize/zoom.html
|
||||||
*
|
*
|
||||||
* Reconstruction Filters in Computer Graphics
|
* Reconstruction Filters in Computer Graphics
|
||||||
* http://www.mentallandscape.com/Papers_siggraph88.pdf
|
* http://www.mentallandscape.com/Papers_siggraph88.pdf
|
||||||
*
|
*
|
||||||
* More references:
|
* More references:
|
||||||
* http://www.worldserver.com/turk/computergraphics/ResamplingFilters.pdf
|
* http://www.worldserver.com/turk/computergraphics/ResamplingFilters.pdf
|
||||||
* http://www.dspguide.com/ch16.htm
|
* http://www.dspguide.com/ch16.htm
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -244,7 +244,7 @@ SincFilter::SincFilter(float w) : Filter(w) {}
|
|||||||
|
|
||||||
float SincFilter::evaluate(float x) const
|
float SincFilter::evaluate(float x) const
|
||||||
{
|
{
|
||||||
return 0.0f;
|
return sincf(PI * x);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -541,12 +541,17 @@ void Kernel2::initBlendedSobel(const Vector4 & scale)
|
|||||||
|
|
||||||
PolyphaseKernel::PolyphaseKernel(const Filter & f, uint srcLength, uint dstLength, int samples/*= 32*/)
|
PolyphaseKernel::PolyphaseKernel(const Filter & f, uint srcLength, uint dstLength, int samples/*= 32*/)
|
||||||
{
|
{
|
||||||
nvCheck(srcLength >= dstLength); // @@ Upsampling not implemented!
|
|
||||||
nvDebugCheck(samples > 0);
|
nvDebugCheck(samples > 0);
|
||||||
|
|
||||||
const float scale = float(dstLength) / float(srcLength);
|
float scale = float(dstLength) / float(srcLength);
|
||||||
const float iscale = 1.0f / scale;
|
const float iscale = 1.0f / scale;
|
||||||
|
|
||||||
|
if (scale > 1) {
|
||||||
|
// Upsampling.
|
||||||
|
samples = 1;
|
||||||
|
scale = 1;
|
||||||
|
}
|
||||||
|
|
||||||
m_length = dstLength;
|
m_length = dstLength;
|
||||||
m_width = f.width() * iscale;
|
m_width = f.width() * iscale;
|
||||||
m_windowSize = (int)ceilf(m_width * 2) + 1;
|
m_windowSize = (int)ceilf(m_width * 2) + 1;
|
||||||
@ -577,6 +582,7 @@ PolyphaseKernel::PolyphaseKernel(const Filter & f, uint srcLength, uint dstLengt
|
|||||||
m_data[i * m_windowSize + j] /= total;
|
m_data[i * m_windowSize + j] /= total;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
PolyphaseKernel::~PolyphaseKernel()
|
PolyphaseKernel::~PolyphaseKernel()
|
||||||
|
@ -540,73 +540,18 @@ FloatImage * FloatImage::fastDownSample() const
|
|||||||
return dst_image.release();
|
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<FloatImage> tmp_image( new FloatImage() );
|
|
||||||
tmp_image->allocate(m_componentNum, w, m_height);
|
|
||||||
|
|
||||||
AutoPtr<FloatImage> 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.
|
/// Downsample applying a 1D kernel separately in each dimension.
|
||||||
FloatImage * FloatImage::downSample(const Filter & filter, WrapMode wm) const
|
FloatImage * FloatImage::downSample(const Filter & filter, WrapMode wm) const
|
||||||
{
|
{
|
||||||
const uint w = max(1, m_width / 2);
|
const uint w = max(1, m_width / 2);
|
||||||
const uint h = max(1, m_height / 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.
|
/// 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
|
// @@ Use monophase filters when frac(m_width / w) == 0
|
||||||
|
|
||||||
|
@ -63,7 +63,7 @@ public:
|
|||||||
|
|
||||||
NVIMAGE_API FloatImage * fastDownSample() const;
|
NVIMAGE_API FloatImage * fastDownSample() const;
|
||||||
NVIMAGE_API FloatImage * downSample(const Filter & filter, WrapMode wm) 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, WrapMode wm) const;
|
||||||
//NVIMAGE_API FloatImage * downSample(const Kernel1 & filter, uint w, uint h, WrapMode wm) const;
|
//NVIMAGE_API FloatImage * downSample(const Kernel1 & filter, uint w, uint h, WrapMode wm) const;
|
||||||
@ -226,11 +226,15 @@ inline uint FloatImage::indexRepeat(int x, int y) const
|
|||||||
|
|
||||||
inline uint FloatImage::indexMirror(int x, int y) const
|
inline uint FloatImage::indexMirror(int x, int y) const
|
||||||
{
|
{
|
||||||
|
if (m_width == 1) x = 0;
|
||||||
|
|
||||||
x = abs(x);
|
x = abs(x);
|
||||||
while (x >= m_width) {
|
while (x >= m_width) {
|
||||||
x = abs(m_width + m_width - x - 2);
|
x = abs(m_width + m_width - x - 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (m_height == 1) y = 0;
|
||||||
|
|
||||||
y = abs(y);
|
y = abs(y);
|
||||||
while (y >= m_height) {
|
while (y >= m_height) {
|
||||||
y = abs(m_height + m_height - y - 2);
|
y = abs(m_height + m_height - y - 2);
|
||||||
|
@ -16,6 +16,7 @@ http://www.efg2.com/Lab/Library/ImageProcessing/DHALF.TXT
|
|||||||
|
|
||||||
#include <nvimage/Image.h>
|
#include <nvimage/Image.h>
|
||||||
#include <nvimage/Quantize.h>
|
#include <nvimage/Quantize.h>
|
||||||
|
#include <nvimage/PixelFormat.h>
|
||||||
|
|
||||||
using namespace nv;
|
using namespace nv;
|
||||||
|
|
||||||
@ -47,94 +48,20 @@ void nv::Quantize::BinaryAlpha( Image * image, int alpha_threshold /*= 127*/ )
|
|||||||
// Simple quantization.
|
// Simple quantization.
|
||||||
void nv::Quantize::RGB16( Image * image )
|
void nv::Quantize::RGB16( Image * image )
|
||||||
{
|
{
|
||||||
nvCheck(image != NULL);
|
Truncate(image, 5, 6, 5, 8);
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Alpha quantization.
|
// Alpha quantization.
|
||||||
void nv::Quantize::Alpha4( Image * image )
|
void nv::Quantize::Alpha4( Image * image )
|
||||||
{
|
{
|
||||||
nvCheck(image != NULL);
|
Truncate(image, 8, 8, 8, 4);
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Error diffusion. Floyd Steinberg.
|
// Error diffusion. Floyd Steinberg.
|
||||||
void nv::Quantize::FloydSteinberg_RGB16( Image * image )
|
void nv::Quantize::FloydSteinberg_RGB16( Image * image )
|
||||||
{
|
{
|
||||||
nvCheck(image != NULL);
|
FloydSteinberg(image, 5, 6, 5, 8);
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -188,47 +115,102 @@ void nv::Quantize::FloydSteinberg_BinaryAlpha( Image * image, int alpha_threshol
|
|||||||
|
|
||||||
// Error diffusion. Floyd Steinberg.
|
// Error diffusion. Floyd Steinberg.
|
||||||
void nv::Quantize::FloydSteinberg_Alpha4( Image * image )
|
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);
|
nvCheck(image != NULL);
|
||||||
|
|
||||||
const uint w = image->width();
|
const uint w = image->width();
|
||||||
const uint h = image->height();
|
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 y = 0; y < h; y++) {
|
||||||
for(uint x = 0; x < w; x++) {
|
for(uint x = 0; x < w; x++) {
|
||||||
|
|
||||||
Color32 pixel = image->pixel(x, y);
|
Color32 pixel = image->pixel(x, y);
|
||||||
|
|
||||||
// Add error.
|
// Convert to our desired size, and reconstruct.
|
||||||
int alpha = int(pixel.a) + int(row0[1+x]);
|
pixel.r = PixelFormat::convert(pixel.r, 8, rsize);
|
||||||
|
pixel.r = PixelFormat::convert(pixel.r, rsize, 8);
|
||||||
// Convert to 4 bit using regular bit expansion.
|
|
||||||
pixel.a = (pixel.a & 0xF0) | ((pixel.a & 0xF0) >> 4);
|
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.
|
// Store color.
|
||||||
image->pixel(x, y) = pixel;
|
image->pixel(x, y) = pixel;
|
||||||
|
|
||||||
// Compute new error.
|
|
||||||
float diff = float(alpha - pixel.a);
|
|
||||||
|
|
||||||
// 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(float)*(w+2));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
delete [] row0;
|
|
||||||
delete [] row1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
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;
|
||||||
|
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(Vector4)*(w+2));
|
||||||
|
}
|
||||||
|
|
||||||
|
delete [] row0;
|
||||||
|
delete [] row1;
|
||||||
|
}
|
||||||
|
@ -17,6 +17,9 @@ namespace nv
|
|||||||
void FloydSteinberg_BinaryAlpha(Image * img, int alpha_threshold = 127);
|
void FloydSteinberg_BinaryAlpha(Image * img, int alpha_threshold = 127);
|
||||||
void FloydSteinberg_Alpha4(Image * img);
|
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!
|
// @@ Add palette quantization algorithms!
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -123,7 +123,7 @@ void nv::compressRGB(const Image * image, const OutputOptions::Private & outputO
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Zero padding.
|
// Zero padding.
|
||||||
for (uint x = w; x < pitch; x++)
|
for (uint x = w * byteCount; x < pitch; x++)
|
||||||
{
|
{
|
||||||
*(dst + x) = 0;
|
*(dst + x) = 0;
|
||||||
}
|
}
|
||||||
|
@ -34,6 +34,7 @@
|
|||||||
#include <nvimage/Filter.h>
|
#include <nvimage/Filter.h>
|
||||||
#include <nvimage/Quantize.h>
|
#include <nvimage/Quantize.h>
|
||||||
#include <nvimage/NormalMap.h>
|
#include <nvimage/NormalMap.h>
|
||||||
|
#include <nvimage/PixelFormat.h>
|
||||||
|
|
||||||
#include "Compressor.h"
|
#include "Compressor.h"
|
||||||
#include "InputOptions.h"
|
#include "InputOptions.h"
|
||||||
@ -570,7 +571,7 @@ void Compressor::Private::scaleMipmap(Mipmap & mipmap, const InputOptions::Priva
|
|||||||
|
|
||||||
// Resize image.
|
// Resize image.
|
||||||
BoxFilter boxFilter;
|
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));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -617,13 +618,6 @@ void Compressor::Private::quantizeMipmap(Mipmap & mipmap, const CompressionOptio
|
|||||||
{
|
{
|
||||||
nvDebugCheck(mipmap.asFixedImage() != NULL);
|
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.binaryAlpha)
|
||||||
{
|
{
|
||||||
if (compressionOptions.enableAlphaDithering)
|
if (compressionOptions.enableAlphaDithering)
|
||||||
@ -635,19 +629,50 @@ void Compressor::Private::quantizeMipmap(Mipmap & mipmap, const CompressionOptio
|
|||||||
Quantize::BinaryAlpha(mipmap.asMutableFixedImage(), compressionOptions.alphaThreshold);
|
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.enableAlphaDithering)
|
||||||
{
|
{
|
||||||
if (compressionOptions.format == Format_DXT3)
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -373,7 +373,6 @@ int main(int argc, char *argv[])
|
|||||||
inputOptions.setMipmapGeneration(false);
|
inputOptions.setMipmapGeneration(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
nvtt::CompressionOptions compressionOptions;
|
nvtt::CompressionOptions compressionOptions;
|
||||||
compressionOptions.setFormat(format);
|
compressionOptions.setFormat(format);
|
||||||
if (fast)
|
if (fast)
|
||||||
@ -397,6 +396,21 @@ int main(int argc, char *argv[])
|
|||||||
compressionOptions.setExternalCompressor(externalCompressor);
|
compressionOptions.setExternalCompressor(externalCompressor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (format == nvtt::Format_RGB)
|
||||||
|
{
|
||||||
|
compressionOptions.setQuantization(true, false, false);
|
||||||
|
//compressionOptions.setPixelFormat(16, 0xF000, 0x0F00, 0x00F0, 0x000F);
|
||||||
|
compressionOptions.setPixelFormat(16,
|
||||||
|
0x0F00,
|
||||||
|
0x00F0,
|
||||||
|
0x000F,
|
||||||
|
0xF000);
|
||||||
|
// 0x003F0000,
|
||||||
|
// 0x00003F00,
|
||||||
|
// 0x0000003F,
|
||||||
|
// 0x3F000000);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
MyErrorHandler errorHandler;
|
MyErrorHandler errorHandler;
|
||||||
MyOutputHandler outputHandler(output);
|
MyOutputHandler outputHandler(output);
|
||||||
|
@ -73,10 +73,12 @@ int main(int argc, char *argv[])
|
|||||||
|
|
||||||
float scale = 0.5f;
|
float scale = 0.5f;
|
||||||
float gamma = 2.2f;
|
float gamma = 2.2f;
|
||||||
nv::Filter * filter = NULL;
|
nv::AutoPtr<nv::Filter> filter;
|
||||||
nv::Path input;
|
nv::Path input;
|
||||||
nv::Path output;
|
nv::Path output;
|
||||||
|
|
||||||
|
nv::FloatImage::WrapMode wrapMode = nv::FloatImage::WrapMode_Mirror;
|
||||||
|
|
||||||
// Parse arguments.
|
// Parse arguments.
|
||||||
for (int i = 1; i < argc; i++)
|
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("lanczos", argv[i]) == 0) filter = new nv::LanczosFilter();
|
||||||
else if (strcmp("kaiser", argv[i]) == 0) {
|
else if (strcmp("kaiser", argv[i]) == 0) {
|
||||||
filter = new nv::KaiserFilter(3);
|
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] != '-')
|
else if (argv[i][0] != '-')
|
||||||
{
|
{
|
||||||
input = argv[i];
|
input = argv[i];
|
||||||
@ -140,6 +151,10 @@ int main(int argc, char *argv[])
|
|||||||
printf(" * mitchell\n");
|
printf(" * mitchell\n");
|
||||||
printf(" * lanczos\n");
|
printf(" * lanczos\n");
|
||||||
printf(" * kaiser\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;
|
return 1;
|
||||||
}
|
}
|
||||||
@ -155,15 +170,14 @@ int main(int argc, char *argv[])
|
|||||||
nv::FloatImage fimage(&image);
|
nv::FloatImage fimage(&image);
|
||||||
fimage.toLinear(0, 3, gamma);
|
fimage.toLinear(0, 3, gamma);
|
||||||
|
|
||||||
nv::AutoPtr<nv::FloatImage> fresult(fimage.downSample(*filter, uint(image.width() * scale), uint(image.height() * scale), nv::FloatImage::WrapMode_Mirror));
|
nv::AutoPtr<nv::FloatImage> fresult(fimage.resize(*filter, uint(image.width() * scale), uint(image.height() * scale), wrapMode));
|
||||||
|
|
||||||
nv::AutoPtr<nv::Image> result(fresult->createImageGammaCorrect(gamma));
|
nv::AutoPtr<nv::Image> result(fresult->createImageGammaCorrect(gamma));
|
||||||
|
result->setFormat(nv::Image::Format_ARGB);
|
||||||
|
|
||||||
nv::StdOutputStream stream(output);
|
nv::StdOutputStream stream(output);
|
||||||
nv::ImageIO::saveTGA(stream, result.ptr()); // @@ Add generic save function. Add support for png too.
|
nv::ImageIO::saveTGA(stream, result.ptr()); // @@ Add generic save function. Add support for png too.
|
||||||
|
|
||||||
delete filter;
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user