Imperative api is sort of working now. Fixed various bugs.
This commit is contained in:
@ -56,34 +56,10 @@ ColorBlock::ColorBlock(const Image * img, uint x, uint y)
|
||||
|
||||
void ColorBlock::init(const Image * img, uint x, uint y)
|
||||
{
|
||||
nvDebugCheck(img != NULL);
|
||||
|
||||
const uint bw = min(img->width() - x, 4U);
|
||||
const uint bh = min(img->height() - y, 4U);
|
||||
nvDebugCheck(bw != 0 && bh != 0);
|
||||
|
||||
static const int remainder[] = {
|
||||
0, 0, 0, 0,
|
||||
0, 1, 0, 1,
|
||||
0, 1, 2, 0,
|
||||
0, 1, 2, 3,
|
||||
};
|
||||
|
||||
// Blocks that are smaller than 4x4 are handled by repeating the pixels.
|
||||
// @@ Thats only correct when block size is 1, 2 or 4, but not with 3. :(
|
||||
|
||||
for(uint i = 0; i < 4; i++) {
|
||||
//const int by = i % bh;
|
||||
const int by = remainder[(bh - 1) * 4 + i];
|
||||
for(uint e = 0; e < 4; e++) {
|
||||
//const int bx = e % bw;
|
||||
const int bx = remainder[(bw - 1) * 4 + e];
|
||||
color(e, i) = img->pixel(x + bx, y + by);
|
||||
}
|
||||
}
|
||||
init(img->width(), img->height(), (const uint *)img->pixels(), x, y);
|
||||
}
|
||||
|
||||
void ColorBlock::init(uint w, uint h, uint * data, uint x, uint y)
|
||||
void ColorBlock::init(uint w, uint h, const uint * data, uint x, uint y)
|
||||
{
|
||||
nvDebugCheck(data != NULL);
|
||||
|
||||
@ -93,6 +69,7 @@ void ColorBlock::init(uint w, uint h, uint * data, uint x, uint y)
|
||||
|
||||
// Blocks that are smaller than 4x4 are handled by repeating the pixels.
|
||||
// @@ Thats only correct when block size is 1, 2 or 4, but not with 3. :(
|
||||
// @@ Ideally we should zero the weights of the pixels out of range.
|
||||
|
||||
for (uint i = 0; i < 4; i++)
|
||||
{
|
||||
@ -108,7 +85,7 @@ void ColorBlock::init(uint w, uint h, uint * data, uint x, uint y)
|
||||
}
|
||||
}
|
||||
|
||||
void ColorBlock::init(uint w, uint h, float * data, uint x, uint y)
|
||||
void ColorBlock::init(uint w, uint h, const float * data, uint x, uint y)
|
||||
{
|
||||
nvDebugCheck(data != NULL);
|
||||
|
||||
@ -118,6 +95,9 @@ void ColorBlock::init(uint w, uint h, float * data, uint x, uint y)
|
||||
|
||||
// Blocks that are smaller than 4x4 are handled by repeating the pixels.
|
||||
// @@ Thats only correct when block size is 1, 2 or 4, but not with 3. :(
|
||||
// @@ Ideally we should zero the weights of the pixels out of range.
|
||||
|
||||
uint srcPlane = w * h;
|
||||
|
||||
for (uint i = 0; i < 4; i++)
|
||||
{
|
||||
@ -126,13 +106,13 @@ void ColorBlock::init(uint w, uint h, float * data, uint x, uint y)
|
||||
for (uint e = 0; e < 4; e++)
|
||||
{
|
||||
const uint bx = e % bw;
|
||||
const uint idx = ((y + by) * w + x + bx) * 4;
|
||||
const uint idx = ((y + by) * w + x + bx);
|
||||
|
||||
Color32 & c = color(e, i);
|
||||
c.r = uint8(255 * clamp(data[idx + 0], 0.0f, 1.0f));
|
||||
c.g = uint8(255 * clamp(data[idx + 1], 0.0f, 1.0f));
|
||||
c.b = uint8(255 * clamp(data[idx + 2], 0.0f, 1.0f));
|
||||
c.a = uint8(255 * clamp(data[idx + 3], 0.0f, 1.0f));
|
||||
c.r = uint8(255 * clamp(data[idx + 0 * srcPlane], 0.0f, 1.0f)); // @@ Is this the right way to quantize floats to bytes?
|
||||
c.g = uint8(255 * clamp(data[idx + 1 * srcPlane], 0.0f, 1.0f));
|
||||
c.b = uint8(255 * clamp(data[idx + 2 * srcPlane], 0.0f, 1.0f));
|
||||
c.a = uint8(255 * clamp(data[idx + 3 * srcPlane], 0.0f, 1.0f));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -19,8 +19,8 @@ namespace nv
|
||||
ColorBlock(const Image * img, uint x, uint y);
|
||||
|
||||
void init(const Image * img, uint x, uint y);
|
||||
void init(uint w, uint h, uint * data, uint x, uint y);
|
||||
void init(uint w, uint h, float * data, uint x, uint y);
|
||||
void init(uint w, uint h, const uint * data, uint x, uint y);
|
||||
void init(uint w, uint h, const float * data, uint x, uint y);
|
||||
|
||||
void swizzle(uint x, uint y, uint z, uint w); // 0=r, 1=g, 2=b, 3=a, 4=0xFF, 5=0
|
||||
|
||||
|
@ -206,10 +206,16 @@ void FloatImage::scaleBias(uint base_component, uint num, float scale, float bia
|
||||
}
|
||||
|
||||
/// Clamp the elements of the image.
|
||||
void FloatImage::clamp(float low, float high)
|
||||
void FloatImage::clamp(uint base_component, uint num, float low, float high)
|
||||
{
|
||||
for(uint i = 0; i < m_count; i++) {
|
||||
m_mem[i] = nv::clamp(m_mem[i], low, high);
|
||||
const uint size = m_width * m_height;
|
||||
|
||||
for(uint c = 0; c < num; c++) {
|
||||
float * ptr = this->channel(base_component + c);
|
||||
|
||||
for(uint i = 0; i < size; i++) {
|
||||
ptr[i] = nv::clamp(ptr[i], low, high);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -697,23 +703,24 @@ FloatImage * FloatImage::resize(const Filter & filter, uint w, uint h, WrapMode
|
||||
Array<float> tmp_column(h);
|
||||
tmp_column.resize(h);
|
||||
|
||||
for (uint c = 0; c < m_componentNum; c++)
|
||||
for (uint i = 0; i < m_componentNum; i++)
|
||||
{
|
||||
// Process alpha channel first.
|
||||
uint c;
|
||||
if (i == 0) c = alpha;
|
||||
else if (i > alpha) c = i;
|
||||
else c = i - 1;
|
||||
|
||||
float * tmp_channel = tmp_image->channel(c);
|
||||
|
||||
for (uint y = 0; y < m_height; y++) {
|
||||
this->applyKernelHorizontal(xkernel, y, c, alpha, wm, tmp_channel + y * w);
|
||||
this->applyKernelHorizontal(xkernel, y, c, wm, tmp_channel + y * w);
|
||||
}
|
||||
}
|
||||
|
||||
// Process all channels before applying vertical kernel to make sure alpha has been computed.
|
||||
|
||||
for (uint c = 0; c < m_componentNum; c++)
|
||||
{
|
||||
float * dst_channel = dst_image->channel(c);
|
||||
|
||||
for (uint x = 0; x < w; x++) {
|
||||
tmp_image->applyKernelVertical(ykernel, x, c, alpha, wm, tmp_column.mutableBuffer());
|
||||
tmp_image->applyKernelVertical(ykernel, x, c, wm, tmp_column.mutableBuffer());
|
||||
|
||||
for (uint y = 0; y < h; y++) {
|
||||
dst_channel[y * w + x] = tmp_column[y];
|
||||
@ -938,69 +945,72 @@ void FloatImage::applyKernelHorizontal(const PolyphaseKernel & k, int y, uint c,
|
||||
}
|
||||
}
|
||||
|
||||
// Vertical flip in place.
|
||||
void FloatImage::flip()
|
||||
{
|
||||
const uint w = m_width;
|
||||
const uint h = m_height;
|
||||
const uint h2 = h / 2;
|
||||
|
||||
for (uint c = 0; c < m_componentNum; c++) {
|
||||
for (uint y = 0; y < h2; y++) {
|
||||
float * src = scanline(y, c);
|
||||
float * dst = scanline(h - 1 - y, c);
|
||||
for (uint x = 0; x < w; x++) {
|
||||
swap(src[x], dst[x]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
float FloatImage::alphaTestCoverage(float alphaRef, int alphaChannel) const
|
||||
{
|
||||
const uint w = m_width;
|
||||
const uint h = m_height;
|
||||
|
||||
float coverage = 0.0f;
|
||||
|
||||
for (uint y = 0; y < h; y++) {
|
||||
const float * alpha = scanline(y, alphaChannel);
|
||||
for (uint x = 0; x < w; x++) {
|
||||
if (alpha[x] > alphaRef) coverage += 1.0f; // @@ gt or lt?
|
||||
}
|
||||
}
|
||||
|
||||
return coverage / float(w * h);
|
||||
}
|
||||
|
||||
void FloatImage::scaleAlphaToCoverage(float desiredCoverage, float alphaRef, int alphaChannel)
|
||||
{
|
||||
float minAlphaRef = 0.0f;
|
||||
float maxAlphaRef = 1.0f;
|
||||
float midAlphaRef = 0.5f;
|
||||
|
||||
// Determine desired scale using a binary search. Hardcoded to 8 steps max.
|
||||
for (int i = 0; i < 8; i++) {
|
||||
float currentCoverage = alphaTestCoverage(midAlphaRef, alphaChannel);
|
||||
|
||||
if (currentCoverage > desiredCoverage) {
|
||||
maxAlphaRef = midAlphaRef;
|
||||
}
|
||||
else if (currentCoverage < desiredCoverage) {
|
||||
minAlphaRef = midAlphaRef;
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
|
||||
midAlphaRef = (minAlphaRef + maxAlphaRef) * 0.5f;
|
||||
}
|
||||
|
||||
float alphaScale = alphaRef / midAlphaRef;
|
||||
|
||||
// Scale alpha channel.
|
||||
scaleBias(alphaChannel, 1, alphaScale, 0.0f);
|
||||
// Vertical flip in place.
|
||||
void FloatImage::flip()
|
||||
{
|
||||
const uint w = m_width;
|
||||
const uint h = m_height;
|
||||
const uint h2 = h / 2;
|
||||
|
||||
for (uint c = 0; c < m_componentNum; c++) {
|
||||
for (uint y = 0; y < h2; y++) {
|
||||
float * src = scanline(y, c);
|
||||
float * dst = scanline(h - 1 - y, c);
|
||||
for (uint x = 0; x < w; x++) {
|
||||
swap(src[x], dst[x]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
float FloatImage::alphaTestCoverage(float alphaRef, int alphaChannel) const
|
||||
{
|
||||
const uint w = m_width;
|
||||
const uint h = m_height;
|
||||
|
||||
float coverage = 0.0f;
|
||||
|
||||
for (uint y = 0; y < h; y++) {
|
||||
const float * alpha = scanline(y, alphaChannel);
|
||||
for (uint x = 0; x < w; x++) {
|
||||
if (alpha[x] > alphaRef) coverage += 1.0f; // @@ gt or lt?
|
||||
}
|
||||
}
|
||||
|
||||
return coverage / float(w * h);
|
||||
}
|
||||
|
||||
void FloatImage::scaleAlphaToCoverage(float desiredCoverage, float alphaRef, int alphaChannel)
|
||||
{
|
||||
float minAlphaRef = 0.0f;
|
||||
float maxAlphaRef = 1.0f;
|
||||
float midAlphaRef = 0.5f;
|
||||
|
||||
// Determine desired scale using a binary search. Hardcoded to 8 steps max.
|
||||
for (int i = 0; i < 10; i++) {
|
||||
float currentCoverage = alphaTestCoverage(midAlphaRef, alphaChannel);
|
||||
|
||||
if (currentCoverage > desiredCoverage) {
|
||||
minAlphaRef = midAlphaRef;
|
||||
}
|
||||
else if (currentCoverage < desiredCoverage) {
|
||||
maxAlphaRef = midAlphaRef;
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
|
||||
midAlphaRef = (minAlphaRef + maxAlphaRef) * 0.5f;
|
||||
}
|
||||
|
||||
float alphaScale = alphaRef / midAlphaRef;
|
||||
|
||||
// Scale alpha channel.
|
||||
scaleBias(alphaChannel, 1, alphaScale, 0.0f);
|
||||
clamp(alphaChannel, 1, 0.0f, 1.0f);
|
||||
|
||||
//float newCoverage = alphaTestCoverage(alphaRef, alphaChannel);
|
||||
}
|
||||
|
||||
FloatImage* FloatImage::clone() const
|
||||
|
@ -60,8 +60,7 @@ namespace nv
|
||||
NVIMAGE_API void expandNormals(uint base_component);
|
||||
NVIMAGE_API void scaleBias(uint base_component, uint num, float scale, float add);
|
||||
|
||||
//NVIMAGE_API void clamp(uint base_component, uint num);
|
||||
NVIMAGE_API void clamp(float low, float high);
|
||||
NVIMAGE_API void clamp(uint base_component, uint num, float low, float high);
|
||||
|
||||
NVIMAGE_API void toLinear(uint base_component, uint num, float gamma = 2.2f);
|
||||
NVIMAGE_API void toGamma(uint base_component, uint num, float gamma = 2.2f);
|
||||
|
@ -203,10 +203,15 @@ FloatImage * nv::ImageIO::loadFloat(const char * fileName, Stream & s)
|
||||
|
||||
const char * extension = Path::extension(fileName);
|
||||
|
||||
FloatImage * floatImage = NULL;
|
||||
|
||||
const uint spos = s.tell(); // Save stream position.
|
||||
|
||||
// Try to load as a floating point image.
|
||||
#if defined(HAVE_FREEIMAGE)
|
||||
FREE_IMAGE_FORMAT fif = FreeImage_GetFIFFromFilename(fileName);
|
||||
if (fif != FIF_UNKNOWN && FreeImage_FIFSupportsReading(fif)) {
|
||||
return loadFloatFreeImage(fif, s);
|
||||
floatImage = loadFloatFreeImage(fif, s);
|
||||
}
|
||||
#else // defined(HAVE_FREEIMAGE)
|
||||
#pragma message(NV_FILE_LINE "TODO: Load TIFF and EXR files from stream.")
|
||||
@ -222,7 +227,17 @@ FloatImage * nv::ImageIO::loadFloat(const char * fileName, Stream & s)
|
||||
#endif
|
||||
#endif // defined(HAVE_FREEIMAGE)
|
||||
|
||||
return NULL;
|
||||
// Try to load as an RGBA8 image and convert to float.
|
||||
if (floatImage == NULL) {
|
||||
s.seek(spos); // Restore stream position.
|
||||
|
||||
AutoPtr<Image> img = load(fileName, s);
|
||||
if (img != NULL) {
|
||||
floatImage = new FloatImage(img.ptr());
|
||||
}
|
||||
}
|
||||
|
||||
return floatImage;
|
||||
}
|
||||
|
||||
bool nv::ImageIO::saveFloat(const char * fileName, Stream & s, const FloatImage * fimage, uint baseComponent, uint componentCount)
|
||||
@ -357,16 +372,7 @@ Image * nv::ImageIO::loadFreeImage(FREE_IMAGE_FORMAT fif, Stream & s)
|
||||
const int w = FreeImage_GetWidth(bitmap);
|
||||
const int h = FreeImage_GetHeight(bitmap);
|
||||
|
||||
if (FreeImage_GetImageType(bitmap) == FIT_BITMAP)
|
||||
{
|
||||
if (FreeImage_GetBPP(bitmap) != 32)
|
||||
{
|
||||
FIBITMAP * tmp = FreeImage_ConvertTo32Bits(bitmap);
|
||||
FreeImage_Unload(bitmap);
|
||||
bitmap = tmp;
|
||||
}
|
||||
}
|
||||
else
|
||||
if (FreeImage_GetImageType(bitmap) != FIT_BITMAP)
|
||||
{
|
||||
// @@ Use tone mapping?
|
||||
FIBITMAP * tmp = FreeImage_ConvertToType(bitmap, FIT_BITMAP, true);
|
||||
@ -374,6 +380,14 @@ Image * nv::ImageIO::loadFreeImage(FREE_IMAGE_FORMAT fif, Stream & s)
|
||||
bitmap = tmp;
|
||||
}
|
||||
|
||||
nvDebugCheck(FreeImage_GetImageType(bitmap) == FIT_BITMAP);
|
||||
if (FreeImage_GetBPP(bitmap) != 32)
|
||||
{
|
||||
FIBITMAP * tmp = FreeImage_ConvertTo32Bits(bitmap);
|
||||
FreeImage_Unload(bitmap);
|
||||
bitmap = tmp;
|
||||
}
|
||||
|
||||
|
||||
Image * image = new Image();
|
||||
image->allocate(w, h);
|
||||
|
Reference in New Issue
Block a user