#include "PtexPlatform.h"
#include "PtexMitchellFilter.h"
#include "PtexUtils.h"
void PtexMitchellFilter::setSharpness(float sharpness)
{
_sharpness = sharpness;
double B = 1 - sharpness;
_filter[0] = 1.5 - B;
_filter[1] = 1.5 * B - 2.5;
_filter[2] = 1 - (1./3) * B;
_filter[3] = (1./3) * B - 0.5;
_filter[4] = 2.5 - 1.5 * B;
_filter[5] = 2 * B - 4;
_filter[6] = 2 - (2./3) * B;
}
void PtexMitchellFilter::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 0
{
const FaceInfo& f = tx->getFaceInfo(faceid);
int resu = f.res.u(), resv = f.res.v();
int ui = PtexUtils::clamp(int(u * resu), 0, resu-1);
int vi = PtexUtils::clamp(int(v * resv), 0, resv-1);
tx->getPixel(faceid, ui, vi, result, firstchan, nchannels);
return;
}
#endif
float uw = fabs(uw1) + fabs(uw2), vw = fabs(vw1) + fabs(vw2);
if (!_ctx.prepare(result, firstchan, nchannels, _tx, faceid, u, v, uw, vw))
return;
double weight = OneValueInv(_ctx.dt);
const FaceInfo& f = _tx->getFaceInfo(faceid);
_isConstant = f.isConstant();
_ctx.uw = _ctx.uw * width + blur;
_ctx.vw = _ctx.vw * width + blur;
_ctx.uw = PtexUtils::min(_ctx.uw, 0.25f);
_ctx.vw = PtexUtils::min(_ctx.vw, 0.25f);
_ctx.uw = PtexUtils::max(_ctx.uw, 1.0f/(f.res.u()));
_ctx.vw = PtexUtils::max(_ctx.vw, 1.0f/(f.res.v()));
int ureslog2 = int(ceil(log2(1.0/_ctx.uw))),
vreslog2 = int(ceil(log2(1.0/_ctx.vw)));
_face.set(faceid, Res(ureslog2, vreslog2));
#if 1
getNeighborhood(f);
#endif
if (_isConstant) {
PtexFaceData* data = _tx->getData(faceid, 0);
if (data) {
char* d = (char*) data->getData() + _ctx.firstchan*DataSize(_ctx.dt);
Ptex::ConvertToFloat(_ctx.result, d, _ctx.dt, _ctx.nchannels);
data->release();
}
return;
}
if (!_uface && !_vface) {
evalFaces(_face.res, weight, _ctx.uw, _ctx.vw);
return;
}
double mweight = weight * (1 - _ublend) * (1 - _vblend);
double uweight = weight * _ublend * (1 - _vblend);
double vweight = weight * (1 - _ublend) * _vblend;
double cweight = weight * _ublend * _vblend;
if (cweight) {
if (_cface) {
if (!_cface.blend) mweight += cweight;
else if (_cface.res == _uface.res) uweight += cweight;
else if (_cface.res == _vface.res) vweight += cweight;
else evalFaces(_cface.res, cweight);
}
else mweight += cweight;
}
if (uweight) {
if (!_uface.blend) mweight += uweight;
else if (_vface && (_uface.res == _vface.res)) vweight += uweight;
else evalFaces(_uface.res, uweight);
}
if (vweight) {
if (!_vface.blend) mweight += vweight;
else evalFaces(_vface.res, vweight);
}
if (mweight) {
evalFaces(_face.res, mweight, _ctx.uw, _ctx.vw);
}
}
void PtexMitchellFilter::getNeighborhood(const FaceInfo& f)
{
_uface.clear(); _vface.clear(), _cface.clear(), _cfaces.clear();
EdgeId ueid, veid;
double udist, vdist;
if (_ctx.u < .5) { ueid = e_left; udist = _ctx.u; }
else { ueid = e_right; udist = 1 - _ctx.u; }
if (_ctx.v < .5) { veid = e_bottom; vdist = _ctx.v; }
else { veid = e_top; vdist = 1 - _ctx.v; }
static const double blendstart = 1.5;
static const double blendend = 2.5;
double ublendstart=0, ublendend=0, vblendstart=0, vblendend=0;
int ufid = f.adjfaces[ueid], vfid = f.adjfaces[veid];
const FaceInfo* uf = 0, * vf = 0;
if (ufid != -1) {
double texel = 1.0/_face.res.u();
ublendstart = PtexUtils::min(blendstart*texel, .375);
ublendend = PtexUtils::min(blendend*texel, 0.5);
uf = &_ctx.tx->getFaceInfo(ufid);
_uface.set(ufid, uf->res, f.adjedge(ueid) - ueid + 2);
_uface.clampres(_face.res);
}
if (vfid != -1) {
double texel = 1.0/_face.res.v();
vblendstart = PtexUtils::min(blendstart*texel, .375);
vblendend = PtexUtils::min(blendend*texel, 0.5);
vf = &_ctx.tx->getFaceInfo(vfid);
_vface.set(vfid, vf->res, f.adjedge(veid) - veid + 2);
_vface.clampres(_face.res);
}
if (_uface && _vface) {
if (_vface.res.ulog2 != _face.res.ulog2) {
double texel = 1.0/_vface.res.u();
double adjstart = PtexUtils::min(blendstart*texel, .375);
double adjend = PtexUtils::min(blendend*texel, .5);
double wblend = PtexUtils::smoothstep(vdist, vblendstart, vblendend);
ublendstart = ublendstart * wblend + adjstart * (1-wblend);
ublendend = ublendend * wblend + adjend * (1-wblend);
}
if (_uface.res.vlog2 != _face.res.vlog2) {
double texel = 1.0/_uface.res.v();
double adjstart = PtexUtils::min(blendstart*texel, .375);
double adjend = PtexUtils::min(blendend*texel, .5);
double wblend = PtexUtils::smoothstep(udist, ublendstart, ublendend);
vblendstart = vblendstart * wblend + adjstart * (1-wblend);
vblendend = vblendend * wblend + adjend * (1-wblend);
}
}
bool nearu = _uface && (udist < ublendend);
bool nearv = _vface && (vdist < vblendend);
if (!nearu) {
_ublend = 0;
_uface.clear();
}
else {
_ublend = 1 - PtexUtils::qsmoothstep(udist, ublendstart, ublendend);
if (_isConstant && !uf->isConstant()) _isConstant = 0;
}
if (!nearv) {
_vblend = 0;
_vface.clear();
}
else {
_vblend = 1 - PtexUtils::qsmoothstep(vdist, vblendstart, vblendend);
if (_isConstant && !vf->isConstant()) _isConstant = 0;
}
_interior = false;
if (nearu && nearv) {
_cfaces.reserve(8);
int cfid = ufid;
const FaceInfo* cf = uf;
EdgeId ceid = f.adjedge(ueid);
int rotate = _uface.rotate;
int dir = (ueid+1)%4==veid? 3 : 1;
int count = 0;
while (count++ < 10) {
int eid = EdgeId((ceid + dir) % 4);
cfid = cf->adjfaces[eid];
if (cfid == _vface.id || cfid == -1)
break;
ceid = cf->adjedge(eid);
cf = &_ctx.tx->getFaceInfo(cfid);
rotate += ceid - eid + 2;
_cfaces.push_back(Face());
Face& face = _cfaces.back();
face.set(cfid, cf->res, rotate);
if (_isConstant && !cf->isConstant()) _isConstant = 0;
}
if (cfid == _vface.id) {
_interior = true;
if (_cfaces.size() == 1) {
_cface = _cfaces.front();
_cface.clampres(_uface.res);
_cface.clampres(_vface.res);
if (_uface.blend || _vface.blend)
_cface.blend = true;
}
}
else {
_cfaces.clear();
}
}
if (_isConstant) {
int pixelsize = DataSize(_ctx.dt) * _ctx.ntxchannels;
PtexFaceData* data = _ctx.tx->getData(_face.id, 0);
if (data) {
void* constval = data->getData();
if (_uface) {
PtexFaceData* udata = _ctx.tx->getData(_uface.id, 0);
if (udata) {
if (0 != memcmp(constval, udata->getData(), pixelsize))
_isConstant = 0;
udata->release();
}
}
if (_isConstant && _vface) {
PtexFaceData* vdata = _ctx.tx->getData(_vface.id, 0);
if (vdata) {
if (0 != memcmp(constval, vdata->getData(), pixelsize))
_isConstant = 0;
vdata->release();
}
}
if (_isConstant) {
for (size_t i = 0, size = _cfaces.size(); i < size; i++) {
PtexFaceData* cdata = _ctx.tx->getData(_cfaces[i].id, 0);
if (cdata) {
if (0 != memcmp(constval, cdata->getData(), pixelsize)) {
_isConstant = 0;
break;
}
cdata->release();
}
}
}
data->release();
}
}
}
void PtexMitchellFilter::evalFaces(Res res, double weight, float uw, float vw)
{
int ures = res.u(), vres = res.v();
if (ures < 4 || vres < 4) {
PtexFilterKernel k;
k.set(0, 0, 0, 1, 1, &weight, 0);
k.apply(_face.id, 0, _ctx);
return;
}
double u = _ctx.u * ures - 0.5, v = _ctx.v * vres - 0.5;
uw *= ures; vw *= vres;
int u1 = int(ceil(u - 2*uw)), u2 = int(ceil(u + 2*uw));
int v1 = int(ceil(v - 2*vw)), v2 = int(ceil(v + 2*vw));
int kuw = u2-u1, kvw = v2-v1;
if (kuw > 8 || kvw > 8) {
assert(kuw <= 8 && kvw <= 8);
return;
}
double* ukernel = (double*)alloca(kuw * sizeof(double));
double* vkernel = (double*)alloca(kvw * sizeof(double));
computeWeights(ukernel, (u1-u)/uw, 1.0/uw, kuw);
computeWeights(vkernel, (v1-v)/vw, 1.0/vw, kvw);
double scale = weight;
while (!ukernel[0]) { ukernel++; u1++; kuw--; }
while (!ukernel[kuw-1]) { kuw--; }
while (!vkernel[0]) { vkernel++; v1++; kvw--; }
while (!vkernel[kvw-1]) { kvw--; }
double sumu = 0; for (int i = 0; i < kuw; i++) sumu += ukernel[i];
double sumv = 0; for (int i = 0; i < kvw; i++) sumv += vkernel[i];
scale /= sumu * sumv;
double* kbuffer = (double*) alloca(kuw*kvw*sizeof(double));
double* kp = kbuffer;
for (int i = 0; i < kvw; i++, kp += kuw) {
double vk = vkernel[i] * scale;
for (int j = 0; j < kuw; j++) kp[j] = ukernel[j]*vk;
}
PtexFilterKernel k; k.set(res, u1, v1, kuw, kvw, kbuffer, kuw);
PtexFilterKernel ku, kv, kc;
k.split(ku, kv, kc);
if (ku || kv) {
if (kc) {
if (!_cface && _interior) {
double amt = 1.0/(1 - kc.totalWeight()/weight);
kc.clear();
for (double *kp = kbuffer, *end = kbuffer + kuw*kvw; kp != end; kp++)
*kp *= amt;
}
else if (!_cface || !(_cface.res >= res)) {
if (kv && _uface) {
if (_vface) {
ku.merge(kc, kv.eidval(), 0.5);
kv.merge(kc, ku.eidval(), 0.5);
}
else {
ku.merge(kc, kv.eidval());
}
} else {
kv.merge(kc, ku.eidval());
}
}
}
if (ku && (!_uface || !(_uface.res >= res)))
k.merge(ku, ku.eidval());
if (kv && (!_vface || !(_vface.res >= res)))
k.merge(kv, kv.eidval());
if (ku) ku.apply(_uface.id, _uface.rotate, _ctx);
if (kv) kv.apply(_vface.id, _vface.rotate, _ctx);
if (kc) kc.apply(_cface.id, _cface.rotate, _ctx);
}
k.apply(_face.id, 0, _ctx);
}