Merge changes from The Witness.

This commit is contained in:
Ignacio
2018-02-05 18:55:07 -08:00
parent 2075d740c9
commit 9489aed825
88 changed files with 8924 additions and 5025 deletions

View File

@ -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));
}
}
}

View File

@ -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.

View File

@ -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})

View File

@ -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;
}
}

View File

@ -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'),
};

View File

@ -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));
}

View File

@ -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

View File

@ -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;

View File

@ -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;

View File

@ -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);

View File

@ -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);
}

View File

@ -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

View File

@ -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);*/
/*

View 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);
}

View File

@ -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