More cleanup. Remove files that are not strictly required.
This commit is contained in:
@ -14,15 +14,12 @@ SET(IMAGE_SRCS
|
||||
ColorBlock.cpp
|
||||
BlockDXT.h
|
||||
BlockDXT.cpp
|
||||
HoleFilling.h
|
||||
HoleFilling.cpp
|
||||
DirectDrawSurface.h
|
||||
DirectDrawSurface.cpp
|
||||
Quantize.h
|
||||
Quantize.cpp
|
||||
NormalMap.h
|
||||
NormalMap.cpp
|
||||
NormalMipmap.h
|
||||
PsdFile.h
|
||||
TgaFile.h
|
||||
ColorSpace.h
|
||||
|
@ -1,122 +0,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 <nvcore/Ptr.h>
|
||||
|
||||
#include <nvmath/Color.h>
|
||||
|
||||
#include <nvimage/NormalMap.h>
|
||||
#include <nvimage/Filter.h>
|
||||
#include <nvimage/FloatImage.h>
|
||||
#include <nvimage/Image.h>
|
||||
|
||||
using namespace nv;
|
||||
|
||||
|
||||
static float processPixel(const FloatImage * img, uint x, uint y)
|
||||
{
|
||||
nvDebugCheck(img != NULL);
|
||||
|
||||
const uint w = img->width();
|
||||
const uint h = img->height();
|
||||
|
||||
float d = img->pixel(x, y, 0);
|
||||
|
||||
float fx0 = (float) x / w;
|
||||
float fy0 = (float) y / h;
|
||||
|
||||
float best_ratio = INF;
|
||||
uint best_x = w;
|
||||
uint best_y = h;
|
||||
|
||||
for (uint yy = 0; yy < h; yy++)
|
||||
{
|
||||
for (uint xx = 0; xx < w; xx++)
|
||||
{
|
||||
float ch = d - img->pixel(xx, yy, 0);
|
||||
|
||||
if (ch > 0)
|
||||
{
|
||||
float dx = float(xx - x);
|
||||
float dy = float(yy - y);
|
||||
|
||||
float ratio = (dx * dx + dy * dy) / ch;
|
||||
|
||||
if (ratio < best_ratio)
|
||||
{
|
||||
best_x = xx;
|
||||
best_y = yy;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (best_x != w)
|
||||
{
|
||||
nvDebugCheck(best_y !=h);
|
||||
|
||||
float dx = float(best_x - x) / w;
|
||||
float dy = float(best_y - y) / h;
|
||||
|
||||
float cw = sqrtf(dx*dx + dy*dy);
|
||||
float ch = d - img->pixel(xx, yy, 0);
|
||||
|
||||
return min(1, sqrtf(cw / ch));
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
// Create cone map using the given kernels.
|
||||
FloatImage * createConeMap(const Image * img, Vector4::Arg heightWeights)
|
||||
{
|
||||
nvCheck(img != NULL);
|
||||
|
||||
const uint w = img->width();
|
||||
const uint h = img->height();
|
||||
|
||||
AutoPtr<FloatImage> fimage(new FloatImage());
|
||||
//fimage->allocate(2, w, h);
|
||||
fimage->allocate(4, w, h);
|
||||
|
||||
// Compute height and store in red channel:
|
||||
float * heightChannel = fimage->channel(0);
|
||||
for(uint i = 0; i < w*h; i++)
|
||||
{
|
||||
Vector4 color = toVector4(img->pixel(i));
|
||||
heightChannel[i] = dot(color, heightWeights);
|
||||
}
|
||||
|
||||
// Compute cones:
|
||||
for(uint y = 0; y < h; y++)
|
||||
{
|
||||
for(uint x = 0; x < w; x++)
|
||||
{
|
||||
processPixel(fimage, x, y);
|
||||
}
|
||||
}
|
||||
|
||||
return fimage.release();
|
||||
}
|
||||
|
@ -1,39 +0,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.
|
||||
|
||||
#ifndef NV_IMAGE_CONEMAP_H
|
||||
#define NV_IMAGE_CONEMAP_H
|
||||
|
||||
#include <nvmath/Vector.h>
|
||||
#include <nvimage/nvimage.h>
|
||||
|
||||
namespace nv
|
||||
{
|
||||
class Image;
|
||||
class FloatImage;
|
||||
|
||||
FloatImage * createConeMap(const Image * img, Vector4::Arg heightWeights);
|
||||
|
||||
} // nv namespace
|
||||
|
||||
#endif // NV_IMAGE_CONEMAP_H
|
@ -1,849 +0,0 @@
|
||||
// This code is in the public domain -- castanyo@yahoo.es
|
||||
|
||||
#include <nvimage/HoleFilling.h>
|
||||
#include <nvimage/FloatImage.h>
|
||||
|
||||
#include <nvmath/nvmath.h>
|
||||
|
||||
#include <nvcore/Containers.h>
|
||||
#include <nvcore/Ptr.h>
|
||||
|
||||
|
||||
using namespace nv;
|
||||
|
||||
float BitMap::sampleLinearClamp(float x, float y) const
|
||||
{
|
||||
const float fracX = x - floor(x);
|
||||
const float fracY = y - floor(y);
|
||||
|
||||
const uint ix0 = ::clamp(uint(floor(x)), 0U, m_width-1);
|
||||
const uint iy0 = ::clamp(uint(floor(y)), 0U, m_height-1);
|
||||
const uint ix1 = ::clamp(uint(floor(x))+1, 0U, m_width-1);
|
||||
const uint iy1 = ::clamp(uint(floor(y))+1, 0U, m_height-1);
|
||||
|
||||
float f1 = bitAt(ix0, iy0);
|
||||
float f2 = bitAt(ix1, iy0);
|
||||
float f3 = bitAt(ix0, iy1);
|
||||
float f4 = bitAt(ix1, iy1);
|
||||
|
||||
float i1 = lerp(f1, f2, fracX);
|
||||
float i2 = lerp(f3, f4, fracX);
|
||||
|
||||
return lerp(i1, i2, fracY);
|
||||
}
|
||||
|
||||
|
||||
// This is a variation of Sapiro's inpainting method.
|
||||
void nv::fillExtrapolate(int passCount, FloatImage * img, BitMap * bmap)
|
||||
{
|
||||
nvCheck(img != NULL);
|
||||
nvCheck(bmap != NULL);
|
||||
|
||||
const int w = img->width();
|
||||
const int h = img->height();
|
||||
const int count = img->componentNum();
|
||||
|
||||
nvCheck(bmap->width() == uint(w));
|
||||
nvCheck(bmap->height() == uint(h));
|
||||
|
||||
AutoPtr<BitMap> newbmap(new BitMap(w, h));
|
||||
newbmap->clearAll();
|
||||
|
||||
for(int p = 0; p < passCount; p++)
|
||||
{
|
||||
for(int c = 0; c < count; c++)
|
||||
{
|
||||
float * channel = img->channel(c);
|
||||
|
||||
for(int y = 0; y < h; y++) {
|
||||
for(int x = 0; x < w; x++) {
|
||||
|
||||
if (bmap->bitAt(x, y)) {
|
||||
// Not a hole.
|
||||
newbmap->setBitAt(x, y);
|
||||
continue;
|
||||
}
|
||||
|
||||
const bool west = bmap->bitAt(img->indexClamp(x-1, y));
|
||||
const bool east = bmap->bitAt(img->indexClamp(x+1, y));
|
||||
const bool north = bmap->bitAt(img->indexClamp(x, y-1));
|
||||
const bool south = bmap->bitAt(img->indexClamp(x, y+1));
|
||||
const bool northwest = bmap->bitAt(img->indexClamp(x-1, y-1));
|
||||
const bool northeast = bmap->bitAt(img->indexClamp(x+1, y-1));
|
||||
const bool southwest = bmap->bitAt(img->indexClamp(x-1, y+1));
|
||||
const bool southeast = bmap->bitAt(img->indexClamp(x+1, y+1));
|
||||
|
||||
int num = west + east + north + south + northwest + northeast + southwest + southeast;
|
||||
|
||||
if (num != 0) {
|
||||
|
||||
float average = 0.0f;
|
||||
if (num == 3 && west && northwest && southwest) {
|
||||
average = channel[img->indexClamp(x-1, y)];
|
||||
}
|
||||
else if (num == 3 && east && northeast && southeast) {
|
||||
average = channel[img->indexClamp(x+1, y)];
|
||||
}
|
||||
else if (num == 3 && north && northwest && northeast) {
|
||||
average = channel[img->indexClamp(x, y-1)];
|
||||
}
|
||||
else if (num == 3 && south && southwest && southeast) {
|
||||
average = channel[img->indexClamp(x, y+1)];
|
||||
}
|
||||
else {
|
||||
float total = 0.0f;
|
||||
if (west) { average += 1 * channel[img->indexClamp(x-1, y)]; total += 1; }
|
||||
if (east) { average += 1 * channel[img->indexClamp(x+1, y)]; total += 1; }
|
||||
if (north) { average += 1 * channel[img->indexClamp(x, y-1)]; total += 1; }
|
||||
if (south) { average += 1 * channel[img->indexClamp(x, y+1)]; total += 1; }
|
||||
|
||||
if (northwest) { average += channel[img->indexClamp(x-1, y-1)]; ++total; }
|
||||
if (northeast) { average += channel[img->indexClamp(x+1, y-1)]; ++total; }
|
||||
if (southwest) { average += channel[img->indexClamp(x-1, y+1)]; ++total; }
|
||||
if (southeast) { average += channel[img->indexClamp(x+1, y+1)]; ++total; }
|
||||
|
||||
average /= total;
|
||||
}
|
||||
|
||||
channel[img->indexClamp(x, y)] = average;
|
||||
newbmap->setBitAt(x, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Update the bit mask.
|
||||
swap(*newbmap, *bmap);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
struct Neighbor {
|
||||
uint16 x;
|
||||
uint16 y;
|
||||
uint32 d;
|
||||
};
|
||||
|
||||
// Compute euclidean squared distance.
|
||||
static uint dist( uint16 ax, uint16 ay, uint16 bx, uint16 by ) {
|
||||
int dx = bx - ax;
|
||||
int dy = by - ay;
|
||||
return uint(dx*dx + dy*dy);
|
||||
}
|
||||
|
||||
// Check neighbour, this is the core of the EDT algorithm.
|
||||
static void checkNeighbour( int x, int y, Neighbor * e, const Neighbor & n ) {
|
||||
nvDebugCheck(e != NULL);
|
||||
|
||||
uint d = dist( x, y, n.x, n.y );
|
||||
if( d < e->d ) {
|
||||
e->x = n.x;
|
||||
e->y = n.y;
|
||||
e->d = d;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// Voronoi filling using EDT-4
|
||||
// This implementation is based on Danielsson's algorithm published in:
|
||||
// "Euclidean Distance Mapping" Per-Erik Danielsson, Computer Graphics and Image Processing, 14, 1980
|
||||
void nv::fillVoronoi(FloatImage * img, const BitMap * bmap)
|
||||
{
|
||||
nvCheck(img != NULL);
|
||||
|
||||
const int w = img->width();
|
||||
const int h = img->height();
|
||||
const int count = img->componentNum();
|
||||
|
||||
nvCheck(bmap->width() == uint(w));
|
||||
nvCheck(bmap->height() == uint(h));
|
||||
|
||||
Array<Neighbor> edm;
|
||||
edm.resize(w * h);
|
||||
|
||||
int x, y;
|
||||
int x0, x1, y0, y1;
|
||||
|
||||
// Init edm.
|
||||
for( y = 0; y < h; y++ ) {
|
||||
for( x = 0; x < w; x++ ) {
|
||||
if( bmap->bitAt(x, y) ) {
|
||||
edm[y * w + x].x = x;
|
||||
edm[y * w + x].y = y;
|
||||
edm[y * w + x].d = 0;
|
||||
}
|
||||
else {
|
||||
edm[y * w + x].x = w;
|
||||
edm[y * w + x].y = h;
|
||||
edm[y * w + x].d = w*w + h*h;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// First pass.
|
||||
for( y = 0; y < h; y++ ) {
|
||||
for( x = 0; x < w; x++ ) {
|
||||
x0 = clamp(x-1, 0, w-1); // @@ Wrap?
|
||||
x1 = clamp(x+1, 0, w-1);
|
||||
y0 = clamp(y-1, 0, h-1);
|
||||
|
||||
Neighbor & e = edm[y * w + x];
|
||||
checkNeighbour(x, y, &e, edm[y0 * w + x0]);
|
||||
checkNeighbour(x, y, &e, edm[y0 * w + x]);
|
||||
checkNeighbour(x, y, &e, edm[y0 * w + x1]);
|
||||
checkNeighbour(x, y, &e, edm[y * w + x0]);
|
||||
}
|
||||
|
||||
for( x = w-1; x >= 0; x-- ) {
|
||||
x1 = clamp(x+1, 0, w-1);
|
||||
|
||||
Neighbor & e = edm[y * w + x];
|
||||
checkNeighbour(x, y, &e, edm[y * w + x1]);
|
||||
}
|
||||
}
|
||||
|
||||
// Third pass.
|
||||
for( y = h-1; y >= 0; y-- ) {
|
||||
for( x = w-1; x >= 0; x-- ) {
|
||||
x0 = clamp(x-1, 0, w-1);
|
||||
x1 = clamp(x+1, 0, w-1);
|
||||
y1 = clamp(y+1, 0, h-1);
|
||||
|
||||
Neighbor & e = edm[y * w + x];
|
||||
checkNeighbour(x, y, &e, edm[y * w + x1]);
|
||||
checkNeighbour(x, y, &e, edm[y1 * w + x0]);
|
||||
checkNeighbour(x, y, &e, edm[y1 * w + x]);
|
||||
checkNeighbour(x, y, &e, edm[y1 * w + x1]);
|
||||
}
|
||||
|
||||
for( x = 0; x < w; x++ ) {
|
||||
x0 = clamp(x-1, 0, w-1);
|
||||
|
||||
Neighbor & e = edm[y * w + x];
|
||||
checkNeighbour(x, y, &e, edm[y * w + x0]);
|
||||
}
|
||||
}
|
||||
|
||||
// Fill empty holes.
|
||||
for( y = 0; y < h; y++ ) {
|
||||
for( x = 0; x < w; x++ ) {
|
||||
const int sx = edm[y * w + x].x;
|
||||
const int sy = edm[y * w + x].y;
|
||||
|
||||
if (sx < w && sy < h)
|
||||
{
|
||||
if (sx != x || sy != y) {
|
||||
for(int c = 0; c < count; c++ ) {
|
||||
img->setPixel(img->pixel(sx, sy, c), x, y, c);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
static bool downsample(const FloatImage * src, const BitMap * srcMask, const FloatImage ** _dst, const BitMap ** _dstMask)
|
||||
{
|
||||
const uint w = src->width();
|
||||
const uint h = src->height();
|
||||
const uint count = src->componentNum();
|
||||
|
||||
// count holes in srcMask, return false if fully filled.
|
||||
uint holes = 0;
|
||||
for(uint y = 0; y < h; y++) {
|
||||
for(uint x = 0; x < w; x++) {
|
||||
holes += srcMask->bitAt(x, y) == 0;
|
||||
}
|
||||
}
|
||||
if (holes == 0 || (w == 2 || h == 2)) {
|
||||
// Stop when no holes or when the texture is very small.
|
||||
return false;
|
||||
}
|
||||
|
||||
// Apply box filter to image and mask and return true.
|
||||
const uint nw = w / 2;
|
||||
const uint nh = h / 2;
|
||||
|
||||
FloatImage * dst = new FloatImage();
|
||||
dst->allocate(count, nw, nh);
|
||||
|
||||
BitMap * dstMask = new BitMap(nw, nh);
|
||||
dstMask->clearAll();
|
||||
|
||||
for(uint c = 0; c < count; c++) {
|
||||
for(uint y = 0; y < nh; y++) {
|
||||
for(uint x = 0; x < nw; x++) {
|
||||
|
||||
const uint x0 = 2 * x + 0;
|
||||
const uint x1 = 2 * x + 1;
|
||||
const uint y0 = 2 * y + 0;
|
||||
const uint y1 = 2 * y + 1;
|
||||
|
||||
const float f0 = src->pixel(x0, y0, c);
|
||||
const float f1 = src->pixel(x1, y0, c);
|
||||
const float f2 = src->pixel(x0, y1, c);
|
||||
const float f3 = src->pixel(x1, y1, c);
|
||||
|
||||
const bool b0 = srcMask->bitAt(x0, y0);
|
||||
const bool b1 = srcMask->bitAt(x1, y0);
|
||||
const bool b2 = srcMask->bitAt(x0, y1);
|
||||
const bool b3 = srcMask->bitAt(x1, y1);
|
||||
|
||||
if (b0 || b1 || b2 || b3) {
|
||||
// Set bit mask.
|
||||
dstMask->setBitAt(x, y);
|
||||
|
||||
// Set pixel.
|
||||
float value = 0.0f;
|
||||
int total = 0;
|
||||
if (b0) { value += f0; total++; }
|
||||
if (b1) { value += f1; total++; }
|
||||
if (b2) { value += f2; total++; }
|
||||
if (b3) { value += f3; total++; }
|
||||
dst->setPixel(value / total, x, y, c);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*_dst = dst;
|
||||
*_dstMask = dstMask;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// This is the filter used in the Lumigraph paper.
|
||||
void nv::fillPullPush(FloatImage * img, const BitMap * bmap)
|
||||
{
|
||||
nvCheck(img != NULL);
|
||||
|
||||
const uint count = img->componentNum();
|
||||
const uint w = img->width();
|
||||
const uint h = img->height();
|
||||
const uint num = log2(max(w,h));
|
||||
|
||||
// Build mipmap chain.
|
||||
Array<const FloatImage *> mipmaps(num);
|
||||
Array<const BitMap *> mipmapMasks(num);
|
||||
|
||||
mipmaps.append(img);
|
||||
mipmapMasks.append(bmap);
|
||||
|
||||
const FloatImage * current;
|
||||
const BitMap * currentMask;
|
||||
|
||||
// Compute mipmap chain.
|
||||
while(downsample(mipmaps.back(), mipmapMasks.back(), ¤t, ¤tMask))
|
||||
{
|
||||
mipmaps.append(current);
|
||||
mipmapMasks.append(currentMask);
|
||||
}
|
||||
|
||||
// Sample mipmaps until non-hole is found.
|
||||
for(uint y = 0; y < h; y++) {
|
||||
for(uint x = 0; x < w; x++) {
|
||||
|
||||
int sx = x;
|
||||
int sy = y;
|
||||
//float sx = x;
|
||||
//float sy = y;
|
||||
|
||||
const uint levelCount = mipmaps.count();
|
||||
for (uint l = 0; l < levelCount; l++)
|
||||
{
|
||||
//const float fx = sx / mipmaps[l]->width();
|
||||
//const float fy = sy / mipmaps[l]->height();
|
||||
|
||||
if (mipmapMasks[l]->bitAt(sx, sy))
|
||||
{
|
||||
// Sample mipmaps[l](sx, sy) and copy to img(x, y)
|
||||
for(uint c = 0; c < count; c++) {
|
||||
//img->setPixel(mipmaps[l]->linear_clamp(fx, fy, c), x, y, c);
|
||||
img->setPixel(mipmaps[l]->pixel(sx, sy, c), x, y, c);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
sx /= 2;
|
||||
sy /= 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Don't delete the original image and mask.
|
||||
mipmaps[0] = NULL;
|
||||
mipmapMasks[0] = NULL;
|
||||
|
||||
// Delete the mipmaps.
|
||||
deleteAll(mipmaps);
|
||||
deleteAll(mipmapMasks);
|
||||
}
|
||||
|
||||
// It looks much cooler with trilinear filtering
|
||||
void nv::fillPullPushLinear(FloatImage * img, const BitMap * bmap)
|
||||
{
|
||||
nvCheck(img != NULL);
|
||||
|
||||
const uint count = img->componentNum();
|
||||
const uint w = img->width();
|
||||
const uint h = img->height();
|
||||
const uint num = log2(max(w,h));
|
||||
|
||||
// Build mipmap chain.
|
||||
Array<const FloatImage *> mipmaps(num);
|
||||
Array<const BitMap *> mipmapMasks(num);
|
||||
|
||||
mipmaps.append(img);
|
||||
mipmapMasks.append(bmap);
|
||||
|
||||
const FloatImage * current;
|
||||
const BitMap * currentMask;
|
||||
|
||||
// Compute mipmap chain.
|
||||
while(downsample(mipmaps.back(), mipmapMasks.back(), ¤t, ¤tMask))
|
||||
{
|
||||
mipmaps.append(current);
|
||||
mipmapMasks.append(currentMask);
|
||||
}
|
||||
|
||||
// Sample mipmaps until non-hole is found.
|
||||
for(uint y = 0; y < h; y++) {
|
||||
for(uint x = 0; x < w; x++) {
|
||||
|
||||
float sx = x;
|
||||
float sy = y;
|
||||
|
||||
float coverageSum = 0.0f;
|
||||
|
||||
const uint levelCount = mipmaps.count();
|
||||
for (uint l = 0; l < levelCount; l++)
|
||||
{
|
||||
const float fx = sx / mipmaps[l]->width();
|
||||
const float fy = sy / mipmaps[l]->height();
|
||||
|
||||
float coverage = mipmapMasks[l]->sampleLinearClamp(sx, sy);
|
||||
|
||||
if (coverage > 0.0f)
|
||||
{
|
||||
// Sample mipmaps[l](sx, sy) and copy to img(x, y)
|
||||
for(uint c = 0; c < count; c++) {
|
||||
img->addPixel((1 - coverageSum) * mipmaps[l]->sampleLinearClamp(fx, fy, c), x, y, c);
|
||||
}
|
||||
|
||||
coverageSum += coverage;
|
||||
|
||||
if (coverageSum >= 1.0f)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
sx /= 2;
|
||||
sy /= 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Don't delete the original image and mask.
|
||||
mipmaps[0] = NULL;
|
||||
mipmapMasks[0] = NULL;
|
||||
|
||||
// Delete the mipmaps.
|
||||
deleteAll(mipmaps);
|
||||
deleteAll(mipmapMasks);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
|
||||
This Code is from Charles Bloom:
|
||||
|
||||
DoPixelSeamFix
|
||||
10-20-02
|
||||
|
||||
Looks in the 5x5 local neighborhood (LocalPixels) of the desired pixel to fill.
|
||||
It tries to build a quadratic model of the neighborhood surface to use in
|
||||
extrapolating. You need 5 pixels to establish a 2d quadratic curve.
|
||||
|
||||
This is really just a nice generic way to extrapolate pixels. It also happens
|
||||
to work great for seam-fixing.
|
||||
|
||||
Note that I'm working on normals, but I treat them just as 3 scalars and normalize
|
||||
at the end. To be more correct, I would work on the surface of a sphere, but that
|
||||
just seems like way too much work.
|
||||
|
||||
*/
|
||||
|
||||
struct LocalPixels
|
||||
{
|
||||
// 5x5 neighborhood
|
||||
// the center is at result
|
||||
// index [y][x]
|
||||
bool fill[5][5];
|
||||
float data[5][5];
|
||||
|
||||
mutable float result;
|
||||
mutable float weight;
|
||||
|
||||
bool Quad3SubH(float * pQ, int row) const
|
||||
{
|
||||
const bool * pFill = fill[row];
|
||||
const float * pDat = data[row];
|
||||
|
||||
if ( pFill[1] && pFill[2] && pFill[3] )
|
||||
{
|
||||
// good row
|
||||
*pQ = pDat[1] - 2.f * pDat[2] + pDat[3];
|
||||
return true;
|
||||
}
|
||||
else if ( pFill[0] && pFill[1] && pFill[2] )
|
||||
{
|
||||
// good row
|
||||
*pQ = pDat[0] - 2.f * pDat[1] + pDat[2];
|
||||
return true;
|
||||
}
|
||||
else if ( pFill[2] && pFill[3] && pFill[4] )
|
||||
{
|
||||
// good row
|
||||
*pQ = pDat[2] - 2.f * pDat[3] + pDat[4];
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// improve result with a horizontal quad in row 1 and/or
|
||||
bool Quad3SubV(float * pQ, int col) const
|
||||
{
|
||||
if ( fill[1][col] && fill[2][col] && fill[3][col] )
|
||||
{
|
||||
// good row
|
||||
*pQ = data[1][col] - 2.f * data[2][col] + data[3][col];
|
||||
return true;
|
||||
}
|
||||
else if ( fill[0][col] && fill[1][col] && fill[2][col] )
|
||||
{
|
||||
// good row
|
||||
*pQ = data[0][col] - 2.f * data[1][col] + data[2][col];
|
||||
return true;
|
||||
}
|
||||
else if ( fill[2][col] && fill[3][col] && fill[4][col] )
|
||||
{
|
||||
// good row
|
||||
*pQ = data[2][col] - 2.f * data[3][col] + data[4][col];
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Quad3H(float * pQ) const
|
||||
{
|
||||
if (!Quad3SubH(pQ,1))
|
||||
{
|
||||
return Quad3SubH(pQ,3);
|
||||
}
|
||||
float q = 0.0f; // initializer not needed, just make it shut up
|
||||
if (Quad3SubH(&q, 3))
|
||||
{
|
||||
// got q and pQ
|
||||
*pQ = (*pQ+q)*0.5f;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Quad3V(float * pQ) const
|
||||
{
|
||||
if (!Quad3SubV(pQ, 1))
|
||||
{
|
||||
return Quad3SubV(pQ, 3);
|
||||
}
|
||||
float q = 0.0f; // initializer not needed, just make it shut up
|
||||
if (Quad3SubV(&q, 3))
|
||||
{
|
||||
// got q and pQ
|
||||
*pQ = (*pQ + q) * 0.5f;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
// Quad returns ([0]+[2] - 2.f*[1])
|
||||
// a common want is [1] - ([0]+[2])*0.5f ;
|
||||
// so use -0.5f*Quad
|
||||
|
||||
bool tryQuads() const
|
||||
{
|
||||
bool res = false;
|
||||
|
||||
// look for a pair that straddles the middle:
|
||||
if ( fill[2][1] && fill[2][3] )
|
||||
{
|
||||
// got horizontal straddle
|
||||
float q;
|
||||
if ( Quad3H(&q) )
|
||||
{
|
||||
result += (data[2][1] + data[2][3] - q) * 0.5f;
|
||||
weight += 1.f;
|
||||
res = true;
|
||||
}
|
||||
}
|
||||
if ( fill[1][2] && fill[3][2] )
|
||||
{
|
||||
// got vertical straddle
|
||||
float q;
|
||||
if ( Quad3V(&q) )
|
||||
{
|
||||
result += (data[1][2] + data[3][2] - q) * 0.5f;
|
||||
weight += 1.f;
|
||||
res = true;
|
||||
}
|
||||
}
|
||||
|
||||
// look for pairs that lead into the middle :
|
||||
if ( fill[2][0] && fill[2][1] )
|
||||
{
|
||||
// got left-side pair
|
||||
float q;
|
||||
if ( Quad3H(&q) )
|
||||
{
|
||||
result += data[2][1]*2.f - data[2][0] + q;
|
||||
weight += 1.f;
|
||||
res = true;
|
||||
}
|
||||
}
|
||||
if ( fill[2][3] && fill[2][4] )
|
||||
{
|
||||
// got right-side pair
|
||||
float q;
|
||||
if ( Quad3H(&q) )
|
||||
{
|
||||
result += data[2][3]*2.f - data[2][4] + q;
|
||||
weight += 1.f;
|
||||
res = true;
|
||||
}
|
||||
}
|
||||
if ( fill[0][2] && fill[1][2] )
|
||||
{
|
||||
// got left-side pair
|
||||
float q;
|
||||
if ( Quad3V(&q) )
|
||||
{
|
||||
result += data[1][2]*2.f - data[0][2] + q;
|
||||
weight += 1.f;
|
||||
res = true;
|
||||
}
|
||||
}
|
||||
if ( fill[3][2] && fill[4][2] )
|
||||
{
|
||||
// got right-side pair
|
||||
float q;
|
||||
if ( Quad3V(&q) )
|
||||
{
|
||||
result += data[3][2]*2.f - data[4][2] + q;
|
||||
weight += 1.f;
|
||||
res = true;
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
bool tryPlanar() const
|
||||
{
|
||||
// four cases :
|
||||
const int indices[] =
|
||||
{
|
||||
2,1, 1,2, 1,1,
|
||||
2,1, 3,2, 3,1,
|
||||
2,3, 1,2, 1,3,
|
||||
2,3, 3,2, 3,3
|
||||
};
|
||||
bool res = false;
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
const int * I = indices + i*6;
|
||||
if (!fill[ I[0] ][ I[1] ])
|
||||
continue;
|
||||
if (!fill[ I[2] ][ I[3] ])
|
||||
continue;
|
||||
if (!fill[ I[4] ][ I[5] ])
|
||||
continue;
|
||||
|
||||
result += data[ I[0] ][ I[1] ] + data[ I[2] ][ I[3] ] - data[ I[4] ][ I[5] ];
|
||||
weight += 1.0f;
|
||||
res = true;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
bool tryTwos() const
|
||||
{
|
||||
bool res = false;
|
||||
|
||||
if (fill[2][1] && fill[2][3])
|
||||
{
|
||||
result += (data[2][1] + data[2][3]) * 0.5f;
|
||||
weight += 1.0f;
|
||||
res = true;
|
||||
}
|
||||
if (fill[1][2] && fill[3][2])
|
||||
{
|
||||
result += (data[1][2] + data[3][2]) * 0.5f;
|
||||
weight += 1.0f;
|
||||
res = true;
|
||||
}
|
||||
|
||||
// four side-rotates :
|
||||
const int indices[] =
|
||||
{
|
||||
2,1, 2,0,
|
||||
2,3, 2,4,
|
||||
1,2, 0,2,
|
||||
3,2, 4,2,
|
||||
};
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
const int * I = indices + i*4;
|
||||
if (!fill[ I[0] ][ I[1] ])
|
||||
continue;
|
||||
if (!fill[ I[2] ][ I[3] ])
|
||||
continue;
|
||||
|
||||
result += data[ I[0] ][ I[1] ]*2.0f - data[ I[2] ][ I[3] ];
|
||||
weight += 1.0f;
|
||||
res = true;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
bool doLocalPixelFill() const
|
||||
{
|
||||
result = 0.0f;
|
||||
weight = 0.0f;
|
||||
|
||||
if (tryQuads()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (tryPlanar()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return tryTwos();
|
||||
}
|
||||
|
||||
}; // struct LocalPixels
|
||||
|
||||
|
||||
|
||||
// This is a quadratic extrapolation filter from Charles Bloom (DoPixelSeamFix). Used with his permission.
|
||||
void nv::fillQuadraticExtrapolate(int passCount, FloatImage * img, BitMap * bmap, int coverageIndex /*= -1*/)
|
||||
{
|
||||
nvCheck(passCount > 0);
|
||||
nvCheck(img != NULL);
|
||||
nvCheck(bmap != NULL);
|
||||
|
||||
const int w = img->width();
|
||||
const int h = img->height();
|
||||
const int count = img->componentNum();
|
||||
|
||||
nvCheck(bmap->width() == uint(w));
|
||||
nvCheck(bmap->height() == uint(h));
|
||||
|
||||
AutoPtr<BitMap> newbmap( new BitMap(w, h) );
|
||||
newbmap->clearAll();
|
||||
|
||||
float * coverageChannel = NULL;
|
||||
if (coverageIndex != -1)
|
||||
{
|
||||
coverageChannel = img->channel(coverageIndex);
|
||||
}
|
||||
|
||||
int firstChannel = -1;
|
||||
|
||||
for (int p = 0; p < passCount; p++)
|
||||
{
|
||||
for (int c = 0; c < count; c++)
|
||||
{
|
||||
if (c == coverageIndex) continue;
|
||||
if (firstChannel == -1) firstChannel = c;
|
||||
|
||||
float * channel = img->channel(c);
|
||||
|
||||
for (int yb = 0; yb < h; yb++) {
|
||||
for (int xb = 0; xb < w; xb++) {
|
||||
|
||||
if (bmap->bitAt(xb, yb)) {
|
||||
// Not a hole.
|
||||
newbmap->setBitAt(xb, yb);
|
||||
continue;
|
||||
}
|
||||
|
||||
int numFill = 0;
|
||||
|
||||
LocalPixels lp;
|
||||
for (int ny = 0; ny < 5; ny++)
|
||||
{
|
||||
int y = (yb + ny - 2);
|
||||
if ( y < 0 || y >= h )
|
||||
{
|
||||
// out of range
|
||||
for(int i = 0; i < 5; i++)
|
||||
{
|
||||
lp.fill[ny][i] = false;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int nx = 0; nx < 5; nx++)
|
||||
{
|
||||
int x = (xb + nx - 2);
|
||||
if (x < 0 || x >= w)
|
||||
{
|
||||
lp.fill[ny][nx] = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
int idx = img->index(x, y);
|
||||
if (!bmap->bitAt(idx))
|
||||
{
|
||||
lp.fill[ny][nx] = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
lp.fill[ny][nx] = true;
|
||||
lp.data[ny][nx] = channel[idx];
|
||||
numFill++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// need at least 3 to do anything decent
|
||||
if (numFill < 2)
|
||||
continue;
|
||||
|
||||
nvDebugCheck(lp.fill[2][2] == false);
|
||||
|
||||
if (lp.doLocalPixelFill())
|
||||
{
|
||||
const int idx = img->index(xb, yb);
|
||||
channel[idx] = lp.result / lp.weight;
|
||||
|
||||
if (c == firstChannel)
|
||||
{
|
||||
//coverageChannel[idx] /= lp.weight; // @@ Not sure what this was for, coverageChannel[idx] is always zero.
|
||||
newbmap->setBitAt(xb, yb);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Update the bit mask.
|
||||
swap(*newbmap, *bmap);
|
||||
}
|
||||
}
|
@ -1,98 +0,0 @@
|
||||
// This code is in the public domain -- castanyo@yahoo.es
|
||||
|
||||
#ifndef NV_IMAGE_HOLEFILLING_H
|
||||
#define NV_IMAGE_HOLEFILLING_H
|
||||
|
||||
#include <nvcore/BitArray.h>
|
||||
#include <nvimage/nvimage.h>
|
||||
|
||||
namespace nv
|
||||
{
|
||||
class FloatImage;
|
||||
|
||||
/// Bit mask.
|
||||
class BitMap
|
||||
{
|
||||
public:
|
||||
BitMap(uint w, uint h) :
|
||||
m_width(w), m_height(h), m_bitArray(w*h)
|
||||
{
|
||||
}
|
||||
|
||||
uint width() const { return m_width; }
|
||||
uint height() const { return m_height; }
|
||||
|
||||
float sampleLinearClamp(float x, float y) const;
|
||||
|
||||
bool bitAt(uint x, uint y) const
|
||||
{
|
||||
nvDebugCheck(x < m_width && y < m_height);
|
||||
return m_bitArray.bitAt(y * m_width + x);
|
||||
}
|
||||
bool bitAt(uint idx) const
|
||||
{
|
||||
return m_bitArray.bitAt(idx);
|
||||
}
|
||||
|
||||
void setBitAt(uint x, uint y)
|
||||
{
|
||||
nvDebugCheck(x < m_width && y < m_height);
|
||||
m_bitArray.setBitAt(y * m_width + x);
|
||||
}
|
||||
void setBitAt(uint idx)
|
||||
{
|
||||
m_bitArray.setBitAt(idx);
|
||||
}
|
||||
|
||||
void clearBitAt(uint x, uint y)
|
||||
{
|
||||
nvDebugCheck(x < m_width && y < m_height);
|
||||
m_bitArray.clearBitAt(y * m_width + x);
|
||||
}
|
||||
void clearBitAt(uint idx)
|
||||
{
|
||||
m_bitArray.clearBitAt(idx);
|
||||
}
|
||||
|
||||
void clearAll()
|
||||
{
|
||||
m_bitArray.clearAll();
|
||||
}
|
||||
|
||||
void setAll()
|
||||
{
|
||||
m_bitArray.setAll();
|
||||
}
|
||||
|
||||
void toggleAll()
|
||||
{
|
||||
m_bitArray.toggleAll();
|
||||
}
|
||||
|
||||
friend void swap(BitMap & a, BitMap & b)
|
||||
{
|
||||
nvCheck(a.m_width == b.m_width);
|
||||
nvCheck(a.m_height == b.m_height);
|
||||
//swap(const_cast<uint &>(a.m_width), const_cast<uint &>(b.m_width));
|
||||
//swap(const_cast<uint &>(a.m_height), const_cast<uint &>(b.m_height));
|
||||
swap(a.m_bitArray, b.m_bitArray);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
const uint m_width;
|
||||
const uint m_height;
|
||||
BitArray m_bitArray;
|
||||
|
||||
};
|
||||
|
||||
NVIMAGE_API void fillVoronoi(FloatImage * img, const BitMap * bmap);
|
||||
NVIMAGE_API void fillPullPush(FloatImage * img, const BitMap * bmap);
|
||||
NVIMAGE_API void fillPullPushLinear(FloatImage * img, const BitMap * bmap);
|
||||
|
||||
NVIMAGE_API void fillExtrapolate(int passCount, FloatImage * img, BitMap * bmap);
|
||||
NVIMAGE_API void fillQuadraticExtrapolate(int passCount, FloatImage * img, BitMap * bmap, int coverageIndex = -1);
|
||||
|
||||
} // nv namespace
|
||||
|
||||
#endif // NV_IMAGE_HOLEFILLING_H
|
@ -12,8 +12,6 @@
|
||||
#include <nvcore/Containers.h>
|
||||
#include <nvcore/StrLib.h>
|
||||
#include <nvcore/StdStream.h>
|
||||
#include <nvcore/Tokenizer.h>
|
||||
#include <nvcore/TextWriter.h>
|
||||
|
||||
// Extern
|
||||
#if defined(HAVE_JPEG)
|
||||
@ -186,13 +184,6 @@ FloatImage * nv::ImageIO::loadFloat(const char * fileName, Stream & s)
|
||||
}
|
||||
#endif
|
||||
|
||||
if (strCaseCmp(extension, ".pfm") == 0) {
|
||||
return loadFloatPFM(fileName, s);
|
||||
}
|
||||
if (strCaseCmp(extension, ".hdr") == 0) {
|
||||
return loadGridFloat(fileName, s);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@ -215,12 +206,6 @@ bool nv::ImageIO::saveFloat(const char * fileName, const FloatImage * fimage, ui
|
||||
}
|
||||
#endif
|
||||
|
||||
// @@ Disable Temporarily
|
||||
if (strCaseCmp(extension, ".pfm") == 0)
|
||||
{
|
||||
// return ImageIO::saveFloatPFM(fileName, fimage, base_component, num_components);
|
||||
}
|
||||
|
||||
//if (num_components == 3 || num_components == 4)
|
||||
if (num_components <= 4)
|
||||
{
|
||||
@ -1532,399 +1517,3 @@ bool nv::ImageIO::saveFloatEXR(const char * fileName, const FloatImage * fimage,
|
||||
|
||||
#endif // defined(HAVE_OPENEXR)
|
||||
|
||||
|
||||
FloatImage * nv::ImageIO::loadFloatPFM(const char * fileName, Stream & s)
|
||||
{
|
||||
nvCheck(s.isLoading());
|
||||
nvCheck(!s.isError());
|
||||
|
||||
Tokenizer parser(&s);
|
||||
|
||||
parser.nextToken();
|
||||
|
||||
bool grayscale;
|
||||
if (parser.token() == "PF")
|
||||
{
|
||||
grayscale = false;
|
||||
}
|
||||
else if (parser.token() == "Pf")
|
||||
{
|
||||
grayscale = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Invalid file.
|
||||
return NULL;
|
||||
}
|
||||
|
||||
parser.nextLine();
|
||||
|
||||
int width = parser.token().toInt(); parser.nextToken();
|
||||
int height = parser.token().toInt();
|
||||
|
||||
parser.nextLine();
|
||||
|
||||
float scaleFactor = parser.token().toFloat();
|
||||
|
||||
if (scaleFactor >= 0)
|
||||
{
|
||||
s.setByteOrder(Stream::BigEndian);
|
||||
}
|
||||
else
|
||||
{
|
||||
s.setByteOrder(Stream::LittleEndian);
|
||||
}
|
||||
scaleFactor = fabsf(scaleFactor);
|
||||
|
||||
// Allocate image.
|
||||
AutoPtr<FloatImage> fimage(new FloatImage());
|
||||
|
||||
if (grayscale)
|
||||
{
|
||||
fimage->allocate(1, width, height);
|
||||
|
||||
float * channel = fimage->channel(0);
|
||||
|
||||
for (int i = 0; i < width * height; i++)
|
||||
{
|
||||
s << channel[i];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
fimage->allocate(3, width, height);
|
||||
|
||||
float * rchannel = fimage->channel(0);
|
||||
float * gchannel = fimage->channel(1);
|
||||
float * bchannel = fimage->channel(2);
|
||||
|
||||
for (int i = 0; i < width * height; i++)
|
||||
{
|
||||
s << rchannel[i] << gchannel[i] << bchannel[i];
|
||||
}
|
||||
}
|
||||
|
||||
return fimage.release();
|
||||
}
|
||||
|
||||
bool nv::ImageIO::saveFloatPFM(const char * fileName, const FloatImage * fimage, uint base_component, uint num_components)
|
||||
{
|
||||
nvCheck(fileName != NULL);
|
||||
nvCheck(fimage != NULL);
|
||||
nvCheck(fimage->componentNum() <= base_component + num_components);
|
||||
nvCheck(num_components == 1 || num_components == 3);
|
||||
|
||||
StdOutputStream stream(fileName);
|
||||
TextWriter writer(&stream);
|
||||
|
||||
if (num_components == 1) writer.write("Pf\n");
|
||||
else /*if (num_components == 3)*/ writer.write("PF\n");
|
||||
|
||||
int w = fimage->width();
|
||||
int h = fimage->height();
|
||||
writer.write("%d %d\n", w, h);
|
||||
writer.write("%f\n", -1.0f); // little endian with 1.0 scale.
|
||||
|
||||
if (num_components == 1)
|
||||
{
|
||||
float * channel = const_cast<float *>(fimage->channel(0));
|
||||
|
||||
for (int i = 0; i < w * h; i++)
|
||||
{
|
||||
stream << channel[i];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
float * rchannel = const_cast<float *>(fimage->channel(0));
|
||||
float * gchannel = const_cast<float *>(fimage->channel(1));
|
||||
float * bchannel = const_cast<float *>(fimage->channel(2));
|
||||
|
||||
for (int i = 0; i < w * h; i++)
|
||||
{
|
||||
stream << rchannel[i] << gchannel[i] << bchannel[i];
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//#pragma warning(disable : 4996)
|
||||
|
||||
NVIMAGE_API FloatImage * nv::ImageIO::loadGridFloat(const char * fileName, Stream & s)
|
||||
{
|
||||
nvCheck(s.isLoading());
|
||||
nvCheck(!s.isError());
|
||||
|
||||
Tokenizer parser(&s);
|
||||
|
||||
parser.nextLine();
|
||||
|
||||
if (parser.token() != "ncols")
|
||||
{
|
||||
nvDebug("Failed to find 'ncols' token in file '%s'.\n", fileName);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
parser.nextToken(true);
|
||||
const int nCols = parser.token().toInt();
|
||||
|
||||
parser.nextToken(true);
|
||||
if (parser.token() != "nrows")
|
||||
{
|
||||
nvDebug("Failed to find 'nrows' token in file '%s'.\n", fileName);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
parser.nextToken(true);
|
||||
const int nRows = parser.token().toInt();
|
||||
|
||||
/* There's a byte order defined in the header. We could read it. However, here we
|
||||
just assume that it matches the platform's byte order.
|
||||
// There is then a bunch of data that we don't care about (lat, long definitions, etc).
|
||||
for (int i=0; i!=9; ++i)
|
||||
parser.nextToken(true);
|
||||
|
||||
if (parser.token() != "byteorder")
|
||||
return NULL;
|
||||
|
||||
parser.nextToken(true);
|
||||
|
||||
const Stream::ByteOrder byteOrder = (parser.token() == "LSBFIRST")? Stream::LittleEndian: Stream::BigEndian;
|
||||
*/
|
||||
|
||||
// GridFloat comes in two files: an ASCII header which was parsed above (.hdr) and a big blob
|
||||
// of binary data in a .flt file.
|
||||
Path dataPath(fileName);
|
||||
dataPath.stripExtension();
|
||||
dataPath.append(".flt");
|
||||
|
||||
// Open the binary data.
|
||||
FILE* file = fopen(dataPath.fileName(), "rb");
|
||||
if (!file)
|
||||
{
|
||||
nvDebug("Failed to find GridFloat blob file '%s' corresponding to '%s'.\n", dataPath.fileName(), fileName);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Allocate image.
|
||||
AutoPtr<FloatImage> fimage(new FloatImage());
|
||||
fimage->allocate(1, nCols, nRows);
|
||||
|
||||
float * channel = fimage->channel(0);
|
||||
|
||||
// The binary blob is defined to be in row-major order, containing IEEE floats.
|
||||
// So we can just slurp it in. Theoretically, we ought to use the byte order.
|
||||
const size_t nRead = fread((void*) channel, sizeof(float), nRows * nCols, file);
|
||||
fclose(file);
|
||||
|
||||
return fimage.release();
|
||||
}
|
||||
|
||||
#if 0
|
||||
|
||||
/** Save PNG*/
|
||||
static bool SavePNG(const PiImage * img, const char * name) {
|
||||
nvCheck( img != NULL );
|
||||
nvCheck( img->mem != NULL );
|
||||
|
||||
if( piStrCmp(piExtension(name), ".png" ) != 0 ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if( img->flags & PI_IT_CUBEMAP ) {
|
||||
nvDebug("*** Cannot save cubemaps as PNG.");
|
||||
return false;
|
||||
}
|
||||
if( img->flags & PI_IT_DDS ) {
|
||||
nvDebug("*** Cannot save DDS surface as PNG.");
|
||||
return false;
|
||||
}
|
||||
|
||||
nvDebug( "--- Saving '%s'.\n", name );
|
||||
|
||||
PiAutoPtr<PiStream> ar( PiFileSystem::CreateFileWriter( name ) );
|
||||
if( ar == NULL ) {
|
||||
nvDebug( "*** SavePNG: Error, cannot save file '%s'.\n", name );
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
public class PNGEnc {
|
||||
|
||||
public static function encode(img:BitmapData):ByteArray {
|
||||
// Create output byte array
|
||||
var png:ByteArray = new ByteArray();
|
||||
// Write PNG signature
|
||||
png.writeUnsignedInt(0x89504e47);
|
||||
png.writeUnsignedInt(0x0D0A1A0A);
|
||||
// Build IHDR chunk
|
||||
var IHDR:ByteArray = new ByteArray();
|
||||
IHDR.writeInt(img.width);
|
||||
IHDR.writeInt(img.height);
|
||||
IHDR.writeUnsignedInt(0x08060000); // 32bit RGBA
|
||||
IHDR.writeByte(0);
|
||||
writeChunk(png,0x49484452,IHDR);
|
||||
// Build IDAT chunk
|
||||
var IDAT:ByteArray= new ByteArray();
|
||||
for(var i:int=0;i < img.height;i++) {
|
||||
// no filter
|
||||
IDAT.writeByte(0);
|
||||
var p:uint;
|
||||
if ( !img.transparent ) {
|
||||
for(var j:int=0;j < img.width;j++) {
|
||||
p = img.getPixel(j,i);
|
||||
IDAT.writeUnsignedInt(
|
||||
uint(((p&0xFFFFFF) << 8)|0xFF));
|
||||
}
|
||||
} else {
|
||||
for(var j:int=0;j < img.width;j++) {
|
||||
p = img.getPixel32(j,i);
|
||||
IDAT.writeUnsignedInt(
|
||||
uint(((p&0xFFFFFF) << 8)|
|
||||
(shr(p,24))));
|
||||
}
|
||||
}
|
||||
}
|
||||
IDAT.compress();
|
||||
writeChunk(png,0x49444154,IDAT);
|
||||
// Build IEND chunk
|
||||
writeChunk(png,0x49454E44,null);
|
||||
// return PNG
|
||||
return png;
|
||||
}
|
||||
|
||||
private static var crcTable:Array;
|
||||
private static var crcTableComputed:Boolean = false;
|
||||
|
||||
private static function writeChunk(png:ByteArray,
|
||||
type:uint, data:ByteArray) {
|
||||
if (!crcTableComputed) {
|
||||
crcTableComputed = true;
|
||||
crcTable = [];
|
||||
for (var n:uint = 0; n < 256; n++) {
|
||||
var c:uint = n;
|
||||
for (var k:uint = 0; k < 8; k++) {
|
||||
if (c & 1) {
|
||||
c = uint(uint(0xedb88320) ^
|
||||
uint(c >>> 1));
|
||||
} else {
|
||||
c = uint(c >>> 1);
|
||||
}
|
||||
}
|
||||
crcTable[n] = c;
|
||||
}
|
||||
}
|
||||
var len:uint = 0;
|
||||
if (data != null) {
|
||||
len = data.length;
|
||||
}
|
||||
png.writeUnsignedInt(len);
|
||||
var p:uint = png.position;
|
||||
png.writeUnsignedInt(type);
|
||||
if ( data != null ) {
|
||||
png.writeBytes(data);
|
||||
}
|
||||
var e:uint = png.position;
|
||||
png.position = p;
|
||||
var c:uint = 0xffffffff;
|
||||
for (var i:int = 0; i < (e-p); i++) {
|
||||
c = uint(crcTable[
|
||||
(c ^ png.readUnsignedByte()) &
|
||||
uint(0xff)] ^ uint(c >>> 8));
|
||||
}
|
||||
c = uint(c^uint(0xffffffff));
|
||||
png.position = e;
|
||||
png.writeUnsignedInt(c);
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
#endif // 0
|
||||
|
||||
#if 0
|
||||
|
||||
|
||||
namespace ImageIO {
|
||||
|
||||
/** Init ImageIO plugins. */
|
||||
void InitPlugins() {
|
||||
// AddInputPlugin( "", LoadANY );
|
||||
AddInputPlugin( "tga", LoadTGA );
|
||||
#if HAVE_PNG
|
||||
AddInputPlugin( "png", LoadPNG );
|
||||
#endif
|
||||
#if HAVE_JPEG
|
||||
AddInputPlugin( "jpg", LoadJPG );
|
||||
#endif
|
||||
AddInputPlugin( "dds", LoadDDS );
|
||||
|
||||
AddOutputPlugin( "tga", SaveTGA );
|
||||
}
|
||||
|
||||
/** Reset ImageIO plugins. */
|
||||
void ResetPlugins() {
|
||||
s_plugin_load_map.Clear();
|
||||
s_plugin_save_map.Clear();
|
||||
}
|
||||
|
||||
/** Add an input plugin. */
|
||||
void AddInputPlugin( const char * ext, ImageInput_Plugin plugin ) {
|
||||
s_plugin_load_map.Add(ext, plugin);
|
||||
}
|
||||
|
||||
/** Add an output plugin. */
|
||||
void AddOutputPlugin( const char * ext, ImageOutput_Plugin plugin ) {
|
||||
s_plugin_save_map.Add(ext, plugin);
|
||||
}
|
||||
|
||||
|
||||
bool Load(PiImage * img, const char * name, PiStream & stream) {
|
||||
|
||||
// Get name extension.
|
||||
const char * extension = piExtension(name);
|
||||
|
||||
// Skip the dot.
|
||||
if( *extension == '.' ) {
|
||||
extension++;
|
||||
}
|
||||
|
||||
// Lookup plugin in the map.
|
||||
ImageInput_Plugin plugin = NULL;
|
||||
if( s_plugin_load_map.Get(extension, &plugin) ) {
|
||||
return plugin(img, stream);
|
||||
}
|
||||
|
||||
/*foreach(i, s_plugin_load_map) {
|
||||
nvDebug("%s %s %d\n", s_plugin_load_map[i].key.GetStr(), extension, 0 == strcmp(extension, s_plugin_load_map[i].key));
|
||||
}
|
||||
|
||||
nvDebug("No plugin found for '%s' %d.\n", extension, s_plugin_load_map.Size());*/
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Save(const PiImage * img, const char * name, PiStream & stream) {
|
||||
|
||||
// Get name extension.
|
||||
const char * extension = piExtension(name);
|
||||
|
||||
// Skip the dot.
|
||||
if( *extension == '.' ) {
|
||||
extension++;
|
||||
}
|
||||
|
||||
// Lookup plugin in the map.
|
||||
ImageOutput_Plugin plugin = NULL;
|
||||
if( s_plugin_save_map.Get(extension, &plugin) ) {
|
||||
return plugin(img, stream);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
} // ImageIO
|
||||
|
||||
#endif // 0
|
||||
|
||||
|
@ -53,14 +53,6 @@ namespace nv
|
||||
NVIMAGE_API bool saveFloatEXR(const char * fileName, const FloatImage * fimage, uint base_component, uint num_components);
|
||||
#endif
|
||||
|
||||
NVIMAGE_API FloatImage * loadFloatPFM(const char * fileName, Stream & s);
|
||||
NVIMAGE_API bool saveFloatPFM(const char * fileName, const FloatImage * fimage, uint base_component, uint num_components);
|
||||
|
||||
// GridFloat is a simple, open format for terrain elevation data. See http://ned.usgs.gov/Ned/faq.asp.
|
||||
// Expects: 1) fileName will be an ".hdr" header file, 2) there will also exist a corresponding float data
|
||||
// blob in a ".flt" file. (This is what USGS gives you.)
|
||||
NVIMAGE_API FloatImage * loadGridFloat(const char * fileName, Stream & s);
|
||||
|
||||
} // ImageIO namespace
|
||||
|
||||
} // nv namespace
|
||||
|
@ -1,17 +0,0 @@
|
||||
// This code is in the public domain -- castanyo@yahoo.es
|
||||
|
||||
#ifndef NV_IMAGE_NORMALMIPMAP_H
|
||||
#define NV_IMAGE_NORMALMIPMAP_H
|
||||
|
||||
#include <nvimage/nvimage.h>
|
||||
|
||||
|
||||
namespace nv
|
||||
{
|
||||
class FloatImage;
|
||||
|
||||
FloatImage * createNormalMipmapMap(const FloatImage * img);
|
||||
|
||||
} // nv namespace
|
||||
|
||||
#endif // NV_IMAGE_NORMALMIPMAP_H
|
Reference in New Issue
Block a user