#include "PtexPlatform.h"
#include <math.h>
#include <assert.h>
#include "PtexSeparableFilter.h"
#include "PtexSeparableKernel.h"
#include "PtexUtils.h"
void PtexSeparableFilter::eval(float* result, int firstChan, int nChannels,
int faceid, float u, float v,
float uw1, float vw1, float uw2, float vw2,
float width, float blur)
{
if (!_tx || nChannels <= 0) return;
if (faceid < 0 || faceid >= _tx->numFaces()) return;
_ntxchan = _tx->numChannels();
_dt = _tx->dataType();
_firstChanOffset = firstChan*DataSize(_dt);
_nchan = PtexUtils::min(nChannels, _ntxchan-firstChan);
const FaceInfo& f = _tx->getFaceInfo(faceid);
if (f.isNeighborhoodConstant()) {
PtexPtr<PtexFaceData> data ( _tx->getData(faceid, 0) );
if (data) {
char* d = (char*) data->getData() + _firstChanOffset;
Ptex::ConvertToFloat(result, d, _dt, _nchan);
}
return;
}
float uw = fabs(uw1) + fabs(uw2), vw = fabs(vw1) + fabs(vw2);
switch (_uMode) {
case m_clamp: u = PtexUtils::clamp(u, 0.0f, 1.0f); break;
case m_periodic: u = u-floor(u); break;
case m_black: break;
}
switch (_vMode) {
case m_clamp: v = PtexUtils::clamp(v, 0.0f, 1.0f); break;
case m_periodic: v = v-floor(v);
case m_black: break;
}
PtexSeparableKernel k;
if (f.isSubface()) {
uw = uw * width + blur * 2;
vw = vw * width + blur * 2;
buildKernel(k, u*.5, v*.5, uw*.5, vw*.5, f.res);
if (k.res.ulog2 == 0) k.upresU();
if (k.res.vlog2 == 0) k.upresV();
k.res.ulog2--; k.res.vlog2--;
}
else {
uw = uw * width + blur;
vw = vw * width + blur;
buildKernel(k, u, v, uw, vw, f.res);
}
k.stripZeros();
assert(k.uw > 0 && k.vw > 0);
assert(k.uw <= PtexSeparableKernel::kmax && k.vw <= PtexSeparableKernel::kmax);
_weight = k.weight();
_result = (double*) alloca(sizeof(double)*_nchan);
memset(_result, 0, sizeof(double)*_nchan);
splitAndApply(k, faceid, f);
double scale = 1.0 / (_weight * OneValue(_dt));
for (int i = 0; i < _nchan; i++) result[i] = float(_result[i] * scale);
_result = 0;
}
void PtexSeparableFilter::splitAndApply(PtexSeparableKernel& k, int faceid, const Ptex::FaceInfo& f)
{
bool splitR = (k.u+k.uw > k.res.u()), splitL = (k.u < 0);
bool splitT = (k.v+k.vw > k.res.v()), splitB = (k.v < 0);
#ifdef NOEDGEBLEND
if (splitR) k.mergeR(_uMode);
if (splitL) k.mergeL(_uMode);
if (splitT) k.mergeT(_vMode);
if (splitB) k.mergeB(_vMode);
#else
if (splitR || splitL || splitT || splitB) {
PtexSeparableKernel ka, kc;
if (splitR) {
if (f.adjface(e_right) >= 0) {
k.splitR(ka);
if (splitT) {
if (f.adjface(e_top) >= 0) {
ka.splitT(kc);
applyToCorner(kc, faceid, f, e_top);
}
else ka.mergeT(_vMode);
}
if (splitB) {
if (f.adjface(e_bottom) >= 0) {
ka.splitB(kc);
applyToCorner(kc, faceid, f, e_right);
}
else ka.mergeB(_vMode);
}
applyAcrossEdge(ka, faceid, f, e_right);
}
else k.mergeR(_uMode);
}
if (splitL) {
if (f.adjface(e_left) >= 0) {
k.splitL(ka);
if (splitT) {
if (f.adjface(e_top) >= 0) {
ka.splitT(kc);
applyToCorner(kc, faceid, f, e_left);
}
else ka.mergeT(_vMode);
}
if (splitB) {
if (f.adjface(e_bottom) >= 0) {
ka.splitB(kc);
applyToCorner(kc, faceid, f, e_bottom);
}
else ka.mergeB(_vMode);
}
applyAcrossEdge(ka, faceid, f, e_left);
}
else k.mergeL(_uMode);
}
if (splitT) {
if (f.adjface(e_top) >= 0) {
k.splitT(ka);
applyAcrossEdge(ka, faceid, f, e_top);
}
else k.mergeT(_vMode);
}
if (splitB) {
if (f.adjface(e_bottom) >= 0) {
k.splitB(ka);
applyAcrossEdge(ka, faceid, f, e_bottom);
}
else k.mergeB(_vMode);
}
}
#endif
apply(k, faceid, f);
}
void PtexSeparableFilter::applyAcrossEdge(PtexSeparableKernel& k,
int faceid, const Ptex::FaceInfo& f, int eid)
{
int afid = f.adjface(eid), aeid = f.adjedge(eid);
const Ptex::FaceInfo* af = &_tx->getFaceInfo(afid);
int rot = eid - aeid + 2;
bool fIsSubface = f.isSubface(), afIsSubface = af->isSubface();
if (fIsSubface != afIsSubface) {
if (afIsSubface) {
bool primary = k.adjustMainToSubface(eid);
if (!primary) {
int neid = (aeid + 3) % 4;
afid = af->adjface(neid);
aeid = af->adjedge(neid);
af = &_tx->getFaceInfo(afid);
rot += neid - aeid + 2;
}
}
else {
bool primary = (af->adjface(aeid) == faceid);
k.adjustSubfaceToMain(eid - primary);
}
}
k.rotate(rot);
if (afIsSubface) splitAndApply(k, afid, *af);
else apply(k, afid, *af);
}
void PtexSeparableFilter::applyToCorner(PtexSeparableKernel& k, int faceid,
const Ptex::FaceInfo& f, int eid)
{
int afid = faceid, aeid = eid;
const FaceInfo* af = &f;
bool prevIsSubface = af->isSubface();
const int MaxValence = 10;
int cfaceId[MaxValence];
int cedgeId[MaxValence];
const FaceInfo* cface[MaxValence];
int numCorners = 0;
for (int i = 0; i < MaxValence; i++) {
int prevFace = afid;
afid = af->adjface(aeid);
aeid = (af->adjedge(aeid) + 1) % 4;
if (afid < 0 || (afid == faceid && aeid == eid)) {
numCorners = i - 2;
break;
}
af = &_tx->getFaceInfo(afid);
cfaceId[i] = afid;
cedgeId[i] = aeid;
cface[i] = af;
bool isSubface = af->isSubface();
if (prevIsSubface && !isSubface && af->adjface((aeid+3)%4) == prevFace)
{
bool primary = (i==1);
k.adjustSubfaceToMain(eid + primary * 2);
k.rotate(eid - aeid + 3 - primary);
splitAndApply(k, afid, *af);
return;
}
prevIsSubface = isSubface;
}
if (numCorners == 1) {
applyToCornerFace(k, f, eid, cfaceId[1], *cface[1], cedgeId[1]);
}
else if (numCorners > 1) {
k.rotate(eid + 2);
k.makeSymmetric();
for (int i = 1; i <= numCorners; i++) {
PtexSeparableKernel kc = k;
applyToCornerFace(kc, f, 2, cfaceId[i], *cface[i], cedgeId[i]);
}
_weight += k.weight() * (numCorners-1);
}
else {
_weight -= k.weight();
}
}
void PtexSeparableFilter::applyToCornerFace(PtexSeparableKernel& k, const Ptex::FaceInfo& f, int eid,
int cfid, const Ptex::FaceInfo& cf, int ceid)
{
bool fIsSubface = f.isSubface(), cfIsSubface = cf.isSubface();
if (fIsSubface != cfIsSubface) {
if (cfIsSubface) k.adjustMainToSubface(eid + 3);
else k.adjustSubfaceToMain(eid + 3);
}
k.rotate(eid - ceid + 2);
if (cfIsSubface) splitAndApply(k, cfid, cf);
else apply(k, cfid, cf);
}
void PtexSeparableFilter::apply(PtexSeparableKernel& k, int faceid, const Ptex::FaceInfo& f)
{
assert(k.u >= 0 && k.u + k.uw <= k.res.u());
assert(k.v >= 0 && k.v + k.vw <= k.res.v());
if (k.uw <= 0 || k.vw <= 0) return;
while (k.res.u() > f.res.u()) k.downresU();
while (k.res.v() > f.res.v()) k.downresV();
PtexPtr<PtexFaceData> dh ( _tx->getData(faceid, k.res) );
if (!dh) return;
if (dh->isConstant()) {
k.applyConst(_result, (char*)dh->getData()+_firstChanOffset, _dt, _nchan);
}
else if (dh->isTiled()) {
Ptex::Res tileres = dh->tileRes();
PtexSeparableKernel kt;
kt.res = tileres;
int tileresu = tileres.u();
int tileresv = tileres.v();
int ntilesu = k.res.u() / tileresu;
for (int v = k.v, vw = k.vw; vw > 0; vw -= kt.vw, v += kt.vw) {
int tilev = v / tileresv;
kt.v = v % tileresv;
kt.vw = PtexUtils::min(vw, tileresv - kt.v);
kt.kv = k.kv + v - k.v;
for (int u = k.u, uw = k.uw; uw > 0; uw -= kt.uw, u += kt.uw) {
int tileu = u / tileresu;
kt.u = u % tileresu;
kt.uw = PtexUtils::min(uw, tileresu - kt.u);
kt.ku = k.ku + u - k.u;
PtexPtr<PtexFaceData> th ( dh->getTile(tilev * ntilesu + tileu) );
if (th) {
if (th->isConstant())
kt.applyConst(_result, (char*)th->getData()+_firstChanOffset, _dt, _nchan);
else
kt.apply(_result, (char*)th->getData()+_firstChanOffset, _dt, _nchan, _ntxchan);
}
}
}
}
else {
k.apply(_result, (char*)dh->getData()+_firstChanOffset, _dt, _nchan, _ntxchan);
}
}