Merge changes from the witness.
This commit is contained in:
@ -28,6 +28,7 @@
|
||||
#include "nvmath/Matrix.inl"
|
||||
#include "nvmath/Color.h"
|
||||
#include "nvmath/Half.h"
|
||||
#include "nvmath/ftoi.h"
|
||||
|
||||
#include "nvimage/Filter.h"
|
||||
#include "nvimage/ImageIO.h"
|
||||
@ -78,13 +79,13 @@ namespace
|
||||
else if (format == Format_DXT3) {
|
||||
return 16;
|
||||
}
|
||||
else if (format == Format_DXT5 || format == Format_DXT5n) {
|
||||
else if (format == Format_DXT5 || format == Format_DXT5n || format == Format_BC3_RGBM) {
|
||||
return 16;
|
||||
}
|
||||
else if (format == Format_BC4) {
|
||||
return 8;
|
||||
}
|
||||
else if (format == Format_BC5) {
|
||||
else if (format == Format_BC5 || format == Format_BC5_Luma) {
|
||||
return 16;
|
||||
}
|
||||
else if (format == Format_CTX1) {
|
||||
@ -347,13 +348,13 @@ int Surface::countMipmaps(int min_size) const
|
||||
return ::countMipmapsWithMinSize(m->image->width(), m->image->height(), 1, min_size);
|
||||
}
|
||||
|
||||
float Surface::alphaTestCoverage(float alphaRef/*= 0.5*/) const
|
||||
float Surface::alphaTestCoverage(float alphaRef/*= 0.5*/, int alpha_channel/*=3*/) const
|
||||
{
|
||||
if (m->image == NULL) return 0.0f;
|
||||
|
||||
alphaRef = nv::clamp(alphaRef, 1.0f/256, 255.0f/256);
|
||||
|
||||
return m->image->alphaTestCoverage(alphaRef, 3);
|
||||
return m->image->alphaTestCoverage(alphaRef, alpha_channel);
|
||||
}
|
||||
|
||||
float Surface::average(int channel, int alpha_channel/*= -1*/, float gamma /*= 2.2f*/) const
|
||||
@ -419,7 +420,7 @@ void Surface::histogram(int channel, float rangeMin, float rangeMax, int binCoun
|
||||
const uint count = m->image->pixelCount();
|
||||
for (uint i = 0; i < count; i++) {
|
||||
float f = c[i] * scale + bias;
|
||||
int idx = ifloor(f);
|
||||
int idx = ftoi_floor(f);
|
||||
if (idx < 0) idx = 0;
|
||||
if (idx > binCount-1) idx = binCount-1;
|
||||
binPtr[idx]++;
|
||||
@ -434,18 +435,17 @@ void Surface::range(int channel, float * rangeMin, float * rangeMax, int alpha_c
|
||||
|
||||
if (alpha_channel == -1) { // no alpha channel; just like the original range function
|
||||
|
||||
if (m->image != NULL)
|
||||
{
|
||||
float * c = img->channel(channel);
|
||||
if (m->image != NULL) {
|
||||
float * c = img->channel(channel);
|
||||
|
||||
const uint count = img->pixelCount();
|
||||
for (uint p = 0; p < count; p++) {
|
||||
float f = c[p];
|
||||
if (f < range.x) range.x = f;
|
||||
if (f > range.y) range.y = f;
|
||||
const uint count = img->pixelCount();
|
||||
for (uint p = 0; p < count; p++) {
|
||||
float f = c[p];
|
||||
if (f < range.x) range.x = f;
|
||||
if (f > range.y) range.y = f;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else { // use alpha test to ignore some pixels
|
||||
//note, it's quite possible to get FLT_MAX,-FLT_MAX back if all pixels fail the test
|
||||
|
||||
@ -623,6 +623,23 @@ bool Surface::setImage(nvtt::InputFormat format, int w, int h, int d, const void
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (format == InputFormat_R_32F)
|
||||
{
|
||||
const float * src = (const float *)data;
|
||||
|
||||
TRY {
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
rdst[i] = src[i];
|
||||
gdst[i] = 0;
|
||||
bdst[i] = 0;
|
||||
adst[i] = 0;
|
||||
}
|
||||
}
|
||||
CATCH {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -695,6 +712,20 @@ bool Surface::setImage(InputFormat format, int w, int h, int d, const void * r,
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (format == InputFormat_R_32F)
|
||||
{
|
||||
const float * rsrc = (const float *)r;
|
||||
|
||||
TRY {
|
||||
memcpy(rdst, rsrc, count * sizeof(float));
|
||||
memset(gdst, 0, count * sizeof(float));
|
||||
memset(bdst, 0, count * sizeof(float));
|
||||
memset(adst, 0, count * sizeof(float));
|
||||
}
|
||||
CATCH {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -703,12 +734,12 @@ bool Surface::setImage(InputFormat format, int w, int h, int d, const void * r,
|
||||
bool Surface::setImage2D(Format format, Decoder decoder, int w, int h, const void * data)
|
||||
{
|
||||
if (format != nvtt::Format_BC1 &&
|
||||
format != nvtt::Format_BC2 &&
|
||||
format != nvtt::Format_BC3 &&
|
||||
format != nvtt::Format_BC4 &&
|
||||
format != nvtt::Format_BC5 &&
|
||||
format != nvtt::Format_BC6 &&
|
||||
format != nvtt::Format_BC7)
|
||||
format != nvtt::Format_BC2 &&
|
||||
format != nvtt::Format_BC3 &&
|
||||
format != nvtt::Format_BC4 &&
|
||||
format != nvtt::Format_BC5 &&
|
||||
format != nvtt::Format_BC6 &&
|
||||
format != nvtt::Format_BC7)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@ -1466,7 +1497,7 @@ void Surface::fill(float red, float green, float blue, float alpha)
|
||||
}
|
||||
|
||||
|
||||
void Surface::scaleAlphaToCoverage(float coverage, float alphaRef/*= 0.5f*/)
|
||||
void Surface::scaleAlphaToCoverage(float coverage, float alphaRef/*= 0.5f*/, int alpha_channel/*= 3*/)
|
||||
{
|
||||
if (isNull()) return;
|
||||
|
||||
@ -1474,7 +1505,7 @@ void Surface::scaleAlphaToCoverage(float coverage, float alphaRef/*= 0.5f*/)
|
||||
|
||||
alphaRef = nv::clamp(alphaRef, 1.0f/256, 255.0f/256);
|
||||
|
||||
m->image->scaleAlphaToCoverage(coverage, alphaRef, 3);
|
||||
m->image->scaleAlphaToCoverage(coverage, alphaRef, alpha_channel);
|
||||
}
|
||||
|
||||
/*bool Surface::normalizeRange(float * rangeMin, float * rangeMax)
|
||||
@ -1507,7 +1538,7 @@ void Surface::scaleAlphaToCoverage(float coverage, float alphaRef/*= 0.5f*/)
|
||||
|
||||
// Ideally you should compress/quantize the RGB and M portions independently.
|
||||
// Once you have M quantized, you would compute the corresponding RGB and quantize that.
|
||||
void Surface::toRGBM(float range/*= 1*/, float threshold/*= 0.0f*/)
|
||||
void Surface::toRGBM(float range/*= 1*/, float threshold/*= 0.25*/)
|
||||
{
|
||||
if (isNull()) return;
|
||||
|
||||
@ -1523,60 +1554,71 @@ void Surface::toRGBM(float range/*= 1*/, float threshold/*= 0.0f*/)
|
||||
|
||||
const uint count = img->pixelCount();
|
||||
for (uint i = 0; i < count; i++) {
|
||||
float R = r[i];
|
||||
float G = g[i];
|
||||
float B = b[i];
|
||||
#if 1
|
||||
float M = nv::clamp(max(max(R, G), max(B, threshold)), 0.0f, range);
|
||||
float R = nv::clamp(r[i], 0.0f, 1.0f);
|
||||
float G = nv::clamp(g[i], 0.0f, 1.0f);
|
||||
float B = nv::clamp(b[i], 0.0f, 1.0f);
|
||||
|
||||
r[i] = nv::clamp(R / M, 0.0f, 1.0f);
|
||||
g[i] = nv::clamp(G / M, 0.0f, 1.0f);
|
||||
b[i] = nv::clamp(B / M, 0.0f, 1.0f);
|
||||
#if 0
|
||||
// Baseline, no compression:
|
||||
r[i] = R;
|
||||
g[i] = G;
|
||||
b[i] = B;
|
||||
a[i] = 1;
|
||||
|
||||
a[i] = (M - threshold) / (range - threshold);
|
||||
#elif 0
|
||||
float M = max(max(R, G), max(B, threshold));
|
||||
|
||||
r[i] = R / M;
|
||||
g[i] = G / M;
|
||||
b[i] = B / M;
|
||||
|
||||
a[i] = (M - threshold) / (1 - threshold);
|
||||
|
||||
#else
|
||||
|
||||
// The optimal compressor theoretically produces the best results, but unfortunately introduces
|
||||
// severe interpolation errors!
|
||||
// The optimal compressor produces the best results, but can introduce interpolation errors!
|
||||
float bestM;
|
||||
float bestError = FLT_MAX;
|
||||
|
||||
int minM = iround(min(R, G, B) * 255.0f);
|
||||
float M = max(max(R, G), max(B, threshold));
|
||||
int iM = ftoi_ceil((M - threshold) / (1 - threshold) * 255.0f);
|
||||
|
||||
for (int m = minM; m < 256; m++) {
|
||||
//for (int m = 0; m < 256; m++) { // If we use the entire search space, interpolation errors are very likely to occur.
|
||||
for (int m = max(iM-16, 0); m < min(iM+16, 256); m++) { // If we constrain the search space, these errors disappear.
|
||||
float fm = float(m) / 255.0f;
|
||||
|
||||
// Decode M
|
||||
float M = fm * (1 - threshold) + threshold;
|
||||
|
||||
// Encode.
|
||||
int ir = iround(255.0f * nv::clamp(R / fm, 0.0f, 1.0f));
|
||||
int ig = iround(255.0f * nv::clamp(G / fm, 0.0f, 1.0f));
|
||||
int ib = iround(255.0f * nv::clamp(B / fm, 0.0f, 1.0f));
|
||||
int ir = ftoi_round(255.0f * nv::saturate(R / M));
|
||||
int ig = ftoi_round(255.0f * nv::saturate(G / M));
|
||||
int ib = ftoi_round(255.0f * nv::saturate(B / M));
|
||||
|
||||
// Decode.
|
||||
float fr = (float(ir) / 255.0f) * fm;
|
||||
float fg = (float(ig) / 255.0f) * fm;
|
||||
float fb = (float(ib) / 255.0f) * fm;
|
||||
float fr = (float(ir) / 255.0f) * M;
|
||||
float fg = (float(ig) / 255.0f) * M;
|
||||
float fb = (float(ib) / 255.0f) * M;
|
||||
|
||||
// Measure error.
|
||||
float error = square(R-fr) + square(G-fg) + square(B-fb);
|
||||
|
||||
if (error < bestError) {
|
||||
bestError = error;
|
||||
bestM = fm;
|
||||
bestM = M;
|
||||
}
|
||||
}
|
||||
|
||||
M = bestM;
|
||||
r[i] = nv::clamp(R / M, 0.0f, 1.0f);
|
||||
g[i] = nv::clamp(G / M, 0.0f, 1.0f);
|
||||
b[i] = nv::clamp(B / M, 0.0f, 1.0f);
|
||||
a[i] = M;
|
||||
r[i] = nv::saturate(R / M);
|
||||
g[i] = nv::saturate(G / M);
|
||||
b[i] = nv::saturate(B / M);
|
||||
a[i] = (M - threshold) / (1 - threshold);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Surface::fromRGBM(float range/*= 1*/, float threshold/*= 0.0*/)
|
||||
// @@ IC: Dubious merge. Review!
|
||||
void Surface::fromRGBM(float range/*= 1*/, float threshold/*= 0.25*/)
|
||||
{
|
||||
if (isNull()) return;
|
||||
|
||||
@ -1798,7 +1840,7 @@ void Surface::toRGBE(int mantissaBits, int exponentBits)
|
||||
double denom = pow(2.0, double(E - exponentBias - mantissaBits));
|
||||
|
||||
// Refine exponent:
|
||||
int m = iround(float(M / denom));
|
||||
int m = ftoi_round(float(M / denom));
|
||||
nvDebugCheck(m <= (1 << mantissaBits));
|
||||
|
||||
if (m == (1 << mantissaBits)) {
|
||||
@ -1866,10 +1908,10 @@ void Surface::fromRGBE(int mantissaBits, int exponentBits)
|
||||
const uint count = img->pixelCount();
|
||||
for (uint i = 0; i < count; i++) {
|
||||
// Expand normalized float to to 9995
|
||||
int R = iround(r[i] * ((1 << mantissaBits) - 1));
|
||||
int G = iround(g[i] * ((1 << mantissaBits) - 1));
|
||||
int B = iround(b[i] * ((1 << mantissaBits) - 1));
|
||||
int E = iround(a[i] * ((1 << exponentBits) - 1));
|
||||
int R = ftoi_round(r[i] * ((1 << mantissaBits) - 1));
|
||||
int G = ftoi_round(g[i] * ((1 << mantissaBits) - 1));
|
||||
int B = ftoi_round(b[i] * ((1 << mantissaBits) - 1));
|
||||
int E = ftoi_round(a[i] * ((1 << exponentBits) - 1));
|
||||
|
||||
//float scale = ldexpf(1.0f, E - exponentBias - mantissaBits);
|
||||
float scale = powf(2, float(E - exponentBias - mantissaBits));
|
||||
@ -2741,8 +2783,8 @@ bool Surface::copy(const Surface & srcImage, int xsrc, int ysrc, int zsrc, int x
|
||||
FloatImage * dst = m->image;
|
||||
const FloatImage * src = srcImage.m->image;
|
||||
|
||||
if (toU32(xsrc + xsize) > src->width() || toU32(ysrc + ysize) > src->height() || toU32(zsrc + zsize) > src->depth()) return false;
|
||||
if (toU32(xdst + xsize) > dst->width() || toU32(ydst + ysize) > dst->height() || toU32(zdst + zsize) > dst->depth()) return false;
|
||||
if (U32(xsrc + xsize) > src->width() || U32(ysrc + ysize) > src->height() || U32(zsrc + zsize) > src->depth()) return false;
|
||||
if (U32(xdst + xsize) > dst->width() || U32(ydst + ysize) > dst->height() || U32(zdst + zsize) > dst->depth()) return false;
|
||||
|
||||
detach();
|
||||
|
||||
@ -2765,6 +2807,65 @@ bool Surface::copy(const Surface & srcImage, int xsrc, int ysrc, int zsrc, int x
|
||||
}
|
||||
|
||||
|
||||
// Draw colored border around atlas elements.
|
||||
void Surface::setAtlasBorder(int aw, int ah, float r, float g, float b, float a)
|
||||
{
|
||||
if (isNull()) return;
|
||||
if (aw <= 0) return;
|
||||
if (ah <= 0) return;
|
||||
|
||||
detach();
|
||||
|
||||
FloatImage * img = m->image;
|
||||
const uint w = img->width();
|
||||
const uint h = img->height();
|
||||
const uint d = img->depth();
|
||||
|
||||
// @@ Ideally the reminder of these divisions should be 0.
|
||||
uint tile_height = h / ah;
|
||||
uint tile_width = w / aw;
|
||||
|
||||
// Note that this renders two consecutive lines between tiles. In theory we could just have one, but this way I think we have better rotation invariance.
|
||||
|
||||
for (uint z = 0; z < d; z++)
|
||||
{
|
||||
// Horizontal lines:
|
||||
for (uint i = 0, y = 0; i < uint(ah); i++, y += tile_height)
|
||||
{
|
||||
for (uint x = 0; x < w; x++)
|
||||
{
|
||||
img->pixel(0, x, y, z) = r;
|
||||
img->pixel(1, x, y, z) = g;
|
||||
img->pixel(2, x, y, z) = b;
|
||||
img->pixel(3, x, y, z) = a;
|
||||
|
||||
img->pixel(0, x, y + tile_height - 1, z) = r;
|
||||
img->pixel(1, x, y + tile_height - 1, z) = g;
|
||||
img->pixel(2, x, y + tile_height - 1, z) = b;
|
||||
img->pixel(3, x, y + tile_height - 1, z) = a;
|
||||
}
|
||||
}
|
||||
|
||||
// Vertical lines:
|
||||
for (uint i = 0, x = 0; i < uint(ah); i++, x += tile_width)
|
||||
{
|
||||
for (uint y = 0; y < h; y++)
|
||||
{
|
||||
img->pixel(0, x, y, z) = r;
|
||||
img->pixel(1, x, y, z) = g;
|
||||
img->pixel(2, x, y, z) = b;
|
||||
img->pixel(3, x, y, z) = a;
|
||||
|
||||
img->pixel(0, x + tile_width - 1, y, z) = r;
|
||||
img->pixel(1, x + tile_width - 1, y, z) = g;
|
||||
img->pixel(2, x + tile_width - 1, y, z) = b;
|
||||
img->pixel(3, x + tile_width - 1, y, z) = a;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
float nvtt::rmsError(const Surface & reference, const Surface & image)
|
||||
{
|
||||
@ -2839,5 +2940,24 @@ Surface nvtt::diff(const Surface & reference, const Surface & image, float scale
|
||||
return diffImage;
|
||||
}
|
||||
|
||||
float nvtt::rmsToneMappedError(const Surface & reference, const Surface & img, float exposure)
|
||||
{
|
||||
// @@ We could do this in the rms function without having to create image copies.
|
||||
Surface r = reference;
|
||||
Surface i = img;
|
||||
|
||||
// @@ Ideally we should use our Reindhart operator. Add Reindhart_L & Reindhart_M ?
|
||||
|
||||
float scale = 1.0f / exposure;
|
||||
|
||||
r.scaleBias(0, scale, 0); r.scaleBias(1, scale, 0); r.scaleBias(2, scale, 0);
|
||||
r.toneMap(ToneMapper_Reindhart, NULL);
|
||||
r.toSrgb();
|
||||
|
||||
i.scaleBias(0, scale, 0); i.scaleBias(1, scale, 0); i.scaleBias(2, scale, 0);
|
||||
i.toneMap(ToneMapper_Reindhart, NULL);
|
||||
i.toSrgb();
|
||||
|
||||
return nv::rmsColorError(r.m->image, i.m->image, reference.alphaMode() == nvtt::AlphaMode_Transparency);
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user