|
|
|
@ -16,6 +16,7 @@ http://www.efg2.com/Lab/Library/ImageProcessing/DHALF.TXT
|
|
|
|
|
|
|
|
|
|
#include <nvimage/Image.h>
|
|
|
|
|
#include <nvimage/Quantize.h>
|
|
|
|
|
#include <nvimage/PixelFormat.h>
|
|
|
|
|
|
|
|
|
|
using namespace nv;
|
|
|
|
|
|
|
|
|
@ -47,94 +48,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);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -188,47 +115,102 @@ 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;
|
|
|
|
|
|
|
|
|
|
// 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;
|
|
|
|
|
}
|
|
|
|
|