#include "PtexFilterContext.h"
#include "PtexFilterKernel.h"
#include "PtexHalf.h"
class PtexFilterKernel::Iter : public Ptex {
public:
const PtexFilterContext& ctx;
int ustride;
int vstride;
int rowskip;
double* pos;
double* rowend;
double* end;
int dstart;
int drowskip;
Iter(const PtexFilterKernel& k, int rotate, const PtexFilterContext& c)
: ctx(c)
{
int resu = k.res.u(), resv = k.res.v();
switch (rotate) {
case 0:
ustride = 1;
vstride = k.stride;
pos = k.start;
rowend = pos + k.uw;
end = pos + vstride * k.vw;
dstart = k.v * resu + k.u;
drowskip = resu - k.uw;
break;
case 1:
ustride = -k.stride;
vstride = 1;
pos = k.start + k.stride * (k.vw-1);
rowend = k.start - k.stride;
end = pos + k.uw;
dstart = k.u * resv + (resv - k.vw - k.v);
drowskip = resv - k.vw;
break;
case 2:
ustride = -1;
vstride = -k.stride;
pos = k.start + k.stride * (k.vw-1) + k.uw-1;
rowend = pos - k.uw;
end = pos + vstride * k.vw;
dstart = (resv - k.vw - k.v) * resu + (resu - k.uw - k.u);
drowskip = resu - k.uw;
break;
case 3:
ustride = k.stride;
vstride = -1;
pos = k.start + k.uw-1;
rowend = pos + k.vw * k.stride;
end = pos - k.uw;
dstart = (resu - k.uw - k.u) * resv + k.v;
drowskip = resv - k.vw;
break;
}
rowskip = int(pos - rowend) + vstride;
dstart = dstart * c.ntxchannels + c.firstchan;
drowskip *= c.ntxchannels;
}
double nextval()
{
double result = *pos;
pos += ustride;
return result;
}
bool rowdone()
{
if (pos != rowend) return 0;
rowend += vstride;
pos += rowskip;
return 1;
}
bool done()
{
return pos == end;
}
};
class PtexFilterKernel::TileIter : public Ptex
{
public:
PtexFilterKernel kernels[4];
int tiles[4];
int ntiles;
int index;
TileIter(const PtexFilterKernel& k, int rotate, Res tileres)
: index(0)
{
int tileu = k.u >> tileres.ulog2;
int tilev = k.v >> tileres.vlog2;
int ntilesu = k.res.ntilesu(tileres);
int ntilesv = k.res.ntilesv(tileres);
int u = k.u - tileu * tileres.u();
int v = k.v - tilev * tileres.v();
kernels[0].set(tileres, u, v, k.uw, k.vw, k.start, k.stride);
int tilesu[4], tilesv[4];
tilesu[0] = tileu; tilesv[0] = tilev;
ntiles = 1;
PtexFilterKernel ku, kv, kc;
kernels[0].split(ku, kv, kc);
if (ku) {
kernels[ntiles] = ku;
tilesu[ntiles] = tileu + 1;
tilesv[ntiles] = tilev;
ntiles++;
}
if (kv) {
kernels[ntiles] = kv;
tilesu[ntiles] = tileu;
tilesv[ntiles] = tilev + 1;
ntiles++;
}
if (kc) {
kernels[ntiles] = kc;
tilesu[ntiles] = tileu + 1;
tilesv[ntiles] = tilev + 1;
ntiles++;
}
switch (rotate) {
default:
case 0:
for (int i = 0; i < ntiles; i++)
tiles[i] = tilesv[i] * ntilesu + tilesu[i];
break;
case 1:
for (int i = 0; i < ntiles; i++)
tiles[i] = tilesu[i] * ntilesv + (ntilesv - 1 - tilesv[i]);
break;
case 2:
for (int i = 0; i < ntiles; i++)
tiles[i] = (ntilesv - 1 - tilesv[i]) * ntilesu
+ (ntilesu - 1 - tilesu[i]);
break;
case 3:
for (int i = 0; i < ntiles; i++)
tiles[i] = (ntilesu - 1 - tilesu[i]) * ntilesv + tilesv[i];
break;
}
}
int tile() { return tiles[index]; }
const PtexFilterKernel& kernel() { return kernels[index]; }
bool next() { return ++index < ntiles; }
};
void PtexFilterKernel::split(PtexFilterKernel& ku, PtexFilterKernel& kv,
PtexFilterKernel& kc)
{
if (u < 0) { splitL(ku); }
else if (u+uw > res.u()) { splitR(ku); }
if (v < 0) { splitB(kv); if (ku) ku.splitB(kc); }
else if (v+vw > res.v()) { splitT(kv); if (ku) ku.splitT(kc); }
if (kc) kc.eid = ((ku.eid+1)%4 == kv.eid) ? ku.eid : kv.eid;
}
namespace {
template<typename T, int n>
struct VecAccum {
VecAccum() {}
void operator()(float* result, const T* val, double weight)
{
*result = float(*result + *val * weight);
VecAccum<T,n-1>()(result+1, val+1, weight);
}
};
template<typename T>
struct VecAccum<T,0> { void operator()(float*, const T*, double) {} };
template<typename T>
struct VecAccumN {
void operator()(float* result, const T* val, int nchan, double weight)
{
for (int i = 0; i < nchan; i++)
result[i] = float(result[i] + val[i] * weight);
}
};
template<typename T>
inline void ApplyConstT(void* data, const PtexFilterContext& c, double weight)
{
VecAccumN<T>()(c.result, ((T*)data)+c.firstchan, c.nchannels, weight);
}
inline void ApplyConst(void* data, const PtexFilterContext& c, double weight)
{
switch (c.dt) {
case Ptex::dt_uint8: ApplyConstT<uint8_t> (data, c, weight); break;
case Ptex::dt_uint16: ApplyConstT<uint16_t>(data, c, weight); break;
case Ptex::dt_half: ApplyConstT<PtexHalf>(data, c, weight); break;
case Ptex::dt_float: ApplyConstT<float> (data, c, weight); break;
}
}
template<typename T, int n>
inline void ApplyT(void* data, PtexFilterKernel::Iter& i)
{
T* ptr = ((T*)data) + i.dstart;
float* result = i.ctx.result;
int ntxchan = i.ctx.ntxchannels;
VecAccum<T,n> accum;
do {
do {
accum(result, ptr, i.nextval());
ptr += ntxchan;
} while (!i.rowdone());
ptr += i.drowskip;
} while(!i.done());
}
template<typename T>
inline void ApplyT(void* data, PtexFilterKernel::Iter& i)
{
T* ptr = ((T*)data) + i.dstart;
float* result = i.ctx.result;
int nchan = i.ctx.nchannels;
int ntxchan = i.ctx.ntxchannels;
VecAccumN<T> accum;
do {
do {
accum(result, ptr, nchan, i.nextval());
ptr += ntxchan;
} while (!i.rowdone());
ptr += i.drowskip;
} while(!i.done());
}
inline void Apply(void* data, PtexFilterKernel::Iter& i)
{
switch((i.ctx.nchannels-1)<<2|i.ctx.dt) {
case (0<<2|Ptex::dt_uint8): ApplyT<uint8_t, 1>(data, i); break;
case (0<<2|Ptex::dt_uint16): ApplyT<uint16_t,1>(data, i); break;
case (0<<2|Ptex::dt_half): ApplyT<PtexHalf,1>(data, i); break;
case (0<<2|Ptex::dt_float): ApplyT<float, 1>(data, i); break;
case (1<<2|Ptex::dt_uint8): ApplyT<uint8_t, 2>(data, i); break;
case (1<<2|Ptex::dt_uint16): ApplyT<uint16_t,2>(data, i); break;
case (1<<2|Ptex::dt_half): ApplyT<PtexHalf,2>(data, i); break;
case (1<<2|Ptex::dt_float): ApplyT<float, 2>(data, i); break;
case (2<<2|Ptex::dt_uint8): ApplyT<uint8_t, 3>(data, i); break;
case (2<<2|Ptex::dt_uint16): ApplyT<uint16_t,3>(data, i); break;
case (2<<2|Ptex::dt_half): ApplyT<PtexHalf,3>(data, i); break;
case (2<<2|Ptex::dt_float): ApplyT<float, 3>(data, i); break;
case (3<<2|Ptex::dt_uint8): ApplyT<uint8_t, 4>(data, i); break;
case (3<<2|Ptex::dt_uint16): ApplyT<uint16_t,4>(data, i); break;
case (3<<2|Ptex::dt_half): ApplyT<PtexHalf,4>(data, i); break;
case (3<<2|Ptex::dt_float): ApplyT<float, 4>(data, i); break;
default:
switch (i.ctx.dt) {
case Ptex::dt_uint8: ApplyT<uint8_t> (data, i); break;
case Ptex::dt_uint16: ApplyT<uint16_t>(data, i); break;
case Ptex::dt_half: ApplyT<PtexHalf>(data, i); break;
case Ptex::dt_float: ApplyT<float> (data, i); break;
}
}
}
}
void PtexFilterKernel::apply(int faceid, int rotate, const PtexFilterContext& c) const
{
PtexPtr<PtexFaceData> dh ( c.tx->getData(faceid, (rotate & 1) ? res.swappeduv() : res) );
if (!dh) return;
if (dh->isConstant()) {
ApplyConst(dh->getData(), c, totalWeight());
}
else if (dh->isTiled()) {
Res tres = dh->tileRes();
TileIter tileiter(*this, rotate, (rotate & 1) ? tres.swappeduv() : tres);
do {
PtexPtr<PtexFaceData> th ( dh->getTile(tileiter.tile()) );
if (th->isConstant()) {
ApplyConst(th->getData(), c, tileiter.kernel().totalWeight());
}
else {
Iter iter(tileiter.kernel(), rotate, c);
Apply(th->getData(), iter);
}
} while (tileiter.next());
}
else {
Iter iter(*this, rotate, c);
Apply(dh->getData(), iter);
}
}
void PtexFilterKernel::applyConst(void* data, const PtexFilterContext& c, double weight)
{
ApplyConst(data, c, weight);
}