Merge changes from The Witness.
This commit is contained in:
@ -632,44 +632,45 @@ void BlockCTX1::setIndices(int * idx)
|
||||
|
||||
|
||||
/// Decode BC6 block.
|
||||
void BlockBC6::decodeBlock(Vector3 colors[16]) const
|
||||
void BlockBC6::decodeBlock(Vector4 colors[16]) const
|
||||
{
|
||||
ZOH::Tile tile(4, 4);
|
||||
ZOH::decompress((const char *)data, tile);
|
||||
ZOH::Tile tile(4, 4);
|
||||
ZOH::decompress((const char *)data, tile);
|
||||
|
||||
// Convert ZOH's tile struct to Vector3, and convert half to float.
|
||||
for (uint y = 0; y < 4; ++y)
|
||||
{
|
||||
for (uint x = 0; x < 4; ++x)
|
||||
{
|
||||
uint16 rHalf = ZOH::Tile::float2half(tile.data[y][x].x);
|
||||
uint16 gHalf = ZOH::Tile::float2half(tile.data[y][x].y);
|
||||
uint16 bHalf = ZOH::Tile::float2half(tile.data[y][x].z);
|
||||
colors[y * 4 + x].x = to_float(rHalf);
|
||||
colors[y * 4 + x].y = to_float(gHalf);
|
||||
colors[y * 4 + x].z = to_float(bHalf);
|
||||
}
|
||||
}
|
||||
// Convert ZOH's tile struct to Vector3, and convert half to float.
|
||||
for (uint y = 0; y < 4; ++y)
|
||||
{
|
||||
for (uint x = 0; x < 4; ++x)
|
||||
{
|
||||
uint16 rHalf = ZOH::Tile::float2half(tile.data[y][x].x);
|
||||
uint16 gHalf = ZOH::Tile::float2half(tile.data[y][x].y);
|
||||
uint16 bHalf = ZOH::Tile::float2half(tile.data[y][x].z);
|
||||
colors[y * 4 + x].x = to_float(rHalf);
|
||||
colors[y * 4 + x].y = to_float(gHalf);
|
||||
colors[y * 4 + x].z = to_float(bHalf);
|
||||
colors[y * 4 + x].w = 1.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Decode BC7 block.
|
||||
void BlockBC7::decodeBlock(ColorBlock * block) const
|
||||
{
|
||||
AVPCL::Tile tile(4, 4);
|
||||
AVPCL::decompress((const char *)data, tile);
|
||||
AVPCL::Tile tile(4, 4);
|
||||
AVPCL::decompress((const char *)data, tile);
|
||||
|
||||
// Convert AVPCL's tile struct back to NVTT's.
|
||||
for (uint y = 0; y < 4; ++y)
|
||||
{
|
||||
for (uint x = 0; x < 4; ++x)
|
||||
{
|
||||
Vector4 rgba = tile.data[y][x];
|
||||
// Note: decoded rgba values are in [0, 255] range and should be an integer,
|
||||
// because BC7 never uses more than 8 bits per channel. So no need to round.
|
||||
block->color(x, y).setRGBA(uint8(rgba.x), uint8(rgba.y), uint8(rgba.z), uint8(rgba.w));
|
||||
}
|
||||
}
|
||||
// Convert AVPCL's tile struct back to NVTT's.
|
||||
for (uint y = 0; y < 4; ++y)
|
||||
{
|
||||
for (uint x = 0; x < 4; ++x)
|
||||
{
|
||||
Vector4 rgba = tile.data[y][x];
|
||||
// Note: decoded rgba values are in [0, 255] range and should be an integer,
|
||||
// because BC7 never uses more than 8 bits per channel. So no need to round.
|
||||
block->color(x, y).setRGBA(uint8(rgba.x), uint8(rgba.y), uint8(rgba.z), uint8(rgba.w));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -36,6 +36,7 @@ namespace nv
|
||||
struct AlphaBlock4x4;
|
||||
class Stream;
|
||||
class Vector3;
|
||||
class Vector4;
|
||||
|
||||
|
||||
/// DXT1 block.
|
||||
@ -220,7 +221,7 @@ namespace nv
|
||||
struct BlockBC6
|
||||
{
|
||||
uint8 data[16]; // Not even going to try to write a union for this thing.
|
||||
void decodeBlock(Vector3 colors[16]) const;
|
||||
void decodeBlock(Vector4 colors[16]) const;
|
||||
};
|
||||
|
||||
/// BC7 block.
|
||||
|
@ -14,7 +14,8 @@ SET(IMAGE_SRCS
|
||||
NormalMap.h NormalMap.cpp
|
||||
PixelFormat.h
|
||||
PsdFile.h
|
||||
TgaFile.h)
|
||||
TgaFile.h
|
||||
KtxFile.h KtxFile.cpp)
|
||||
|
||||
INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR})
|
||||
|
||||
|
@ -454,7 +454,8 @@ namespace
|
||||
|
||||
{ D3DFMT_L8, DXGI_FORMAT_R8_UNORM , { 8, 0xFF, 0, 0, 0 } },
|
||||
{ D3DFMT_L16, DXGI_FORMAT_R16_UNORM, { 16, 0xFFFF, 0, 0, 0 } },
|
||||
{ D3DFMT_A8L8, DXGI_FORMAT_R8G8_UNORM, { 16, 0xFF, 0, 0, 0xFF00 } },
|
||||
{ D3DFMT_A8L8, 0, { 16, 0xFF, 0, 0, 0xFF00 } },
|
||||
{ 0, DXGI_FORMAT_R8G8_UNORM, { 16, 0xFF, 0xFF00, 0, 0 } },
|
||||
};
|
||||
|
||||
static const uint s_formatCount = NV_ARRAY_SIZE(s_formats);
|
||||
@ -635,7 +636,7 @@ void DDSHeader::setFourCC(uint8 c0, uint8 c1, uint8 c2, uint8 c3)
|
||||
{
|
||||
// set fourcc pixel format.
|
||||
this->pf.flags = DDPF_FOURCC;
|
||||
this->pf.fourcc = MAKEFOURCC(c0, c1, c2, c3);
|
||||
this->pf.fourcc = NV_MAKEFOURCC(c0, c1, c2, c3);
|
||||
|
||||
this->pf.bitcount = 0;
|
||||
this->pf.rmask = 0;
|
||||
@ -659,7 +660,7 @@ void DDSHeader::setFormatCode(uint32 code)
|
||||
|
||||
void DDSHeader::setSwizzleCode(uint8 c0, uint8 c1, uint8 c2, uint8 c3)
|
||||
{
|
||||
this->pf.bitcount = MAKEFOURCC(c0, c1, c2, c3);
|
||||
this->pf.bitcount = NV_MAKEFOURCC(c0, c1, c2, c3);
|
||||
}
|
||||
|
||||
|
||||
@ -1445,7 +1446,7 @@ void DirectDrawSurface::readBlock(ColorBlock * rgba)
|
||||
{
|
||||
BlockBC6 block;
|
||||
*stream << block;
|
||||
Vector3 colors[16];
|
||||
Vector4 colors[16];
|
||||
block.decodeBlock(colors);
|
||||
|
||||
// Clamp to [0, 1] and round to 8-bit
|
||||
@ -1453,7 +1454,7 @@ void DirectDrawSurface::readBlock(ColorBlock * rgba)
|
||||
{
|
||||
for (int x = 0; x < 4; ++x)
|
||||
{
|
||||
Vector3 px = colors[y*4 + x];
|
||||
Vector4 px = colors[y*4 + x];
|
||||
rgba->color(x, y).setRGBA(
|
||||
ftoi_round(clamp(px.x, 0.0f, 1.0f) * 255.0f),
|
||||
ftoi_round(clamp(px.y, 0.0f, 1.0f) * 255.0f),
|
||||
@ -1535,7 +1536,7 @@ uint DirectDrawSurface::surfaceSize(uint mipmap) const
|
||||
else {
|
||||
w = (w + 3) / 4;
|
||||
h = (h + 3) / 4;
|
||||
d = d; // @@ How are 3D textures aligned?
|
||||
//d = d; // @@ How are 3D textures aligned?
|
||||
return blockSize * w * h * d;
|
||||
}
|
||||
}
|
||||
|
@ -27,11 +27,9 @@
|
||||
|
||||
#include "nvimage.h"
|
||||
|
||||
#if !defined(MAKEFOURCC)
|
||||
#define MAKEFOURCC(ch0, ch1, ch2, ch3) \
|
||||
#define NV_MAKEFOURCC(ch0, ch1, ch2, ch3) \
|
||||
(uint(uint8(ch0)) | (uint(uint8(ch1)) << 8) | \
|
||||
(uint(uint8(ch2)) << 16) | (uint(uint8(ch3)) << 24 ))
|
||||
#endif
|
||||
|
||||
namespace nv
|
||||
{
|
||||
@ -101,19 +99,26 @@ namespace nv
|
||||
|
||||
enum FOURCC
|
||||
{
|
||||
FOURCC_NVTT = MAKEFOURCC('N', 'V', 'T', 'T'),
|
||||
FOURCC_DDS = MAKEFOURCC('D', 'D', 'S', ' '),
|
||||
FOURCC_DXT1 = MAKEFOURCC('D', 'X', 'T', '1'),
|
||||
FOURCC_DXT2 = MAKEFOURCC('D', 'X', 'T', '2'),
|
||||
FOURCC_DXT3 = MAKEFOURCC('D', 'X', 'T', '3'),
|
||||
FOURCC_DXT4 = MAKEFOURCC('D', 'X', 'T', '4'),
|
||||
FOURCC_DXT5 = MAKEFOURCC('D', 'X', 'T', '5'),
|
||||
FOURCC_RXGB = MAKEFOURCC('R', 'X', 'G', 'B'),
|
||||
FOURCC_ATI1 = MAKEFOURCC('A', 'T', 'I', '1'),
|
||||
FOURCC_ATI2 = MAKEFOURCC('A', 'T', 'I', '2'),
|
||||
FOURCC_A2XY = MAKEFOURCC('A', '2', 'X', 'Y'),
|
||||
FOURCC_DX10 = MAKEFOURCC('D', 'X', '1', '0'),
|
||||
FOURCC_UVER = MAKEFOURCC('U', 'V', 'E', 'R'),
|
||||
FOURCC_NVTT = NV_MAKEFOURCC('N', 'V', 'T', 'T'),
|
||||
FOURCC_DDS = NV_MAKEFOURCC('D', 'D', 'S', ' '),
|
||||
FOURCC_DXT1 = NV_MAKEFOURCC('D', 'X', 'T', '1'),
|
||||
FOURCC_DXT2 = NV_MAKEFOURCC('D', 'X', 'T', '2'),
|
||||
FOURCC_DXT3 = NV_MAKEFOURCC('D', 'X', 'T', '3'),
|
||||
FOURCC_DXT4 = NV_MAKEFOURCC('D', 'X', 'T', '4'),
|
||||
FOURCC_DXT5 = NV_MAKEFOURCC('D', 'X', 'T', '5'),
|
||||
FOURCC_RXGB = NV_MAKEFOURCC('R', 'X', 'G', 'B'),
|
||||
FOURCC_ATI1 = NV_MAKEFOURCC('A', 'T', 'I', '1'),
|
||||
FOURCC_ATI2 = NV_MAKEFOURCC('A', 'T', 'I', '2'),
|
||||
FOURCC_A2XY = NV_MAKEFOURCC('A', '2', 'X', 'Y'),
|
||||
FOURCC_DX10 = NV_MAKEFOURCC('D', 'X', '1', '0'),
|
||||
FOURCC_UVER = NV_MAKEFOURCC('U', 'V', 'E', 'R'),
|
||||
FOURCC_BC6H = NV_MAKEFOURCC('B', 'C', '6', 'H'),
|
||||
FOURCC_BC7L = NV_MAKEFOURCC('B', 'C', '7', 'L'),
|
||||
|
||||
FOURCC_PVR0 = NV_MAKEFOURCC('P', 'V', 'R', '0'),
|
||||
FOURCC_PVR1 = NV_MAKEFOURCC('P', 'V', 'R', '1'),
|
||||
FOURCC_PVR2 = NV_MAKEFOURCC('P', 'V', 'R', '2'),
|
||||
FOURCC_PVR3 = NV_MAKEFOURCC('P', 'V', 'R', '3'),
|
||||
};
|
||||
|
||||
|
||||
|
@ -1,460 +1,513 @@
|
||||
|
||||
#include "ErrorMetric.h"
|
||||
#include "FloatImage.h"
|
||||
#include "Filter.h"
|
||||
|
||||
#include "nvmath/Matrix.h"
|
||||
#include "nvmath/Vector.inl"
|
||||
|
||||
#include <float.h> // FLT_MAX
|
||||
|
||||
using namespace nv;
|
||||
|
||||
float nv::rmsColorError(const FloatImage * ref, const FloatImage * img, bool alphaWeight)
|
||||
{
|
||||
if (!sameLayout(img, ref)) {
|
||||
return FLT_MAX;
|
||||
}
|
||||
nvDebugCheck(img->componentCount() == 4);
|
||||
nvDebugCheck(ref->componentCount() == 4);
|
||||
|
||||
double mse = 0;
|
||||
|
||||
const uint count = img->pixelCount();
|
||||
for (uint i = 0; i < count; i++)
|
||||
{
|
||||
float r0 = ref->pixel(i + count * 0);
|
||||
float g0 = ref->pixel(i + count * 1);
|
||||
float b0 = ref->pixel(i + count * 2);
|
||||
float a0 = ref->pixel(i + count * 3);
|
||||
float r1 = img->pixel(i + count * 0);
|
||||
float g1 = img->pixel(i + count * 1);
|
||||
float b1 = img->pixel(i + count * 2);
|
||||
//float a1 = img->pixel(i + count * 3);
|
||||
|
||||
float r = r0 - r1;
|
||||
float g = g0 - g1;
|
||||
float b = b0 - b1;
|
||||
|
||||
float a = 1;
|
||||
if (alphaWeight) a = a0 * a0; // @@ a0*a1 or a0*a0 ?
|
||||
|
||||
mse += (r * r) * a;
|
||||
mse += (g * g) * a;
|
||||
mse += (b * b) * a;
|
||||
}
|
||||
|
||||
return float(sqrt(mse / count));
|
||||
}
|
||||
|
||||
float nv::rmsAlphaError(const FloatImage * ref, const FloatImage * img)
|
||||
{
|
||||
if (!sameLayout(img, ref)) {
|
||||
return FLT_MAX;
|
||||
}
|
||||
nvDebugCheck(img->componentCount() == 4 && ref->componentCount() == 4);
|
||||
|
||||
double mse = 0;
|
||||
|
||||
const uint count = img->pixelCount();
|
||||
for (uint i = 0; i < count; i++)
|
||||
{
|
||||
float a0 = img->pixel(i + count * 3);
|
||||
float a1 = ref->pixel(i + count * 3);
|
||||
|
||||
float a = a0 - a1;
|
||||
|
||||
mse += a * a;
|
||||
}
|
||||
|
||||
return float(sqrt(mse / count));
|
||||
}
|
||||
|
||||
|
||||
float nv::averageColorError(const FloatImage * ref, const FloatImage * img, bool alphaWeight)
|
||||
{
|
||||
if (!sameLayout(img, ref)) {
|
||||
return FLT_MAX;
|
||||
}
|
||||
nvDebugCheck(img->componentCount() == 4);
|
||||
nvDebugCheck(ref->componentCount() == 4);
|
||||
|
||||
double mae = 0;
|
||||
|
||||
const uint count = img->pixelCount();
|
||||
for (uint i = 0; i < count; i++)
|
||||
{
|
||||
float r0 = img->pixel(i + count * 0);
|
||||
float g0 = img->pixel(i + count * 1);
|
||||
float b0 = img->pixel(i + count * 2);
|
||||
//float a0 = img->pixel(i + count * 3);
|
||||
float r1 = ref->pixel(i + count * 0);
|
||||
float g1 = ref->pixel(i + count * 1);
|
||||
float b1 = ref->pixel(i + count * 2);
|
||||
float a1 = ref->pixel(i + count * 3);
|
||||
|
||||
float r = fabs(r0 - r1);
|
||||
float g = fabs(g0 - g1);
|
||||
float b = fabs(b0 - b1);
|
||||
|
||||
float a = 1;
|
||||
if (alphaWeight) a = a1;
|
||||
|
||||
mae += r * a;
|
||||
mae += g * a;
|
||||
mae += b * a;
|
||||
}
|
||||
|
||||
return float(mae / count);
|
||||
}
|
||||
|
||||
float nv::averageAlphaError(const FloatImage * ref, const FloatImage * img)
|
||||
{
|
||||
if (img == NULL || ref == NULL || img->width() != ref->width() || img->height() != ref->height()) {
|
||||
return FLT_MAX;
|
||||
}
|
||||
nvDebugCheck(img->componentCount() == 4 && ref->componentCount() == 4);
|
||||
|
||||
double mae = 0;
|
||||
|
||||
const uint count = img->width() * img->height();
|
||||
for (uint i = 0; i < count; i++)
|
||||
{
|
||||
float a0 = img->pixel(i + count * 3);
|
||||
float a1 = ref->pixel(i + count * 3);
|
||||
|
||||
float a = a0 - a1;
|
||||
|
||||
mae += fabs(a);
|
||||
}
|
||||
|
||||
return float(mae / count);
|
||||
}
|
||||
|
||||
|
||||
// Color space conversions based on:
|
||||
// http://www.brucelindbloom.com/
|
||||
|
||||
// Assumes input is in *linear* sRGB color space.
|
||||
static Vector3 rgbToXyz(Vector3::Arg c)
|
||||
{
|
||||
Vector3 xyz;
|
||||
xyz.x = 0.412453f * c.x + 0.357580f * c.y + 0.180423f * c.z;
|
||||
xyz.y = 0.212671f * c.x + 0.715160f * c.y + 0.072169f * c.z;
|
||||
xyz.z = 0.019334f * c.x + 0.119193f * c.y + 0.950227f * c.z;
|
||||
return xyz;
|
||||
}
|
||||
|
||||
static Vector3 xyzToRgb(Vector3::Arg c)
|
||||
{
|
||||
Vector3 rgb;
|
||||
rgb.x = 3.2404542f * c.x - 1.5371385f * c.y - 0.4985314f * c.z;
|
||||
rgb.y = -0.9692660f * c.x + 1.8760108f * c.y + 0.0415560f * c.z;
|
||||
rgb.z = 0.0556434f * c.x - 0.2040259f * c.y + 1.0572252f * c.z;
|
||||
return rgb;
|
||||
}
|
||||
|
||||
static float toLinear(float f)
|
||||
{
|
||||
return powf(f, 2.2f);
|
||||
}
|
||||
|
||||
static float toGamma(float f)
|
||||
{
|
||||
// @@ Use sRGB space?
|
||||
return powf(f, 1.0f/2.2f);
|
||||
}
|
||||
|
||||
static Vector3 toLinear(Vector3::Arg c)
|
||||
{
|
||||
return Vector3(toLinear(c.x), toLinear(c.y), toLinear(c.z));
|
||||
}
|
||||
|
||||
static Vector3 toGamma(Vector3::Arg c)
|
||||
{
|
||||
return Vector3(toGamma(c.x), toGamma(c.y), toGamma(c.z));
|
||||
}
|
||||
|
||||
static float f(float t)
|
||||
{
|
||||
const float epsilon = powf(6.0f/29.0f, 3);
|
||||
|
||||
if (t > epsilon) {
|
||||
return powf(t, 1.0f/3.0f);
|
||||
}
|
||||
else {
|
||||
return 1.0f/3.0f * powf(29.0f/6.0f, 2) * t + 4.0f / 29.0f;
|
||||
}
|
||||
}
|
||||
|
||||
static float finv(float t)
|
||||
{
|
||||
if (t > 6.0f / 29.0f) {
|
||||
return 3.0f * powf(6.0f / 29.0f, 2) * (t - 4.0f / 29.0f);
|
||||
}
|
||||
else {
|
||||
return powf(t, 3.0f);
|
||||
}
|
||||
}
|
||||
|
||||
static Vector3 xyzToCieLab(Vector3::Arg c)
|
||||
{
|
||||
// Normalized white point.
|
||||
const float Xn = 0.950456f;
|
||||
const float Yn = 1.0f;
|
||||
const float Zn = 1.088754f;
|
||||
|
||||
float Xr = c.x / Xn;
|
||||
float Yr = c.y / Yn;
|
||||
float Zr = c.z / Zn;
|
||||
|
||||
float fx = f(Xr);
|
||||
float fy = f(Yr);
|
||||
float fz = f(Zr);
|
||||
|
||||
float L = 116 * fx - 16;
|
||||
float a = 500 * (fx - fy);
|
||||
float b = 200 * (fy - fz);
|
||||
|
||||
return Vector3(L, a, b);
|
||||
}
|
||||
|
||||
static Vector3 rgbToCieLab(Vector3::Arg c)
|
||||
{
|
||||
return xyzToCieLab(rgbToXyz(toLinear(c)));
|
||||
}
|
||||
|
||||
// h is hue-angle in radians
|
||||
static Vector3 cieLabToLCh(Vector3::Arg c)
|
||||
{
|
||||
return Vector3(c.x, sqrtf(c.y*c.y + c.z*c.z), atan2f(c.y, c.z));
|
||||
}
|
||||
|
||||
static void rgbToCieLab(const FloatImage * rgbImage, FloatImage * LabImage)
|
||||
{
|
||||
nvDebugCheck(rgbImage != NULL && LabImage != NULL);
|
||||
nvDebugCheck(rgbImage->width() == LabImage->width() && rgbImage->height() == LabImage->height());
|
||||
nvDebugCheck(rgbImage->componentCount() >= 3 && LabImage->componentCount() >= 3);
|
||||
|
||||
const uint w = rgbImage->width();
|
||||
const uint h = LabImage->height();
|
||||
|
||||
const float * R = rgbImage->channel(0);
|
||||
const float * G = rgbImage->channel(1);
|
||||
const float * B = rgbImage->channel(2);
|
||||
|
||||
float * L = LabImage->channel(0);
|
||||
float * a = LabImage->channel(1);
|
||||
float * b = LabImage->channel(2);
|
||||
|
||||
const uint count = w*h;
|
||||
for (uint i = 0; i < count; i++)
|
||||
{
|
||||
Vector3 Lab = rgbToCieLab(Vector3(R[i], G[i], B[i]));
|
||||
L[i] = Lab.x;
|
||||
a[i] = Lab.y;
|
||||
b[i] = Lab.z;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Assumes input images are in linear sRGB space.
|
||||
float nv::cieLabError(const FloatImage * img0, const FloatImage * img1)
|
||||
{
|
||||
if (!sameLayout(img0, img1)) return FLT_MAX;
|
||||
nvDebugCheck(img0->componentCount() == 4 && img1->componentCount() == 4);
|
||||
|
||||
const float * r0 = img0->channel(0);
|
||||
const float * g0 = img0->channel(1);
|
||||
const float * b0 = img0->channel(2);
|
||||
|
||||
const float * r1 = img1->channel(0);
|
||||
const float * g1 = img1->channel(1);
|
||||
const float * b1 = img1->channel(2);
|
||||
|
||||
double error = 0.0f;
|
||||
|
||||
const uint count = img0->pixelCount();
|
||||
for (uint i = 0; i < count; i++)
|
||||
{
|
||||
Vector3 lab0 = rgbToCieLab(Vector3(r0[i], g0[i], b0[i]));
|
||||
Vector3 lab1 = rgbToCieLab(Vector3(r1[i], g1[i], b1[i]));
|
||||
|
||||
// @@ Measure Delta E.
|
||||
Vector3 delta = lab0 - lab1;
|
||||
|
||||
error += length(delta);
|
||||
}
|
||||
|
||||
return float(error / count);
|
||||
}
|
||||
|
||||
// Assumes input images are in linear sRGB space.
|
||||
float nv::cieLab94Error(const FloatImage * img0, const FloatImage * img1)
|
||||
{
|
||||
if (!sameLayout(img0, img1)) return FLT_MAX;
|
||||
nvDebugCheck(img0->componentCount() == 4 && img1->componentCount() == 4);
|
||||
|
||||
const float kL = 1;
|
||||
const float kC = 1;
|
||||
const float kH = 1;
|
||||
const float k1 = 0.045f;
|
||||
const float k2 = 0.015f;
|
||||
|
||||
const float sL = 1;
|
||||
|
||||
const float * r0 = img0->channel(0);
|
||||
const float * g0 = img0->channel(1);
|
||||
const float * b0 = img0->channel(2);
|
||||
|
||||
const float * r1 = img1->channel(0);
|
||||
const float * g1 = img1->channel(1);
|
||||
const float * b1 = img1->channel(2);
|
||||
|
||||
double error = 0.0f;
|
||||
|
||||
const uint count = img0->pixelCount();
|
||||
for (uint i = 0; i < count; ++i)
|
||||
{
|
||||
Vector3 lab0 = rgbToCieLab(Vector3(r0[i], g0[i], b0[i]));
|
||||
Vector3 lch0 = cieLabToLCh(lab0);
|
||||
Vector3 lab1 = rgbToCieLab(Vector3(r1[i], g1[i], b1[i]));
|
||||
Vector3 lch1 = cieLabToLCh(lab1);
|
||||
|
||||
const float sC = 1 + k1*lch0.x;
|
||||
const float sH = 1 + k2*lch0.x;
|
||||
|
||||
// @@ Measure Delta E using the 1994 definition
|
||||
Vector3 labDelta = lab0 - lab1;
|
||||
Vector3 lchDelta = lch0 - lch1;
|
||||
|
||||
double deltaLsq = powf(lchDelta.x / (kL*sL), 2);
|
||||
double deltaCsq = powf(lchDelta.y / (kC*sC), 2);
|
||||
|
||||
// avoid possible sqrt of negative value by computing (deltaH/(kH*sH))^2
|
||||
double deltaHsq = powf(labDelta.y, 2) + powf(labDelta.z, 2) - powf(lchDelta.y, 2);
|
||||
deltaHsq /= powf(kH*sH, 2);
|
||||
|
||||
error += sqrt(deltaLsq + deltaCsq + deltaHsq);
|
||||
}
|
||||
|
||||
return float(error / count);
|
||||
}
|
||||
|
||||
float nv::spatialCieLabError(const FloatImage * img0, const FloatImage * img1)
|
||||
{
|
||||
if (img0 == NULL || img1 == NULL || img0->width() != img1->width() || img0->height() != img1->height()) {
|
||||
return FLT_MAX;
|
||||
}
|
||||
nvDebugCheck(img0->componentCount() == 4 && img1->componentCount() == 4);
|
||||
|
||||
uint w = img0->width();
|
||||
uint h = img0->height();
|
||||
uint d = img0->depth();
|
||||
|
||||
FloatImage lab0, lab1; // Original images in CIE-Lab space.
|
||||
lab0.allocate(3, w, h, d);
|
||||
lab1.allocate(3, w, h, d);
|
||||
|
||||
// Convert input images to CIE-Lab.
|
||||
rgbToCieLab(img0, &lab0);
|
||||
rgbToCieLab(img1, &lab1);
|
||||
|
||||
// @@ Convolve each channel by the corresponding filter.
|
||||
/*
|
||||
GaussianFilter LFilter(5);
|
||||
GaussianFilter aFilter(5);
|
||||
GaussianFilter bFilter(5);
|
||||
|
||||
lab0.convolve(0, LFilter);
|
||||
lab0.convolve(1, aFilter);
|
||||
lab0.convolve(2, bFilter);
|
||||
|
||||
lab1.convolve(0, LFilter);
|
||||
lab1.convolve(1, aFilter);
|
||||
lab1.convolve(2, bFilter);
|
||||
*/
|
||||
// @@ Measure Delta E between lab0 and lab1.
|
||||
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
|
||||
// Assumes input images are normal maps.
|
||||
float nv::averageAngularError(const FloatImage * img0, const FloatImage * img1)
|
||||
{
|
||||
if (img0 == NULL || img1 == NULL || img0->width() != img1->width() || img0->height() != img1->height()) {
|
||||
return FLT_MAX;
|
||||
}
|
||||
nvDebugCheck(img0->componentCount() == 4 && img1->componentCount() == 4);
|
||||
|
||||
uint w = img0->width();
|
||||
uint h = img0->height();
|
||||
|
||||
const float * x0 = img0->channel(0);
|
||||
const float * y0 = img0->channel(1);
|
||||
const float * z0 = img0->channel(2);
|
||||
|
||||
const float * x1 = img1->channel(0);
|
||||
const float * y1 = img1->channel(1);
|
||||
const float * z1 = img1->channel(2);
|
||||
|
||||
double error = 0.0f;
|
||||
|
||||
const uint count = w*h;
|
||||
for (uint i = 0; i < count; i++)
|
||||
{
|
||||
Vector3 n0 = Vector3(x0[i], y0[i], z0[i]);
|
||||
Vector3 n1 = Vector3(x1[i], y1[i], z1[i]);
|
||||
|
||||
n0 = 2.0f * n0 - Vector3(1);
|
||||
n1 = 2.0f * n1 - Vector3(1);
|
||||
|
||||
n0 = normalizeSafe(n0, Vector3(0), 0.0f);
|
||||
n1 = normalizeSafe(n1, Vector3(0), 0.0f);
|
||||
|
||||
error += acos(clamp(dot(n0, n1), -1.0f, 1.0f));
|
||||
}
|
||||
|
||||
return float(error / count);
|
||||
}
|
||||
|
||||
float nv::rmsAngularError(const FloatImage * img0, const FloatImage * img1)
|
||||
{
|
||||
if (img0 == NULL || img1 == NULL || img0->width() != img1->width() || img0->height() != img1->height()) {
|
||||
return FLT_MAX;
|
||||
}
|
||||
nvDebugCheck(img0->componentCount() == 4 && img1->componentCount() == 4);
|
||||
|
||||
uint w = img0->width();
|
||||
uint h = img0->height();
|
||||
|
||||
const float * x0 = img0->channel(0);
|
||||
const float * y0 = img0->channel(1);
|
||||
const float * z0 = img0->channel(2);
|
||||
|
||||
const float * x1 = img1->channel(0);
|
||||
const float * y1 = img1->channel(1);
|
||||
const float * z1 = img1->channel(2);
|
||||
|
||||
double error = 0.0f;
|
||||
|
||||
const uint count = w*h;
|
||||
for (uint i = 0; i < count; i++)
|
||||
{
|
||||
Vector3 n0 = Vector3(x0[i], y0[i], z0[i]);
|
||||
Vector3 n1 = Vector3(x1[i], y1[i], z1[i]);
|
||||
|
||||
n0 = 2.0f * n0 - Vector3(1);
|
||||
n1 = 2.0f * n1 - Vector3(1);
|
||||
|
||||
n0 = normalizeSafe(n0, Vector3(0), 0.0f);
|
||||
n1 = normalizeSafe(n1, Vector3(0), 0.0f);
|
||||
|
||||
float angle = acosf(clamp(dot(n0, n1), -1.0f, 1.0f));
|
||||
error += angle * angle;
|
||||
}
|
||||
|
||||
return float(sqrt(error / count));
|
||||
}
|
||||
|
||||
|
||||
#include "ErrorMetric.h"
|
||||
#include "FloatImage.h"
|
||||
#include "Filter.h"
|
||||
|
||||
#include "nvmath/Matrix.h"
|
||||
#include "nvmath/Vector.inl"
|
||||
|
||||
#include <float.h> // FLT_MAX
|
||||
|
||||
using namespace nv;
|
||||
|
||||
float nv::rmsColorError(const FloatImage * ref, const FloatImage * img, bool alphaWeight)
|
||||
{
|
||||
if (!sameLayout(img, ref)) {
|
||||
return FLT_MAX;
|
||||
}
|
||||
nvDebugCheck(img->componentCount() == 4);
|
||||
nvDebugCheck(ref->componentCount() == 4);
|
||||
|
||||
double mse = 0;
|
||||
|
||||
const uint count = img->pixelCount();
|
||||
for (uint i = 0; i < count; i++)
|
||||
{
|
||||
float r0 = ref->pixel(i + count * 0);
|
||||
float g0 = ref->pixel(i + count * 1);
|
||||
float b0 = ref->pixel(i + count * 2);
|
||||
float a0 = ref->pixel(i + count * 3);
|
||||
float r1 = img->pixel(i + count * 0);
|
||||
float g1 = img->pixel(i + count * 1);
|
||||
float b1 = img->pixel(i + count * 2);
|
||||
//float a1 = img->pixel(i + count * 3);
|
||||
|
||||
float r = r0 - r1;
|
||||
float g = g0 - g1;
|
||||
float b = b0 - b1;
|
||||
|
||||
float a = 1;
|
||||
if (alphaWeight) a = a0 * a0; // @@ a0*a1 or a0*a0 ?
|
||||
|
||||
mse += (r * r) * a;
|
||||
mse += (g * g) * a;
|
||||
mse += (b * b) * a;
|
||||
}
|
||||
|
||||
return float(sqrt(mse / count));
|
||||
}
|
||||
|
||||
float nv::rmsAlphaError(const FloatImage * ref, const FloatImage * img)
|
||||
{
|
||||
if (!sameLayout(img, ref)) {
|
||||
return FLT_MAX;
|
||||
}
|
||||
nvDebugCheck(img->componentCount() == 4 && ref->componentCount() == 4);
|
||||
|
||||
double mse = 0;
|
||||
|
||||
const uint count = img->pixelCount();
|
||||
for (uint i = 0; i < count; i++)
|
||||
{
|
||||
float a0 = img->pixel(i + count * 3);
|
||||
float a1 = ref->pixel(i + count * 3);
|
||||
|
||||
float a = a0 - a1;
|
||||
|
||||
mse += a * a;
|
||||
}
|
||||
|
||||
return float(sqrt(mse / count));
|
||||
}
|
||||
|
||||
|
||||
float nv::averageColorError(const FloatImage * ref, const FloatImage * img, bool alphaWeight)
|
||||
{
|
||||
if (!sameLayout(img, ref)) {
|
||||
return FLT_MAX;
|
||||
}
|
||||
nvDebugCheck(img->componentCount() == 4);
|
||||
nvDebugCheck(ref->componentCount() == 4);
|
||||
|
||||
double mae = 0;
|
||||
|
||||
const uint count = img->pixelCount();
|
||||
for (uint i = 0; i < count; i++)
|
||||
{
|
||||
float r0 = img->pixel(i + count * 0);
|
||||
float g0 = img->pixel(i + count * 1);
|
||||
float b0 = img->pixel(i + count * 2);
|
||||
//float a0 = img->pixel(i + count * 3);
|
||||
float r1 = ref->pixel(i + count * 0);
|
||||
float g1 = ref->pixel(i + count * 1);
|
||||
float b1 = ref->pixel(i + count * 2);
|
||||
float a1 = ref->pixel(i + count * 3);
|
||||
|
||||
float r = fabs(r0 - r1);
|
||||
float g = fabs(g0 - g1);
|
||||
float b = fabs(b0 - b1);
|
||||
|
||||
float a = 1;
|
||||
if (alphaWeight) a = a1;
|
||||
|
||||
mae += r * a;
|
||||
mae += g * a;
|
||||
mae += b * a;
|
||||
}
|
||||
|
||||
return float(mae / count);
|
||||
}
|
||||
|
||||
float nv::averageAlphaError(const FloatImage * ref, const FloatImage * img)
|
||||
{
|
||||
if (img == NULL || ref == NULL || img->width() != ref->width() || img->height() != ref->height()) {
|
||||
return FLT_MAX;
|
||||
}
|
||||
nvDebugCheck(img->componentCount() == 4 && ref->componentCount() == 4);
|
||||
|
||||
double mae = 0;
|
||||
|
||||
const uint count = img->width() * img->height();
|
||||
for (uint i = 0; i < count; i++)
|
||||
{
|
||||
float a0 = img->pixel(i + count * 3);
|
||||
float a1 = ref->pixel(i + count * 3);
|
||||
|
||||
float a = a0 - a1;
|
||||
|
||||
mae += fabs(a);
|
||||
}
|
||||
|
||||
return float(mae / count);
|
||||
}
|
||||
|
||||
|
||||
float nv::rmsBilinearColorError(const FloatImage * ref, const FloatImage * img, FloatImage::WrapMode wm, bool alphaWeight)
|
||||
{
|
||||
nvDebugCheck(img->componentCount() == 4);
|
||||
nvDebugCheck(ref->componentCount() == 4);
|
||||
|
||||
double mse = 0;
|
||||
|
||||
const uint w0 = ref->width();
|
||||
const uint h0 = ref->height();
|
||||
const uint d0 = ref->depth();
|
||||
|
||||
const uint w1 = img->width();
|
||||
const uint h1 = img->height();
|
||||
const uint d1 = img->depth();
|
||||
|
||||
for (uint z = 0; z < d0; z++) {
|
||||
for (uint y = 0; y < h0; y++) {
|
||||
for (uint x = 0; x < w0; x++) {
|
||||
float r0 = ref->pixel(0, x, y, z);
|
||||
float g0 = ref->pixel(1, x, y, z);
|
||||
float b0 = ref->pixel(2, x, y, z);
|
||||
float a0 = ref->pixel(3, x, y, z);
|
||||
|
||||
float fx = float(x) / w0;
|
||||
float fy = float(y) / h0;
|
||||
float fz = float(z) / d0;
|
||||
|
||||
float r1 = img->sampleLinear(0, fx, fy, fz, wm);
|
||||
float g1 = img->sampleLinear(1, fx, fy, fz, wm);
|
||||
float b1 = img->sampleLinear(2, fx, fy, fz, wm);
|
||||
float a1 = img->sampleLinear(2, fx, fy, fz, wm);
|
||||
|
||||
float dr = r0 - r1;
|
||||
float dg = g0 - g1;
|
||||
float db = b0 - b1;
|
||||
float da = a0 - a1;
|
||||
|
||||
float w = 1;
|
||||
if (alphaWeight) w = a0 * a0; // @@ a0*a1 or a0*a0 ?
|
||||
|
||||
mse += (dr * dr) * w;
|
||||
mse += (dg * dg) * w;
|
||||
mse += (db * db) * w;
|
||||
mse += (da * da);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int count = w0 * h0 * d0;
|
||||
return float(sqrt(mse / count));
|
||||
}
|
||||
|
||||
|
||||
// Color space conversions based on:
|
||||
// http://www.brucelindbloom.com/
|
||||
|
||||
// Assumes input is in *linear* sRGB color space.
|
||||
static Vector3 rgbToXyz(Vector3::Arg c)
|
||||
{
|
||||
Vector3 xyz;
|
||||
xyz.x = 0.412453f * c.x + 0.357580f * c.y + 0.180423f * c.z;
|
||||
xyz.y = 0.212671f * c.x + 0.715160f * c.y + 0.072169f * c.z;
|
||||
xyz.z = 0.019334f * c.x + 0.119193f * c.y + 0.950227f * c.z;
|
||||
return xyz;
|
||||
}
|
||||
|
||||
static Vector3 xyzToRgb(Vector3::Arg c)
|
||||
{
|
||||
Vector3 rgb;
|
||||
rgb.x = 3.2404542f * c.x - 1.5371385f * c.y - 0.4985314f * c.z;
|
||||
rgb.y = -0.9692660f * c.x + 1.8760108f * c.y + 0.0415560f * c.z;
|
||||
rgb.z = 0.0556434f * c.x - 0.2040259f * c.y + 1.0572252f * c.z;
|
||||
return rgb;
|
||||
}
|
||||
|
||||
static float toLinear(float f)
|
||||
{
|
||||
return powf(f, 2.2f);
|
||||
}
|
||||
|
||||
static float toGamma(float f)
|
||||
{
|
||||
// @@ Use sRGB space?
|
||||
return powf(f, 1.0f/2.2f);
|
||||
}
|
||||
|
||||
static Vector3 toLinear(Vector3::Arg c)
|
||||
{
|
||||
return Vector3(toLinear(c.x), toLinear(c.y), toLinear(c.z));
|
||||
}
|
||||
|
||||
static Vector3 toGamma(Vector3::Arg c)
|
||||
{
|
||||
return Vector3(toGamma(c.x), toGamma(c.y), toGamma(c.z));
|
||||
}
|
||||
|
||||
static float f(float t)
|
||||
{
|
||||
const float epsilon = powf(6.0f/29.0f, 3);
|
||||
|
||||
if (t > epsilon) {
|
||||
return powf(t, 1.0f/3.0f);
|
||||
}
|
||||
else {
|
||||
return 1.0f/3.0f * powf(29.0f/6.0f, 2) * t + 4.0f / 29.0f;
|
||||
}
|
||||
}
|
||||
|
||||
static float finv(float t)
|
||||
{
|
||||
if (t > 6.0f / 29.0f) {
|
||||
return 3.0f * powf(6.0f / 29.0f, 2) * (t - 4.0f / 29.0f);
|
||||
}
|
||||
else {
|
||||
return powf(t, 3.0f);
|
||||
}
|
||||
}
|
||||
|
||||
static Vector3 xyzToCieLab(Vector3::Arg c)
|
||||
{
|
||||
// Normalized white point.
|
||||
const float Xn = 0.950456f;
|
||||
const float Yn = 1.0f;
|
||||
const float Zn = 1.088754f;
|
||||
|
||||
float Xr = c.x / Xn;
|
||||
float Yr = c.y / Yn;
|
||||
float Zr = c.z / Zn;
|
||||
|
||||
float fx = f(Xr);
|
||||
float fy = f(Yr);
|
||||
float fz = f(Zr);
|
||||
|
||||
float L = 116 * fx - 16;
|
||||
float a = 500 * (fx - fy);
|
||||
float b = 200 * (fy - fz);
|
||||
|
||||
return Vector3(L, a, b);
|
||||
}
|
||||
|
||||
static Vector3 rgbToCieLab(Vector3::Arg c)
|
||||
{
|
||||
return xyzToCieLab(rgbToXyz(toLinear(c)));
|
||||
}
|
||||
|
||||
// h is hue-angle in radians
|
||||
static Vector3 cieLabToLCh(Vector3::Arg c)
|
||||
{
|
||||
return Vector3(c.x, sqrtf(c.y*c.y + c.z*c.z), atan2f(c.y, c.z));
|
||||
}
|
||||
|
||||
static void rgbToCieLab(const FloatImage * rgbImage, FloatImage * LabImage)
|
||||
{
|
||||
nvDebugCheck(rgbImage != NULL && LabImage != NULL);
|
||||
nvDebugCheck(rgbImage->width() == LabImage->width() && rgbImage->height() == LabImage->height());
|
||||
nvDebugCheck(rgbImage->componentCount() >= 3 && LabImage->componentCount() >= 3);
|
||||
|
||||
const uint w = rgbImage->width();
|
||||
const uint h = LabImage->height();
|
||||
|
||||
const float * R = rgbImage->channel(0);
|
||||
const float * G = rgbImage->channel(1);
|
||||
const float * B = rgbImage->channel(2);
|
||||
|
||||
float * L = LabImage->channel(0);
|
||||
float * a = LabImage->channel(1);
|
||||
float * b = LabImage->channel(2);
|
||||
|
||||
const uint count = w*h;
|
||||
for (uint i = 0; i < count; i++)
|
||||
{
|
||||
Vector3 Lab = rgbToCieLab(Vector3(R[i], G[i], B[i]));
|
||||
L[i] = Lab.x;
|
||||
a[i] = Lab.y;
|
||||
b[i] = Lab.z;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Assumes input images are in linear sRGB space.
|
||||
float nv::cieLabError(const FloatImage * img0, const FloatImage * img1)
|
||||
{
|
||||
if (!sameLayout(img0, img1)) return FLT_MAX;
|
||||
nvDebugCheck(img0->componentCount() == 4 && img1->componentCount() == 4);
|
||||
|
||||
const float * r0 = img0->channel(0);
|
||||
const float * g0 = img0->channel(1);
|
||||
const float * b0 = img0->channel(2);
|
||||
|
||||
const float * r1 = img1->channel(0);
|
||||
const float * g1 = img1->channel(1);
|
||||
const float * b1 = img1->channel(2);
|
||||
|
||||
double error = 0.0f;
|
||||
|
||||
const uint count = img0->pixelCount();
|
||||
for (uint i = 0; i < count; i++)
|
||||
{
|
||||
Vector3 lab0 = rgbToCieLab(Vector3(r0[i], g0[i], b0[i]));
|
||||
Vector3 lab1 = rgbToCieLab(Vector3(r1[i], g1[i], b1[i]));
|
||||
|
||||
// @@ Measure Delta E.
|
||||
Vector3 delta = lab0 - lab1;
|
||||
|
||||
error += length(delta);
|
||||
}
|
||||
|
||||
return float(error / count);
|
||||
}
|
||||
|
||||
// Assumes input images are in linear sRGB space.
|
||||
float nv::cieLab94Error(const FloatImage * img0, const FloatImage * img1)
|
||||
{
|
||||
if (!sameLayout(img0, img1)) return FLT_MAX;
|
||||
nvDebugCheck(img0->componentCount() == 4 && img1->componentCount() == 4);
|
||||
|
||||
const float kL = 1;
|
||||
const float kC = 1;
|
||||
const float kH = 1;
|
||||
const float k1 = 0.045f;
|
||||
const float k2 = 0.015f;
|
||||
|
||||
const float sL = 1;
|
||||
|
||||
const float * r0 = img0->channel(0);
|
||||
const float * g0 = img0->channel(1);
|
||||
const float * b0 = img0->channel(2);
|
||||
|
||||
const float * r1 = img1->channel(0);
|
||||
const float * g1 = img1->channel(1);
|
||||
const float * b1 = img1->channel(2);
|
||||
|
||||
double error = 0.0f;
|
||||
|
||||
const uint count = img0->pixelCount();
|
||||
for (uint i = 0; i < count; ++i)
|
||||
{
|
||||
Vector3 lab0 = rgbToCieLab(Vector3(r0[i], g0[i], b0[i]));
|
||||
Vector3 lch0 = cieLabToLCh(lab0);
|
||||
Vector3 lab1 = rgbToCieLab(Vector3(r1[i], g1[i], b1[i]));
|
||||
Vector3 lch1 = cieLabToLCh(lab1);
|
||||
|
||||
const float sC = 1 + k1*lch0.x;
|
||||
const float sH = 1 + k2*lch0.x;
|
||||
|
||||
// @@ Measure Delta E using the 1994 definition
|
||||
Vector3 labDelta = lab0 - lab1;
|
||||
Vector3 lchDelta = lch0 - lch1;
|
||||
|
||||
double deltaLsq = powf(lchDelta.x / (kL*sL), 2);
|
||||
double deltaCsq = powf(lchDelta.y / (kC*sC), 2);
|
||||
|
||||
// avoid possible sqrt of negative value by computing (deltaH/(kH*sH))^2
|
||||
double deltaHsq = powf(labDelta.y, 2) + powf(labDelta.z, 2) - powf(lchDelta.y, 2);
|
||||
deltaHsq /= powf(kH*sH, 2);
|
||||
|
||||
error += sqrt(deltaLsq + deltaCsq + deltaHsq);
|
||||
}
|
||||
|
||||
return float(error / count);
|
||||
}
|
||||
|
||||
float nv::spatialCieLabError(const FloatImage * img0, const FloatImage * img1)
|
||||
{
|
||||
if (img0 == NULL || img1 == NULL || img0->width() != img1->width() || img0->height() != img1->height()) {
|
||||
return FLT_MAX;
|
||||
}
|
||||
nvDebugCheck(img0->componentCount() == 4 && img1->componentCount() == 4);
|
||||
|
||||
uint w = img0->width();
|
||||
uint h = img0->height();
|
||||
uint d = img0->depth();
|
||||
|
||||
FloatImage lab0, lab1; // Original images in CIE-Lab space.
|
||||
lab0.allocate(3, w, h, d);
|
||||
lab1.allocate(3, w, h, d);
|
||||
|
||||
// Convert input images to CIE-Lab.
|
||||
rgbToCieLab(img0, &lab0);
|
||||
rgbToCieLab(img1, &lab1);
|
||||
|
||||
// @@ Convolve each channel by the corresponding filter.
|
||||
/*
|
||||
GaussianFilter LFilter(5);
|
||||
GaussianFilter aFilter(5);
|
||||
GaussianFilter bFilter(5);
|
||||
|
||||
lab0.convolve(0, LFilter);
|
||||
lab0.convolve(1, aFilter);
|
||||
lab0.convolve(2, bFilter);
|
||||
|
||||
lab1.convolve(0, LFilter);
|
||||
lab1.convolve(1, aFilter);
|
||||
lab1.convolve(2, bFilter);
|
||||
*/
|
||||
// @@ Measure Delta E between lab0 and lab1.
|
||||
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
|
||||
// Assumes input images are normal maps.
|
||||
float nv::averageAngularError(const FloatImage * img0, const FloatImage * img1)
|
||||
{
|
||||
if (img0 == NULL || img1 == NULL || img0->width() != img1->width() || img0->height() != img1->height()) {
|
||||
return FLT_MAX;
|
||||
}
|
||||
nvDebugCheck(img0->componentCount() == 4 && img1->componentCount() == 4);
|
||||
|
||||
uint w = img0->width();
|
||||
uint h = img0->height();
|
||||
|
||||
const float * x0 = img0->channel(0);
|
||||
const float * y0 = img0->channel(1);
|
||||
const float * z0 = img0->channel(2);
|
||||
|
||||
const float * x1 = img1->channel(0);
|
||||
const float * y1 = img1->channel(1);
|
||||
const float * z1 = img1->channel(2);
|
||||
|
||||
double error = 0.0f;
|
||||
|
||||
const uint count = w*h;
|
||||
for (uint i = 0; i < count; i++)
|
||||
{
|
||||
Vector3 n0 = Vector3(x0[i], y0[i], z0[i]);
|
||||
Vector3 n1 = Vector3(x1[i], y1[i], z1[i]);
|
||||
|
||||
n0 = 2.0f * n0 - Vector3(1);
|
||||
n1 = 2.0f * n1 - Vector3(1);
|
||||
|
||||
n0 = normalizeSafe(n0, Vector3(0), 0.0f);
|
||||
n1 = normalizeSafe(n1, Vector3(0), 0.0f);
|
||||
|
||||
error += acos(clamp(dot(n0, n1), -1.0f, 1.0f));
|
||||
}
|
||||
|
||||
return float(error / count);
|
||||
}
|
||||
|
||||
float nv::rmsAngularError(const FloatImage * img0, const FloatImage * img1)
|
||||
{
|
||||
if (img0 == NULL || img1 == NULL || img0->width() != img1->width() || img0->height() != img1->height()) {
|
||||
return FLT_MAX;
|
||||
}
|
||||
nvDebugCheck(img0->componentCount() == 4 && img1->componentCount() == 4);
|
||||
|
||||
uint w = img0->width();
|
||||
uint h = img0->height();
|
||||
|
||||
const float * x0 = img0->channel(0);
|
||||
const float * y0 = img0->channel(1);
|
||||
const float * z0 = img0->channel(2);
|
||||
|
||||
const float * x1 = img1->channel(0);
|
||||
const float * y1 = img1->channel(1);
|
||||
const float * z1 = img1->channel(2);
|
||||
|
||||
double error = 0.0f;
|
||||
|
||||
const uint count = w*h;
|
||||
for (uint i = 0; i < count; i++)
|
||||
{
|
||||
Vector3 n0 = Vector3(x0[i], y0[i], z0[i]);
|
||||
Vector3 n1 = Vector3(x1[i], y1[i], z1[i]);
|
||||
|
||||
n0 = 2.0f * n0 - Vector3(1);
|
||||
n1 = 2.0f * n1 - Vector3(1);
|
||||
|
||||
n0 = normalizeSafe(n0, Vector3(0), 0.0f);
|
||||
n1 = normalizeSafe(n1, Vector3(0), 0.0f);
|
||||
|
||||
float angle = acosf(clamp(dot(n0, n1), -1.0f, 1.0f));
|
||||
error += angle * angle;
|
||||
}
|
||||
|
||||
return float(sqrt(error / count));
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
|
||||
#include "nvimage.h"
|
||||
#include "FloatImage.h" // For FloatImage::WrapMode
|
||||
|
||||
|
||||
namespace nv
|
||||
@ -9,13 +10,15 @@ namespace nv
|
||||
float rmsColorError(const FloatImage * ref, const FloatImage * img, bool alphaWeight);
|
||||
float rmsAlphaError(const FloatImage * ref, const FloatImage * img);
|
||||
|
||||
float averageColorError(const FloatImage * ref, const FloatImage * img, bool alphaWeight);
|
||||
float averageAlphaError(const FloatImage * ref, const FloatImage * img);
|
||||
|
||||
float rmsBilinearColorError(const FloatImage * ref, const FloatImage * img, FloatImage::WrapMode wm, bool alphaWeight);
|
||||
|
||||
float cieLabError(const FloatImage * ref, const FloatImage * img);
|
||||
float cieLab94Error(const FloatImage * ref, const FloatImage * img);
|
||||
float spatialCieLabError(const FloatImage * ref, const FloatImage * img);
|
||||
|
||||
float averageColorError(const FloatImage * ref, const FloatImage * img, bool alphaWeight);
|
||||
float averageAlphaError(const FloatImage * ref, const FloatImage * img);
|
||||
|
||||
float averageAngularError(const FloatImage * img0, const FloatImage * img1);
|
||||
float rmsAngularError(const FloatImage * img0, const FloatImage * img1);
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -35,6 +35,7 @@ namespace nv
|
||||
};
|
||||
|
||||
NVIMAGE_API FloatImage();
|
||||
NVIMAGE_API FloatImage(const FloatImage & img);
|
||||
NVIMAGE_API FloatImage(const Image * img);
|
||||
NVIMAGE_API virtual ~FloatImage();
|
||||
|
||||
@ -92,10 +93,10 @@ namespace nv
|
||||
NVIMAGE_API float applyKernelY(const Kernel1 * k, int x, int y, int z, uint c, WrapMode wm) const;
|
||||
NVIMAGE_API float applyKernelZ(const Kernel1 * k, int x, int y, int z, uint c, WrapMode wm) const;
|
||||
NVIMAGE_API void applyKernelX(const PolyphaseKernel & k, int y, int z, uint c, WrapMode wm, float * output) const;
|
||||
NVIMAGE_API void applyKernelY(const PolyphaseKernel & k, int x, int z, uint c, WrapMode wm, float * output) const;
|
||||
NVIMAGE_API void applyKernelY(const PolyphaseKernel & k, int x, int z, uint c, WrapMode wm, float * output, int output_stride) const;
|
||||
NVIMAGE_API void applyKernelZ(const PolyphaseKernel & k, int x, int y, uint c, WrapMode wm, float * output) const;
|
||||
NVIMAGE_API void applyKernelX(const PolyphaseKernel & k, int y, int z, uint c, uint a, WrapMode wm, float * output) const;
|
||||
NVIMAGE_API void applyKernelY(const PolyphaseKernel & k, int x, int z, uint c, uint a, WrapMode wm, float * output) const;
|
||||
NVIMAGE_API void applyKernelY(const PolyphaseKernel & k, int x, int z, uint c, uint a, WrapMode wm, float * output, int output_stride) const;
|
||||
NVIMAGE_API void applyKernelZ(const PolyphaseKernel & k, int x, int y, uint c, uint a, WrapMode wm, float * output) const;
|
||||
|
||||
|
||||
|
@ -42,13 +42,21 @@ const Image & Image::operator=(const Image & img)
|
||||
|
||||
void Image::allocate(uint w, uint h, uint d/*= 1*/)
|
||||
{
|
||||
free();
|
||||
m_width = w;
|
||||
m_height = h;
|
||||
m_depth = d;
|
||||
m_data = realloc<Color32>(m_data, w * h * d);
|
||||
}
|
||||
|
||||
void Image::acquire(Color32 * data, uint w, uint h, uint d/*= 1*/)
|
||||
{
|
||||
free();
|
||||
m_width = w;
|
||||
m_height = h;
|
||||
m_depth = d;
|
||||
m_data = data;
|
||||
}
|
||||
|
||||
void Image::resize(uint w, uint h, uint d/*= 1*/) {
|
||||
|
||||
Image img;
|
||||
|
@ -34,6 +34,7 @@ namespace nv
|
||||
|
||||
|
||||
void allocate(uint w, uint h, uint d = 1);
|
||||
void acquire(Color32 * data, uint w, uint h, uint d = 1);
|
||||
bool load(const char * name);
|
||||
|
||||
void resize(uint w, uint h, uint d = 1);
|
||||
|
@ -8,6 +8,8 @@
|
||||
#include "DirectDrawSurface.h"
|
||||
#include "PixelFormat.h"
|
||||
|
||||
#include "nvthread/ParallelFor.h"
|
||||
|
||||
#include "nvmath/Color.h"
|
||||
#include "nvmath/Half.h"
|
||||
|
||||
@ -19,31 +21,31 @@
|
||||
#include "nvcore/TextWriter.h"
|
||||
|
||||
// Extern
|
||||
#if defined(HAVE_FREEIMAGE)
|
||||
#if defined(NV_HAVE_FREEIMAGE)
|
||||
# include <FreeImage.h>
|
||||
// If FreeImage available, do not use individual libraries, since that produces link conflicts in some platforms.
|
||||
# undef HAVE_JPEG
|
||||
# undef HAVE_PNG
|
||||
# undef HAVE_TIFF
|
||||
# undef HAVE_OPENEXR
|
||||
# undef NV_HAVE_JPEG
|
||||
# undef NV_HAVE_PNG
|
||||
# undef NV_HAVE_TIFF
|
||||
# undef NV_HAVE_OPENEXR
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_JPEG)
|
||||
#if defined(NV_HAVE_JPEG)
|
||||
extern "C" {
|
||||
# include <jpeglib.h>
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_PNG)
|
||||
#if defined(NV_HAVE_PNG)
|
||||
# include <png.h>
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_TIFF)
|
||||
#if defined(NV_HAVE_TIFF)
|
||||
# define _TIFF_DATA_TYPEDEFS_
|
||||
# include <tiffio.h>
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_OPENEXR)
|
||||
#if defined(NV_HAVE_OPENEXR)
|
||||
# include <ImfIO.h>
|
||||
# include <ImathBox.h>
|
||||
# include <ImfChannelList.h>
|
||||
@ -52,7 +54,7 @@ extern "C" {
|
||||
# include <ImfArray.h>
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_STBIMAGE)
|
||||
#if defined(NV_HAVE_STBIMAGE)
|
||||
# define STBI_NO_STDIO
|
||||
# include <stb_image.h>
|
||||
#endif
|
||||
@ -303,6 +305,51 @@ static bool saveTGA(Stream & s, const Image * img)
|
||||
return true;
|
||||
}
|
||||
|
||||
#pragma optimize("", off)
|
||||
|
||||
// Save BMP image.
|
||||
static bool saveBMP(Stream & s, const Image * img)
|
||||
{
|
||||
int w = img->width();
|
||||
int h = img->height();
|
||||
int image_size = w * h * 3;
|
||||
|
||||
BmpFileHeader header;
|
||||
zero(header);
|
||||
header.type = BM_TYPE;
|
||||
header.size = BITMAPFILEHEADER_SIZE + BITMAPINFOHEADER_SIZE + image_size;
|
||||
header.offBits = BITMAPFILEHEADER_SIZE + BITMAPINFOHEADER_SIZE;
|
||||
|
||||
BmpInfoHeader info;
|
||||
zero(info);
|
||||
info.size = BITMAPINFOHEADER_SIZE;
|
||||
info.width = w;
|
||||
info.height = h;
|
||||
info.planes = 1;
|
||||
info.bitCount = 24;
|
||||
info.sizeImage = image_size;
|
||||
info.xPelsPerMeter = 2000;
|
||||
info.yPelsPerMeter = 2000;
|
||||
|
||||
s << header;
|
||||
s << info;
|
||||
|
||||
nv::Array<uint8> data;
|
||||
data.resize(3 * w);
|
||||
|
||||
for (int y = 0; y < h; y++) {
|
||||
for (int x = 0; x < w; x++) {
|
||||
data[x * 3 + 0] = img->pixel(x, h - y - 1).b;
|
||||
data[x * 3 + 1] = img->pixel(x, h - y - 1).g;
|
||||
data[x * 3 + 2] = img->pixel(x, h - y - 1).r;
|
||||
}
|
||||
|
||||
s.serialize(data.buffer(), data.size());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*static Image * loadPPM(Stream & s)
|
||||
{
|
||||
// @@
|
||||
@ -324,7 +371,10 @@ static bool savePPM(Stream & s, const Image * img)
|
||||
writer.writeString("255\n");
|
||||
for (uint i = 0; i < w * h; i++) {
|
||||
Color32 c = img->pixel(i);
|
||||
s << (uint8_t&)c.r << (uint8_t&)c.g << (uint8_t&)c.b;
|
||||
uint8 r = c.r; // current version of apple's llvm compiling for arm64 doesn't like taking the address of a bit-field. Workaround by using the stack
|
||||
uint8 g = c.g;
|
||||
uint8 b = c.b;
|
||||
s << r << g << b;
|
||||
}
|
||||
|
||||
return true;
|
||||
@ -653,7 +703,7 @@ static bool saveFloatDDS(Stream & s, const FloatImage * img, uint base_component
|
||||
}
|
||||
|
||||
|
||||
#if defined(HAVE_PNG)
|
||||
#if defined(NV_HAVE_PNG)
|
||||
|
||||
static void user_read_data(png_structp png_ptr, png_bytep data, png_size_t length)
|
||||
{
|
||||
@ -902,9 +952,9 @@ static bool savePNG(Stream & s, const Image * img, const char ** tags/*=NULL*/)
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif // defined(HAVE_PNG)
|
||||
#endif // defined(NV_HAVE_PNG)
|
||||
|
||||
#if defined(HAVE_JPEG)
|
||||
#if defined(NV_HAVE_JPEG)
|
||||
|
||||
static void init_source (j_decompress_ptr /*cinfo*/){
|
||||
}
|
||||
@ -1011,9 +1061,9 @@ static Image * loadJPG(Stream & s)
|
||||
return img.release();
|
||||
}
|
||||
|
||||
#endif // defined(HAVE_JPEG)
|
||||
#endif // defined(NV_HAVE_JPEG)
|
||||
|
||||
#if defined(HAVE_TIFF)
|
||||
#if defined(NV_HAVE_TIFF)
|
||||
|
||||
/*
|
||||
static tsize_t tiffReadWriteProc(thandle_t h, tdata_t ptr, tsize_t size)
|
||||
@ -1207,9 +1257,9 @@ static bool saveFloatTIFF(const char * fileName, const FloatImage * fimage, uint
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif // defined(HAVE_TIFF)
|
||||
#endif // defined(NV_HAVE_TIFF)
|
||||
|
||||
#if defined(HAVE_OPENEXR)
|
||||
#if defined(NV_HAVE_OPENEXR)
|
||||
|
||||
namespace
|
||||
{
|
||||
@ -1348,10 +1398,10 @@ static bool saveFloatEXR(const char * fileName, const FloatImage * fimage, uint
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif // defined(HAVE_OPENEXR)
|
||||
#endif // defined(NV_HAVE_OPENEXR)
|
||||
|
||||
|
||||
#if defined(HAVE_FREEIMAGE)
|
||||
#if defined(NV_HAVE_FREEIMAGE)
|
||||
|
||||
static unsigned DLL_CALLCONV ReadProc(void *buffer, unsigned size, unsigned count, fi_handle handle)
|
||||
{
|
||||
@ -1688,10 +1738,10 @@ bool nv::ImageIO::saveFloatFreeImage(FREE_IMAGE_FORMAT fif, Stream & s, const Fl
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif // defined(HAVE_FREEIMAGE)
|
||||
#endif // defined(NV_HAVE_FREEIMAGE)
|
||||
|
||||
|
||||
#if defined(HAVE_STBIMAGE)
|
||||
#if defined(NV_HAVE_STBIMAGE)
|
||||
|
||||
static Image * loadSTB(Stream & s)
|
||||
{
|
||||
@ -1704,28 +1754,22 @@ static Image * loadSTB(Stream & s)
|
||||
int w, h, n;
|
||||
uint8 * data = stbi_load_from_memory(buffer, size, &w, &h, &n, 4);
|
||||
|
||||
// @@ Hack: STB is returning n=4, because we request 4 components, even when input only has 3.
|
||||
n = 3;
|
||||
|
||||
delete [] buffer;
|
||||
|
||||
if (data != NULL) {
|
||||
Image * img = new Image;
|
||||
img->allocate(w, h);
|
||||
img->acquire((Color32 *)data, w, h);
|
||||
img->setFormat(n == 4 ? Image::Format_ARGB : Image::Format_RGB);
|
||||
|
||||
for (int y = 0; y < h; ++y)
|
||||
{
|
||||
nv::Color32* dest = img->scanline(y);
|
||||
uint8* src = data + y * w * 4;
|
||||
|
||||
for (int x = 0; x < w; ++x)
|
||||
{
|
||||
dest[x].r = src[x * 4 + 0];
|
||||
dest[x].g = src[x * 4 + 1];
|
||||
dest[x].b = src[x * 4 + 2];
|
||||
dest[x].a = src[x * 4 + 3];
|
||||
}
|
||||
}
|
||||
|
||||
free(data);
|
||||
int count = w * h;
|
||||
for (int i = 0; i < count; ++i) {
|
||||
//parallel_for(count, 128, [&](int i) {
|
||||
Color32 & pixel = img->pixel(i);
|
||||
swap(pixel.r, pixel.b);
|
||||
}//);
|
||||
|
||||
return img;
|
||||
}
|
||||
@ -1766,7 +1810,7 @@ static FloatImage * loadFloatSTB(Stream & s)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#endif // defined(HAVE_STBIMAGE)
|
||||
#endif // defined(NV_HAVE_STBIMAGE)
|
||||
|
||||
|
||||
|
||||
@ -1804,32 +1848,33 @@ Image * nv::ImageIO::load(const char * fileName, Stream & s)
|
||||
return loadPPM(s);
|
||||
}*/
|
||||
|
||||
#if defined(HAVE_JPEG)
|
||||
#if defined(NV_HAVE_JPEG)
|
||||
if (strCaseDiff(extension, ".jpg") == 0 || strCaseDiff(extension, ".jpeg") == 0) {
|
||||
return loadJPG(s);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_PNG)
|
||||
#if defined(NV_HAVE_PNG)
|
||||
if (strCaseDiff(extension, ".png") == 0) {
|
||||
return loadPNG(s);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_FREEIMAGE)
|
||||
#if defined(NV_HAVE_FREEIMAGE)
|
||||
FREE_IMAGE_FORMAT fif = FreeImage_GetFIFFromFilename(fileName);
|
||||
if (fif != FIF_UNKNOWN && FreeImage_FIFSupportsReading(fif)) {
|
||||
return loadFreeImage(fif, s);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_STBIMAGE)
|
||||
#if defined(NV_HAVE_STBIMAGE)
|
||||
return loadSTB(s);
|
||||
#endif
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
bool nv::ImageIO::save(const char * fileName, Stream & s, const Image * img, const char ** tags/*=NULL*/)
|
||||
{
|
||||
nvDebugCheck(fileName != NULL);
|
||||
@ -1838,6 +1883,10 @@ bool nv::ImageIO::save(const char * fileName, Stream & s, const Image * img, con
|
||||
|
||||
const char * extension = Path::extension(fileName);
|
||||
|
||||
if (strCaseDiff(extension, ".bmp") == 0) {
|
||||
return saveBMP(s, img);
|
||||
}
|
||||
|
||||
if (strCaseDiff(extension, ".tga") == 0) {
|
||||
return saveTGA(s, img);
|
||||
}
|
||||
@ -1846,13 +1895,13 @@ bool nv::ImageIO::save(const char * fileName, Stream & s, const Image * img, con
|
||||
return savePPM(s, img);
|
||||
}
|
||||
|
||||
#if defined(HAVE_PNG)
|
||||
#if defined(NV_HAVE_PNG)
|
||||
if (strCaseDiff(extension, ".png") == 0) {
|
||||
return savePNG(s, img, tags);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_FREEIMAGE)
|
||||
#if defined(NV_HAVE_FREEIMAGE)
|
||||
FREE_IMAGE_FORMAT fif = FreeImage_GetFIFFromFilename(fileName);
|
||||
if (fif != FIF_UNKNOWN && FreeImage_FIFSupportsWriting(fif)) {
|
||||
return saveFreeImage(fif, s, img, tags);
|
||||
@ -1899,27 +1948,27 @@ FloatImage * nv::ImageIO::loadFloat(const char * fileName, Stream & s)
|
||||
return loadFloatPFM(s);
|
||||
}*/
|
||||
|
||||
#if defined(HAVE_TIFF)
|
||||
#if defined(NV_HAVE_TIFF)
|
||||
#pragma NV_MESSAGE("TODO: Load TIFF from stream.")
|
||||
if (strCaseDiff(extension, ".tif") == 0 || strCaseDiff(extension, ".tiff") == 0) {
|
||||
return loadFloatTIFF(fileName, s);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_OPENEXR)
|
||||
#if defined(NV_HAVE_OPENEXR)
|
||||
#pragma NV_MESSAGE("TODO: Load EXR from stream.")
|
||||
if (strCaseDiff(extension, ".exr") == 0) {
|
||||
return loadFloatEXR(fileName, s);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_STBIMAGE)
|
||||
#if defined(NV_HAVE_STBIMAGE)
|
||||
if (strCaseDiff(extension, ".hdr") == 0) {
|
||||
return loadFloatSTB(s);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_FREEIMAGE)
|
||||
#if defined(NV_HAVE_FREEIMAGE)
|
||||
FREE_IMAGE_FORMAT fif = FreeImage_GetFIFFromFilename(fileName);
|
||||
if (fif != FIF_UNKNOWN && FreeImage_FIFSupportsReading(fif)) {
|
||||
return loadFloatFreeImage(fif, s);
|
||||
@ -1961,7 +2010,7 @@ bool nv::ImageIO::saveFloat(const char * fileName, Stream & s, const FloatImage
|
||||
return saveFloatPFM(s, fimage, baseComponent, componentCount);
|
||||
}*/
|
||||
|
||||
#if defined(HAVE_FREEIMAGE)
|
||||
#if defined(NV_HAVE_FREEIMAGE)
|
||||
FREE_IMAGE_FORMAT fif = FreeImage_GetFIFFromFilename(fileName);
|
||||
if (fif != FIF_UNKNOWN && FreeImage_FIFSupportsWriting(fif)) {
|
||||
return saveFloatFreeImage(fif, s, fimage, baseComponent, componentCount);
|
||||
@ -2005,14 +2054,15 @@ bool nv::ImageIO::saveFloat(const char * fileName, const FloatImage * fimage, ui
|
||||
}
|
||||
|
||||
const char * extension = Path::extension(fileName);
|
||||
NV_UNUSED(extension);
|
||||
|
||||
#if defined(HAVE_OPENEXR)
|
||||
#if defined(NV_HAVE_OPENEXR)
|
||||
if (strCaseDiff(extension, ".exr") == 0) {
|
||||
return saveFloatEXR(fileName, fimage, baseComponent, componentCount);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_TIFF)
|
||||
#if defined(NV_HAVE_TIFF)
|
||||
if (strCaseDiff(extension, ".tif") == 0 || strCaseDiff(extension, ".tiff") == 0) {
|
||||
return saveFloatTIFF(fileName, fimage, baseComponent, componentCount);
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
// This code is in the public domain -- Ignacio Casta<74>o <castano@gmail.com>
|
||||
|
||||
#include "KtxFile.h"
|
||||
#include "nvcore/StdStream.h"
|
||||
|
||||
using namespace nv;
|
||||
|
||||
@ -10,6 +11,8 @@ static const uint8 fileIdentifier[12] = {
|
||||
0x0D, 0x0A, 0x1A, 0x0A
|
||||
};
|
||||
|
||||
namespace nv
|
||||
{
|
||||
|
||||
KtxHeader::KtxHeader() {
|
||||
memcpy(identifier, fileIdentifier, 12);
|
||||
@ -19,8 +22,8 @@ KtxHeader::KtxHeader() {
|
||||
glType = 0;
|
||||
glTypeSize = 1;
|
||||
glFormat = 0;
|
||||
glInternalFormat = KTX_RGBA;
|
||||
glBaseInternalFormat = KTX_RGBA;
|
||||
glInternalFormat = KTX_INTERNAL_COMPRESSED_SRGB_S3TC_DXT1;
|
||||
glBaseInternalFormat = KTX_BASE_INTERNAL_RGB;
|
||||
pixelWidth = 0;
|
||||
pixelHeight = 0;
|
||||
pixelDepth = 0;
|
||||
@ -31,9 +34,9 @@ KtxHeader::KtxHeader() {
|
||||
}
|
||||
|
||||
|
||||
Stream & operator<< (Stream & s, DDSHeader & header) {
|
||||
Stream & operator<< (Stream & s, KtxHeader & header) {
|
||||
s.serialize(header.identifier, 12);
|
||||
s << header.endiannes << header.glType << header.glTypeSize << header.glFormat << header.glInternalFormat << header.glBaseInternalFormat;
|
||||
s << header.endianness << header.glType << header.glTypeSize << header.glFormat << header.glInternalFormat << header.glBaseInternalFormat;
|
||||
s << header.pixelWidth << header.pixelHeight << header.pixelDepth;
|
||||
s << header.numberOfArrayElements << header.numberOfFaces << header.numberOfMipmapLevels;
|
||||
s << header.bytesOfKeyValueData;
|
||||
@ -41,7 +44,7 @@ Stream & operator<< (Stream & s, DDSHeader & header) {
|
||||
}
|
||||
|
||||
|
||||
KtxFile::KtxFile() {
|
||||
/*KtxFile::KtxFile() {
|
||||
}
|
||||
KtxFile::~KtxFile() {
|
||||
}
|
||||
@ -49,7 +52,7 @@ KtxFile::~KtxFile() {
|
||||
void KtxFile::addKeyValue(const char * key, const char * value) {
|
||||
keyArray.append(key);
|
||||
valueArray.append(value);
|
||||
bytesOfKeyValueData += strlen(key) + 1 + strlen(value) + 1;
|
||||
header.bytesOfKeyValueData += strlen(key) + 1 + strlen(value) + 1;
|
||||
}
|
||||
|
||||
|
||||
@ -77,7 +80,8 @@ Stream & operator<< (Stream & s, KtxFile & file) {
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
}*/
|
||||
|
||||
} // nv
|
||||
|
||||
|
||||
|
@ -6,6 +6,7 @@
|
||||
|
||||
#include "nvimage.h"
|
||||
#include "nvcore/StrLib.h"
|
||||
#include "nvcore/Array.h"
|
||||
|
||||
// KTX File format specification:
|
||||
// http://www.khronos.org/opengles/sdk/tools/KTX/file_format_spec/#key
|
||||
@ -14,22 +15,99 @@ namespace nv
|
||||
{
|
||||
class Stream;
|
||||
|
||||
// GL types (Table 3.2)
|
||||
const uint KTX_UNSIGNED_BYTE;
|
||||
const uint KTX_UNSIGNED_SHORT_5_6_5;
|
||||
// ...
|
||||
// GL types
|
||||
const uint KTX_UNSIGNED_BYTE = 0x1401;
|
||||
const uint KTX_BYTE = 0x1400;
|
||||
const uint KTX_UNSIGNED_SHORT = 0x1403;
|
||||
const uint KTX_SHORT = 0x1402;
|
||||
const uint KTX_UNSIGNED_INT = 0x1405;
|
||||
const uint KTX_INT = 0x1404;
|
||||
const uint KTX_FLOAT = 0x1406;
|
||||
const uint KTX_UNSIGNED_BYTE_3_3_2 = 0x8032;
|
||||
const uint KTX_UNSIGNED_BYTE_2_3_3_REV = 0x8362;
|
||||
const uint KTX_UNSIGNED_SHORT_5_6_5 = 0x8363;
|
||||
const uint KTX_UNSIGNED_SHORT_5_6_5_REV = 0x8364;
|
||||
const uint KTX_UNSIGNED_SHORT_4_4_4_4 = 0x8033;
|
||||
const uint KTX_UNSIGNED_SHORT_4_4_4_4_REV = 0x8365;
|
||||
const uint KTX_UNSIGNED_SHORT_5_5_5_1 = 0x8034;
|
||||
const uint KTX_UNSIGNED_SHORT_1_5_5_5_REV = 0x8366;
|
||||
const uint KTX_UNSIGNED_INT_8_8_8_8 = 0x8035;
|
||||
const uint KTX_UNSIGNED_INT_8_8_8_8_REV = 0x8367;
|
||||
const uint KTX_UNSIGNED_INT_10_10_10_2 = 0x8036;
|
||||
const uint KTX_UNSIGNED_INT_2_10_10_10_REV = 0x8368;
|
||||
|
||||
// GL formats (Table 3.3)
|
||||
// ...
|
||||
// GL formats
|
||||
const uint KTX_FORMAT_RED = 0x1903;
|
||||
const uint KTX_FORMAT_RG = 0x8227;
|
||||
const uint KTX_FORMAT_RGB = 0x1907;
|
||||
const uint KTX_FORMAT_BGR = 0x80E0;
|
||||
const uint KTX_FORMAT_RGBA = 0x1908;
|
||||
const uint KTX_FORMAT_BGRA = 0x80E1;
|
||||
const uint KTX_FORMAT_RED_INTEGER = 0x8D94;
|
||||
const uint KTX_FORMAT_RG_INTEGER = 0x8228;
|
||||
const uint KTX_FORMAT_RGB_INTEGER = 0x8D98;
|
||||
const uint KTX_FORMAT_BGR_INTEGER = 0x8D9A;
|
||||
const uint KTX_FORMAT_RGBA_INTEGER = 0x8D99;
|
||||
const uint KTX_FORMAT_BGRA_INTEGER = 0x8D9B;
|
||||
const uint KTX_FORMAT_STENCIL_INDEX = 0x1901;
|
||||
const uint KTX_FORMAT_DEPTH_COMPONENT = 0x1902;
|
||||
const uint KTX_FORMAT_DEPTH_STENCIL = 0x84F9;
|
||||
|
||||
// GL internal formats (Table 3.12, 3.13)
|
||||
// ...
|
||||
// GL internal formats
|
||||
// BC1
|
||||
const uint KTX_INTERNAL_COMPRESSED_RGB_S3TC_DXT1 = 0x83F0;
|
||||
const uint KTX_INTERNAL_COMPRESSED_SRGB_S3TC_DXT1 = 0x8C4C;
|
||||
// BC1a
|
||||
const uint KTX_INTERNAL_COMPRESSED_RGBA_S3TC_DXT1 = 0x83F1;
|
||||
const uint KTX_INTERNAL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1 = 0x8C4D;
|
||||
// BC2
|
||||
const uint KTX_INTERNAL_COMPRESSED_RGBA_S3TC_DXT3 = 0x83F2;
|
||||
const uint KTX_INTERNAL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3 = 0x8C4E;
|
||||
// BC3
|
||||
const uint KTX_INTERNAL_COMPRESSED_RGBA_S3TC_DXT5 = 0x83F3;
|
||||
const uint KTX_INTERNAL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5 = 0x8C4F;
|
||||
// BC4
|
||||
const uint KTX_INTERNAL_COMPRESSED_RED_RGTC1 = 0x8DBB;
|
||||
const uint KTX_INTERNAL_COMPRESSED_SIGNED_RED_RGTC1 = 0x8DBC;
|
||||
// BC5
|
||||
const uint KTX_INTERNAL_COMPRESSED_RG_RGTC2 = 0x8DBD;
|
||||
const uint KTX_INTERNAL_COMPRESSED_SIGNED_RG_RGTC2 = 0x8DBE;
|
||||
// BC6
|
||||
const uint KTX_INTERNAL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT = 0x8E8F;
|
||||
const uint KTX_INTERNAL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT = 0x8E8E;
|
||||
// BC7
|
||||
const uint KTX_INTERNAL_COMPRESSED_RGBA_BPTC_UNORM = 0x8E8C;
|
||||
const uint KTX_INTERNAL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM = 0x8E8D;
|
||||
|
||||
// GL base internal format. (Table 3.11)
|
||||
const uint KTX_RGB;
|
||||
const uint KTX_RGBA;
|
||||
const uint KTX_ALPHA;
|
||||
// ...
|
||||
// ETC
|
||||
const uint KTX_INTERNAL_COMPRESSED_RGB_ETC1 = 0x8D64;
|
||||
const uint KTX_INTERNAL_COMPRESSED_SRGB_ETC1 = 0x8D64; // ???
|
||||
|
||||
// ETC2
|
||||
const uint KTX_INTERNAL_COMPRESSED_RED_EAC = 0x9270;
|
||||
const uint KTX_INTERNAL_COMPRESSED_SIGNED_RED_EAC = 0x9271;
|
||||
|
||||
const uint KTX_INTERNAL_COMPRESSED_RG_EAC = 0x9272;
|
||||
const uint KTX_INTERNAL_COMPRESSED_SIGNED_RG_EAC = 0x9273;
|
||||
|
||||
const uint KTX_INTERNAL_COMPRESSED_RGB_ETC2 = 0x9274;
|
||||
const uint KTX_INTERNAL_COMPRESSED_SRGB_ETC2 = 0x9275;
|
||||
|
||||
const uint KTX_INTERNAL_COMPRESSED_RGB_PUNCHTHROUGH_ALPHA_ETC2 = 0x9276;
|
||||
const uint KTX_INTERNAL_COMPRESSED_SRGB_PUNCHTHROUGH_ALPHA_ETC2 = 0x9277;
|
||||
|
||||
const uint KTX_INTERNAL_COMPRESSED_RGBA_ETC2_EAC = 0x9278;
|
||||
const uint KTX_INTERNAL_COMPRESSED_SRGB_ALPHA_ETC2_EAC = 0x9279;
|
||||
|
||||
|
||||
// GL base internal formats
|
||||
const uint KTX_BASE_INTERNAL_DEPTH_COMPONENT = 0x1902;
|
||||
const uint KTX_BASE_INTERNAL_DEPTH_STENCIL = 0x84F9;
|
||||
const uint KTX_BASE_INTERNAL_RED = 0x1903;
|
||||
const uint KTX_BASE_INTERNAL_RG = 0x8227;
|
||||
const uint KTX_BASE_INTERNAL_RGB = 0x1907;
|
||||
const uint KTX_BASE_INTERNAL_RGBA = 0x1908;
|
||||
const uint KTX_BASE_INTERNAL_STENCIL_INDEX = 0x1901;
|
||||
|
||||
|
||||
struct KtxHeader {
|
||||
@ -52,10 +130,10 @@ namespace nv
|
||||
|
||||
};
|
||||
|
||||
NVIMAGE_API Stream & operator<< (Stream & s, DDSHeader & header);
|
||||
NVIMAGE_API Stream & operator<< (Stream & s, KtxHeader & header);
|
||||
|
||||
|
||||
struct KtxFile {
|
||||
/* struct KtxFile {
|
||||
KtxFile();
|
||||
~KtxFile();
|
||||
|
||||
@ -66,10 +144,9 @@ namespace nv
|
||||
|
||||
Array<String> keyArray;
|
||||
Array<String> valueArray;
|
||||
|
||||
};
|
||||
|
||||
NVIMAGE_API Stream & operator<< (Stream & s, KtxFile & file);
|
||||
NVIMAGE_API Stream & operator<< (Stream & s, KtxFile & file);*/
|
||||
|
||||
|
||||
/*
|
||||
|
@ -1,208 +1,208 @@
|
||||
// Copyright NVIDIA Corporation 2007 -- Ignacio Castano <icastano@nvidia.com>
|
||||
//
|
||||
// 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 "NormalMap.h"
|
||||
#include "Filter.h"
|
||||
#include "FloatImage.h"
|
||||
#include "Image.h"
|
||||
|
||||
#include "nvmath/Color.inl"
|
||||
#include "nvmath/Vector.h"
|
||||
|
||||
#include "nvcore/Ptr.h"
|
||||
|
||||
#include <string.h> // memcpy
|
||||
|
||||
|
||||
using namespace nv;
|
||||
|
||||
// Create normal map using the given kernels.
|
||||
static FloatImage * createNormalMap(const Image * img, FloatImage::WrapMode wm, Vector4::Arg heightWeights, const Kernel2 * kdu, const Kernel2 * kdv)
|
||||
{
|
||||
nvDebugCheck(kdu != NULL);
|
||||
nvDebugCheck(kdv != NULL);
|
||||
nvDebugCheck(img != NULL);
|
||||
|
||||
const uint w = img->width();
|
||||
const uint h = img->height();
|
||||
|
||||
AutoPtr<FloatImage> fimage(new FloatImage());
|
||||
fimage->allocate(4, w, h);
|
||||
|
||||
// Compute height and store in alpha channel:
|
||||
float * alphaChannel = fimage->channel(3);
|
||||
for(uint i = 0; i < w * h; i++)
|
||||
{
|
||||
Vector4 color = toVector4(img->pixel(i));
|
||||
alphaChannel[i] = dot(color, heightWeights);
|
||||
}
|
||||
|
||||
float heightScale = 1.0f / 16.0f; // @@ Use a user defined factor.
|
||||
|
||||
for(uint y = 0; y < h; y++)
|
||||
{
|
||||
for(uint x = 0; x < w; x++)
|
||||
{
|
||||
const float du = fimage->applyKernelXY(kdu, x, y, 0, 3, wm);
|
||||
const float dv = fimage->applyKernelXY(kdv, x, y, 0, 3, wm);
|
||||
|
||||
Vector3 n = normalize(Vector3(du, dv, heightScale));
|
||||
|
||||
fimage->pixel(0, x, y, 0) = 0.5f * n.x + 0.5f;
|
||||
fimage->pixel(1, x, y, 0) = 0.5f * n.y + 0.5f;
|
||||
fimage->pixel(2, x, y, 0) = 0.5f * n.z + 0.5f;
|
||||
}
|
||||
}
|
||||
|
||||
return fimage.release();
|
||||
}
|
||||
|
||||
|
||||
// Create normal map using the given kernels.
|
||||
static FloatImage * createNormalMap(const FloatImage * img, FloatImage::WrapMode wm, const Kernel2 * kdu, const Kernel2 * kdv)
|
||||
{
|
||||
nvDebugCheck(kdu != NULL);
|
||||
nvDebugCheck(kdv != NULL);
|
||||
nvDebugCheck(img != NULL);
|
||||
|
||||
#pragma NV_MESSAGE("FIXME: Height scale parameter should go away. It should be a sensible value that produces good results when the heightmap is in the [0, 1] range.")
|
||||
const float heightScale = 1.0f / 16.0f;
|
||||
|
||||
const uint w = img->width();
|
||||
const uint h = img->height();
|
||||
|
||||
AutoPtr<FloatImage> img_out(new FloatImage());
|
||||
img_out->allocate(4, w, h);
|
||||
|
||||
for (uint y = 0; y < h; y++)
|
||||
{
|
||||
for (uint x = 0; x < w; x++)
|
||||
{
|
||||
const float du = img->applyKernelXY(kdu, x, y, 0, 3, wm);
|
||||
const float dv = img->applyKernelXY(kdv, x, y, 0, 3, wm);
|
||||
|
||||
Vector3 n = normalize(Vector3(du, dv, heightScale));
|
||||
|
||||
img_out->pixel(0, x, y, 0) = n.x;
|
||||
img_out->pixel(1, x, y, 0) = n.y;
|
||||
img_out->pixel(2, x, y, 0) = n.z;
|
||||
}
|
||||
}
|
||||
|
||||
// Copy alpha channel.
|
||||
/*for (uint y = 0; y < h; y++)
|
||||
{
|
||||
for (uint x = 0; x < w; x++)
|
||||
{
|
||||
|
||||
img_out->pixel(3, x, y, 0) = img->pixel(3, x, y, 0);
|
||||
}
|
||||
}*/
|
||||
memcpy(img_out->channel(3), img->channel(3), w * h * sizeof(float));
|
||||
|
||||
return img_out.release();
|
||||
}
|
||||
|
||||
|
||||
/// Create normal map using the given filter.
|
||||
FloatImage * nv::createNormalMap(const Image * img, FloatImage::WrapMode wm, Vector4::Arg heightWeights, NormalMapFilter filter /*= Sobel3x3*/)
|
||||
{
|
||||
nvDebugCheck(img != NULL);
|
||||
|
||||
// Init the kernels.
|
||||
Kernel2 * kdu = NULL;
|
||||
Kernel2 * kdv = NULL;
|
||||
|
||||
switch(filter)
|
||||
{
|
||||
case NormalMapFilter_Sobel3x3:
|
||||
kdu = new Kernel2(3);
|
||||
break;
|
||||
case NormalMapFilter_Sobel5x5:
|
||||
kdu = new Kernel2(5);
|
||||
break;
|
||||
case NormalMapFilter_Sobel7x7:
|
||||
kdu = new Kernel2(7);
|
||||
break;
|
||||
case NormalMapFilter_Sobel9x9:
|
||||
kdu = new Kernel2(9);
|
||||
break;
|
||||
default:
|
||||
nvDebugCheck(false);
|
||||
};
|
||||
|
||||
kdu->initSobel();
|
||||
kdu->normalize();
|
||||
|
||||
kdv = new Kernel2(*kdu);
|
||||
kdv->transpose();
|
||||
|
||||
return ::createNormalMap(img, wm, heightWeights, kdu, kdv);
|
||||
}
|
||||
|
||||
|
||||
/// Create normal map combining multiple sobel filters.
|
||||
FloatImage * nv::createNormalMap(const Image * img, FloatImage::WrapMode wm, Vector4::Arg heightWeights, Vector4::Arg filterWeights)
|
||||
{
|
||||
nvDebugCheck(img != NULL);
|
||||
|
||||
Kernel2 * kdu = NULL;
|
||||
Kernel2 * kdv = NULL;
|
||||
|
||||
kdu = new Kernel2(9);
|
||||
kdu->initBlendedSobel(filterWeights);
|
||||
kdu->normalize();
|
||||
|
||||
kdv = new Kernel2(*kdu);
|
||||
kdv->transpose();
|
||||
|
||||
return ::createNormalMap(img, wm, heightWeights, kdu, kdv);
|
||||
}
|
||||
|
||||
|
||||
FloatImage * nv::createNormalMap(const FloatImage * img, FloatImage::WrapMode wm, Vector4::Arg filterWeights)
|
||||
{
|
||||
nvDebugCheck(img != NULL);
|
||||
|
||||
Kernel2 * kdu = NULL;
|
||||
Kernel2 * kdv = NULL;
|
||||
|
||||
kdu = new Kernel2(9);
|
||||
kdu->initBlendedSobel(filterWeights);
|
||||
kdu->normalize();
|
||||
|
||||
kdv = new Kernel2(*kdu);
|
||||
kdv->transpose();
|
||||
|
||||
return ::createNormalMap(img, wm, kdu, kdv);
|
||||
}
|
||||
|
||||
|
||||
/// Normalize the given image in place.
|
||||
void nv::normalizeNormalMap(FloatImage * img)
|
||||
{
|
||||
nvDebugCheck(img != NULL);
|
||||
|
||||
img->normalize(0);
|
||||
}
|
||||
|
||||
// Copyright NVIDIA Corporation 2007 -- Ignacio Castano <icastano@nvidia.com>
|
||||
//
|
||||
// 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 "NormalMap.h"
|
||||
#include "Filter.h"
|
||||
#include "FloatImage.h"
|
||||
#include "Image.h"
|
||||
|
||||
#include "nvmath/Color.inl"
|
||||
#include "nvmath/Vector.h"
|
||||
|
||||
#include "nvcore/Ptr.h"
|
||||
|
||||
#include <string.h> // memcpy
|
||||
|
||||
|
||||
using namespace nv;
|
||||
|
||||
// Create normal map using the given kernels.
|
||||
static FloatImage * createNormalMap(const Image * img, FloatImage::WrapMode wm, Vector4::Arg heightWeights, const Kernel2 * kdu, const Kernel2 * kdv)
|
||||
{
|
||||
nvDebugCheck(kdu != NULL);
|
||||
nvDebugCheck(kdv != NULL);
|
||||
nvDebugCheck(img != NULL);
|
||||
|
||||
const uint w = img->width();
|
||||
const uint h = img->height();
|
||||
|
||||
AutoPtr<FloatImage> fimage(new FloatImage());
|
||||
fimage->allocate(4, w, h);
|
||||
|
||||
// Compute height and store in alpha channel:
|
||||
float * alphaChannel = fimage->channel(3);
|
||||
for(uint i = 0; i < w * h; i++)
|
||||
{
|
||||
Vector4 color = toVector4(img->pixel(i));
|
||||
alphaChannel[i] = dot(color, heightWeights);
|
||||
}
|
||||
|
||||
float heightScale = 1.0f / 16.0f; // @@ Use a user defined factor.
|
||||
|
||||
for(uint y = 0; y < h; y++)
|
||||
{
|
||||
for(uint x = 0; x < w; x++)
|
||||
{
|
||||
const float du = fimage->applyKernelXY(kdu, x, y, 0, 3, wm);
|
||||
const float dv = fimage->applyKernelXY(kdv, x, y, 0, 3, wm);
|
||||
|
||||
Vector3 n = normalize(Vector3(du, dv, heightScale));
|
||||
|
||||
fimage->pixel(0, x, y, 0) = 0.5f * n.x + 0.5f;
|
||||
fimage->pixel(1, x, y, 0) = 0.5f * n.y + 0.5f;
|
||||
fimage->pixel(2, x, y, 0) = 0.5f * n.z + 0.5f;
|
||||
}
|
||||
}
|
||||
|
||||
return fimage.release();
|
||||
}
|
||||
|
||||
|
||||
// Create normal map using the given kernels.
|
||||
static FloatImage * createNormalMap(const FloatImage * img, FloatImage::WrapMode wm, const Kernel2 * kdu, const Kernel2 * kdv)
|
||||
{
|
||||
nvDebugCheck(kdu != NULL);
|
||||
nvDebugCheck(kdv != NULL);
|
||||
nvDebugCheck(img != NULL);
|
||||
|
||||
#pragma NV_MESSAGE("FIXME: Height scale parameter should go away. It should be a sensible value that produces good results when the heightmap is in the [0, 1] range.")
|
||||
const float heightScale = 1.0f / 16.0f;
|
||||
|
||||
const uint w = img->width();
|
||||
const uint h = img->height();
|
||||
|
||||
AutoPtr<FloatImage> img_out(new FloatImage());
|
||||
img_out->allocate(4, w, h);
|
||||
|
||||
for (uint y = 0; y < h; y++)
|
||||
{
|
||||
for (uint x = 0; x < w; x++)
|
||||
{
|
||||
const float du = img->applyKernelXY(kdu, x, y, 0, 3, wm);
|
||||
const float dv = img->applyKernelXY(kdv, x, y, 0, 3, wm);
|
||||
|
||||
Vector3 n = normalize(Vector3(du, dv, heightScale));
|
||||
|
||||
img_out->pixel(0, x, y, 0) = n.x;
|
||||
img_out->pixel(1, x, y, 0) = n.y;
|
||||
img_out->pixel(2, x, y, 0) = n.z;
|
||||
}
|
||||
}
|
||||
|
||||
// Copy alpha channel.
|
||||
/*for (uint y = 0; y < h; y++)
|
||||
{
|
||||
for (uint x = 0; x < w; x++)
|
||||
{
|
||||
|
||||
img_out->pixel(3, x, y, 0) = img->pixel(3, x, y, 0);
|
||||
}
|
||||
}*/
|
||||
memcpy(img_out->channel(3), img->channel(3), w * h * sizeof(float));
|
||||
|
||||
return img_out.release();
|
||||
}
|
||||
|
||||
|
||||
/// Create normal map using the given filter.
|
||||
FloatImage * nv::createNormalMap(const Image * img, FloatImage::WrapMode wm, Vector4::Arg heightWeights, NormalMapFilter filter /*= Sobel3x3*/)
|
||||
{
|
||||
nvDebugCheck(img != NULL);
|
||||
|
||||
// Init the kernels.
|
||||
Kernel2 * kdu = NULL;
|
||||
Kernel2 * kdv = NULL;
|
||||
|
||||
switch(filter)
|
||||
{
|
||||
case NormalMapFilter_Sobel3x3:
|
||||
kdu = new Kernel2(3);
|
||||
break;
|
||||
case NormalMapFilter_Sobel5x5:
|
||||
kdu = new Kernel2(5);
|
||||
break;
|
||||
case NormalMapFilter_Sobel7x7:
|
||||
kdu = new Kernel2(7);
|
||||
break;
|
||||
case NormalMapFilter_Sobel9x9:
|
||||
kdu = new Kernel2(9);
|
||||
break;
|
||||
default:
|
||||
nvDebugCheck(false);
|
||||
};
|
||||
|
||||
kdu->initSobel();
|
||||
kdu->normalize();
|
||||
|
||||
kdv = new Kernel2(*kdu);
|
||||
kdv->transpose();
|
||||
|
||||
return ::createNormalMap(img, wm, heightWeights, kdu, kdv);
|
||||
}
|
||||
|
||||
|
||||
/// Create normal map combining multiple sobel filters.
|
||||
FloatImage * nv::createNormalMap(const Image * img, FloatImage::WrapMode wm, Vector4::Arg heightWeights, Vector4::Arg filterWeights)
|
||||
{
|
||||
nvDebugCheck(img != NULL);
|
||||
|
||||
Kernel2 * kdu = NULL;
|
||||
Kernel2 * kdv = NULL;
|
||||
|
||||
kdu = new Kernel2(9);
|
||||
kdu->initBlendedSobel(filterWeights);
|
||||
kdu->normalize();
|
||||
|
||||
kdv = new Kernel2(*kdu);
|
||||
kdv->transpose();
|
||||
|
||||
return ::createNormalMap(img, wm, heightWeights, kdu, kdv);
|
||||
}
|
||||
|
||||
|
||||
FloatImage * nv::createNormalMap(const FloatImage * img, FloatImage::WrapMode wm, Vector4::Arg filterWeights)
|
||||
{
|
||||
nvDebugCheck(img != NULL);
|
||||
|
||||
Kernel2 * kdu = NULL;
|
||||
Kernel2 * kdv = NULL;
|
||||
|
||||
kdu = new Kernel2(9);
|
||||
kdu->initBlendedSobel(filterWeights);
|
||||
kdu->normalize();
|
||||
|
||||
kdv = new Kernel2(*kdu);
|
||||
kdv->transpose();
|
||||
|
||||
return ::createNormalMap(img, wm, kdu, kdv);
|
||||
}
|
||||
|
||||
|
||||
/// Normalize the given image in place.
|
||||
void nv::normalizeNormalMap(FloatImage * img)
|
||||
{
|
||||
nvDebugCheck(img != NULL);
|
||||
|
||||
img->normalize(0);
|
||||
}
|
||||
|
||||
|
@ -101,6 +101,48 @@ inline Stream & operator<< (Stream & s, TgaFile & tga)
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// @@ Move to BMP file?
|
||||
|
||||
const int BITMAPFILEHEADER_SIZE = 14;
|
||||
const int BITMAPINFOHEADER_SIZE = 40;
|
||||
const int BM_TYPE = ((unsigned int)'M') << 8 | ((unsigned int)'B');
|
||||
|
||||
// BMP Header.
|
||||
struct BmpFileHeader {
|
||||
uint16 type;
|
||||
uint32 size;
|
||||
uint16 reserved1;
|
||||
uint16 reserved2;
|
||||
uint32 offBits;
|
||||
};
|
||||
|
||||
struct BmpInfoHeader {
|
||||
uint32 size;
|
||||
uint32 width;
|
||||
uint32 height;
|
||||
uint16 planes;
|
||||
uint16 bitCount;
|
||||
uint32 compression;
|
||||
uint32 sizeImage;
|
||||
uint32 xPelsPerMeter;
|
||||
uint32 yPelsPerMeter;
|
||||
uint32 clrUsed;
|
||||
uint32 clrImportant;
|
||||
};
|
||||
|
||||
inline Stream & operator<< (Stream & s, BmpFileHeader & bmp) {
|
||||
return s << bmp.type << bmp.size << bmp.reserved1 << bmp.reserved2 << bmp.offBits;
|
||||
}
|
||||
|
||||
inline Stream & operator<< (Stream & s, BmpInfoHeader & bmp) {
|
||||
s << bmp.size << bmp.width << bmp.height << bmp.planes << bmp.bitCount << bmp.compression << bmp.sizeImage;
|
||||
s << bmp.xPelsPerMeter << bmp.yPelsPerMeter << bmp.clrUsed << bmp.clrImportant;
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
} // nv namespace
|
||||
|
||||
#endif // NV_IMAGE_TGAFILE_H
|
||||
|
Reference in New Issue
Block a user