Ptex
PtexSeparableFilter.cpp
Go to the documentation of this file.
1/*
2PTEX SOFTWARE
3Copyright 2014 Disney Enterprises, Inc. All rights reserved
4
5Redistribution and use in source and binary forms, with or without
6modification, are permitted provided that the following conditions are
7met:
8
9 * Redistributions of source code must retain the above copyright
10 notice, this list of conditions and the following disclaimer.
11
12 * Redistributions in binary form must reproduce the above copyright
13 notice, this list of conditions and the following disclaimer in
14 the documentation and/or other materials provided with the
15 distribution.
16
17 * The names "Disney", "Walt Disney Pictures", "Walt Disney Animation
18 Studios" or the names of its contributors may NOT be used to
19 endorse or promote products derived from this software without
20 specific prior written permission from Walt Disney Pictures.
21
22Disclaimer: THIS SOFTWARE IS PROVIDED BY WALT DISNEY PICTURES AND
23CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
24BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS
25FOR A PARTICULAR PURPOSE, NONINFRINGEMENT AND TITLE ARE DISCLAIMED.
26IN NO EVENT SHALL WALT DISNEY PICTURES, THE COPYRIGHT HOLDER OR
27CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
28EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
29PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
30PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND BASED ON ANY
31THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
32(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
33OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
34*/
35
36#include "PtexPlatform.h"
37#include <cmath>
38#include <assert.h>
39
40#include "PtexSeparableFilter.h"
41#include "PtexSeparableKernel.h"
42#include "PtexUtils.h"
43
44
46
47void PtexSeparableFilter::eval(float* result, int firstChan, int nChannels,
48 int faceid, float u, float v,
49 float uw1, float vw1, float uw2, float vw2,
50 float width, float blur)
51{
52 // init
53 if (!_tx || nChannels <= 0) return;
54 if (faceid < 0 || faceid >= _tx->numFaces()) return;
55 _firstChanOffset = firstChan*DataSize(_dt);
56 _nchan = std::min(nChannels, _ntxchan-firstChan);
57
58 // get face info
59 const FaceInfo& f = _tx->getFaceInfo(faceid);
60
61 // if neighborhood is constant, just return constant value of face
62 if (f.isNeighborhoodConstant()) {
63 char* d = (char*) _tx->getConstantData(faceid) + _firstChanOffset;
64 Ptex::ConvertToFloat(result, d, _dt, _nchan);
65 return;
66 }
67
68 // find filter width as bounding box of vectors w1 and w2
69 float uw = std::abs(uw1) + std::abs(uw2), vw = std::abs(vw1) + std::abs(vw2);
70
71 // handle border modes
72 bool return_black = false;
73
74 switch (_uMode) {
75 case m_clamp: u = std::clamp(u, 0.0f, 1.0f); break;
76 case m_periodic: u = u-std::floor(u); break;
77 case m_black: if (u <= -1.0f || u >= 2.0f) return_black = true; break;
78 }
79
80 switch (_vMode) {
81 case m_clamp: v = std::clamp(v, 0.0f, 1.0f); break;
82 case m_periodic: v = v-std::floor(v); break;
83 case m_black: if (v <= -1.0f || v >= 2.0f) return_black = true; break;
84 }
85
86 if (return_black) {
87 memset(result, 0, sizeof(float)*_nchan);
88 return;
89 }
90
91 // build kernel
93 if (f.isSubface()) {
94 // for a subface, build the kernel as if it were on a main face and then downres
95 uw = uw * width + blur * 2.0f;
96 vw = vw * width + blur * 2.0f;
97 buildKernel(k, u*.5f, v*.5f, uw*.5f, vw*.5f,
98 Ptex::Res((int8_t)(f.res.ulog2+1),(int8_t)(f.res.vlog2+1)));
99 if (k.res.ulog2 == 0) k.upresU();
100 if (k.res.vlog2 == 0) k.upresV();
101 k.res.ulog2--; k.res.vlog2--;
102 }
103 else {
104 uw = uw * width + blur;
105 vw = vw * width + blur;
106 buildKernel(k, u, v, uw, vw, f.res);
107 }
108 k.stripZeros();
109
110 // check kernel (debug only)
111 assert(k.uw > 0 && k.vw > 0);
113 _weight = k.weight();
114
115 // allocate temporary result
116 _result = (float*) alloca(sizeof(float)*_nchan);
117 memset(_result, 0, sizeof(float)*_nchan);
118
119 // apply to faces
120 splitAndApply(k, faceid, f);
121
122 // normalize (both for data type and cumulative kernel weight applied)
123 // and output result
124 float scale = 1.0f / (_weight * OneValue(_dt));
125 for (int i = 0; i < _nchan; i++) result[i] = float(_result[i] * scale);
126
127 // clear temp result
128 _result = 0;
129}
130
131
133{
134 // do we need to split? (i.e. does kernel span an edge?)
135 bool splitR = (k.u+k.uw > k.res.u()), splitL = (k.u < 0);
136 bool splitT = (k.v+k.vw > k.res.v()), splitB = (k.v < 0);
137
138 if (_options.noedgeblend) {
139 if (splitR) k.mergeR(_uMode);
140 if (splitL) k.mergeL(_uMode);
141 if (splitT) k.mergeT(_vMode);
142 if (splitB) k.mergeB(_vMode);
143 apply(k, faceid, f);
144 return;
145 }
146
147 if (splitR || splitL || splitT || splitB) {
148 PtexSeparableKernel ka, kc;
149 if (splitR) {
150 if (f.adjface(e_right) >= 0) {
151 k.splitR(ka);
152 if (splitT) {
153 if (f.adjface(e_top) >= 0) {
154 ka.splitT(kc);
155 applyToCorner(kc, faceid, f, e_top);
156 }
157 else ka.mergeT(_vMode);
158 }
159 if (splitB) {
160 if (f.adjface(e_bottom) >= 0) {
161 ka.splitB(kc);
162 applyToCorner(kc, faceid, f, e_right);
163 }
164 else ka.mergeB(_vMode);
165 }
166 applyAcrossEdge(ka, faceid, f, e_right);
167 }
168 else k.mergeR(_uMode);
169 }
170 if (splitL) {
171 if (f.adjface(e_left) >= 0) {
172 k.splitL(ka);
173 if (splitT) {
174 if (f.adjface(e_top) >= 0) {
175 ka.splitT(kc);
176 applyToCorner(kc, faceid, f, e_left);
177 }
178 else ka.mergeT(_vMode);
179 }
180 if (splitB) {
181 if (f.adjface(e_bottom) >= 0) {
182 ka.splitB(kc);
183 applyToCorner(kc, faceid, f, e_bottom);
184 }
185 else ka.mergeB(_vMode);
186 }
187 applyAcrossEdge(ka, faceid, f, e_left);
188 }
189 else k.mergeL(_uMode);
190 }
191 if (splitT) {
192 if (f.adjface(e_top) >= 0) {
193 k.splitT(ka);
194 applyAcrossEdge(ka, faceid, f, e_top);
195 }
196 else k.mergeT(_vMode);
197 }
198 if (splitB) {
199 if (f.adjface(e_bottom) >= 0) {
200 k.splitB(ka);
201 applyAcrossEdge(ka, faceid, f, e_bottom);
202 }
203 else k.mergeB(_vMode);
204 }
205 }
206
207 // do local face
208 apply(k, faceid, f);
209}
210
211
213 int faceid, const Ptex::FaceInfo& f, int eid)
214{
215 int afid = f.adjface(eid), aeid = f.adjedge(eid);
216 const Ptex::FaceInfo* af = &_tx->getFaceInfo(afid);
217 int rot = eid - aeid + 2;
218
219 // adjust uv coord and res for face/subface boundary
220 bool fIsSubface = f.isSubface(), afIsSubface = af->isSubface();
221 if (fIsSubface != afIsSubface) {
222 if (afIsSubface) {
223 // main face to subface transition
224 // adjust res and offset uv coord for primary subface
225 bool primary = k.adjustMainToSubface(eid);
226 if (!primary) {
227 // advance ajacent face and edge id to secondary subface
228 int neid = (aeid + 3) % 4;
229 afid = af->adjface(neid);
230 aeid = af->adjedge(neid);
231 af = &_tx->getFaceInfo(afid);
232 rot += neid - aeid + 2;
233 }
234 }
235 else {
236 // subface to main face transition
237 // Note: the transform depends on which subface the kernel is
238 // coming from. The "primary" subface is the one the main
239 // face is pointing at. The secondary subface adjustment
240 // happens to be the same as for the primary subface for the
241 // next edge, so the cases can be combined.
242 bool primary = (af->adjface(aeid) == faceid);
243 k.adjustSubfaceToMain(eid - primary);
244 }
245 }
246
247 // rotate and apply (resplit if going to a subface)
248 k.rotate(rot);
249 if (afIsSubface) splitAndApply(k, afid, *af);
250 else apply(k, afid, *af);
251}
252
253
255 const Ptex::FaceInfo& f, int eid)
256{
257 // traverse clockwise around corner vertex and gather corner faces
258 int afid = faceid, aeid = eid;
259 const FaceInfo* af = &f;
260 bool prevIsSubface = af->isSubface();
261
262 const int MaxValence = 10;
263 int cfaceId[MaxValence];
264 int cedgeId[MaxValence];
265 const FaceInfo* cface[MaxValence];
266
267 int numCorners = 0;
268 for (int i = 0; i < MaxValence; i++) {
269 // advance to next face
270 int prevFace = afid;
271 afid = af->adjface(aeid);
272 aeid = (af->adjedge(aeid) + 1) % 4;
273
274 // we hit a boundary or reached starting face
275 // note: we need to check edge id too because we might have
276 // a periodic texture (w/ toroidal topology) where all 4 corners
277 // are from the same face
278 if (afid < 0 || (afid == faceid && aeid == eid)) {
279 numCorners = i - 2;
280 break;
281 }
282
283 // record face info
284 af = &_tx->getFaceInfo(afid);
285 cfaceId[i] = afid;
286 cedgeId[i] = aeid;
287 cface[i] = af;
288
289 // check to see if corner is a subface "tee"
290 bool isSubface = af->isSubface();
291 if (prevIsSubface && !isSubface && af->adjface((aeid+3)%4) == prevFace)
292 {
293 // adjust the eid depending on whether we started from
294 // the primary or secondary subface.
295 bool primary = (i==1);
296 k.adjustSubfaceToMain(eid + primary * 2);
297 k.rotate(eid - aeid + 3 - primary);
298 splitAndApply(k, afid, *af);
299 return;
300 }
301 prevIsSubface = isSubface;
302 }
303
304 if (numCorners == 1) {
305 // regular case (valence 4)
306 applyToCornerFace(k, f, eid, cfaceId[1], *cface[1], cedgeId[1]);
307 }
308 else if (numCorners > 1) {
309 // valence 5+, make kernel symmetric and apply equally to each face
310 // first, rotate to standard orientation, u=v=0
311 k.rotate(eid + 2);
312 float initialWeight = k.weight();
313 float newWeight = k.makeSymmetric(initialWeight);
314 for (int i = 1; i <= numCorners; i++) {
315 PtexSeparableKernel kc = k;
316 applyToCornerFace(kc, f, 2, cfaceId[i], *cface[i], cedgeId[i]);
317 }
318 // adjust weight for symmetrification and for additional corners
319 _weight += newWeight * (float)numCorners - initialWeight;
320 }
321 else {
322 // valence 2 or 3, ignore corner face (just adjust weight)
323 _weight -= k.weight();
324 }
325}
326
327
329 int cfid, const Ptex::FaceInfo& cf, int ceid)
330{
331 // adjust uv coord and res for face/subface boundary
332 bool fIsSubface = f.isSubface(), cfIsSubface = cf.isSubface();
333 if (fIsSubface != cfIsSubface) {
334 if (cfIsSubface) k.adjustMainToSubface(eid + 3);
335 else k.adjustSubfaceToMain(eid + 3);
336 }
337
338 // rotate and apply (resplit if going to a subface)
339 k.rotate(eid - ceid + 2);
340 if (cfIsSubface) splitAndApply(k, cfid, cf);
341 else apply(k, cfid, cf);
342}
343
344
346{
347 assert(k.u >= 0 && k.u + k.uw <= k.res.u());
348 assert(k.v >= 0 && k.v + k.vw <= k.res.v());
349
350 if (k.uw <= 0 || k.vw <= 0) return;
351
352 if (f.isConstant() ||
353 ( (k.res.ulog2 == 0 || f.res.ulog2 == 0) &&
354 (k.res.vlog2 == 0 || f.res.vlog2 == 0) ))
355 {
356 // texture is constant, or kernel will be after downresing to texture res
357 k.applyConst(_result, (char*)_tx->getConstantData(faceid) +_firstChanOffset, _dt, _nchan);
358 return;
359 }
360
361 // downres kernel if needed
362 while (k.res.ulog2 > f.res.ulog2) k.downresU();
363 while (k.res.vlog2 > f.res.vlog2) k.downresV();
364
365 // get face data, and apply
366 PtexPtr<PtexFaceData> dh ( _tx->getData(faceid, k.res) );
367 if (!dh) return;
368
369 if (dh->isConstant()) {
370 k.applyConst(_result, (char*)dh->getData()+_firstChanOffset, _dt, _nchan);
371 return;
372 }
373
374 // allocate temporary result for tanvec mode (if needed)
375 bool tanvecMode = (_efm == efm_tanvec) && (_nchan >= 2) && (k.rot > 0);
376 float* result = tanvecMode ? (float*) alloca(sizeof(float)*_nchan) : _result;
377 if (tanvecMode) memset(result, 0, sizeof(float)*_nchan);
378
379 if (dh->isTiled()) {
380 Ptex::Res tileres = dh->tileRes();
382 kt.res = tileres;
383 int tileresu = tileres.u();
384 int tileresv = tileres.v();
385 int ntilesu = k.res.u() / tileresu;
386 for (int v = k.v, vw = k.vw; vw > 0; vw -= kt.vw, v += kt.vw) {
387 int tilev = v / tileresv;
388 kt.v = v % tileresv;
389 kt.vw = std::min(vw, tileresv - kt.v);
390 kt.kv = k.kv + v - k.v;
391 for (int u = k.u, uw = k.uw; uw > 0; uw -= kt.uw, u += kt.uw) {
392 int tileu = u / tileresu;
393 kt.u = u % tileresu;
394 kt.uw = std::min(uw, tileresu - kt.u);
395 kt.ku = k.ku + u - k.u;
396 PtexPtr<PtexFaceData> th ( dh->getTile(tilev * ntilesu + tileu) );
397 if (th) {
398 if (th->isConstant())
399 kt.applyConst(result, (char*)th->getData()+_firstChanOffset, _dt, _nchan);
400 else
401 kt.apply(result, (char*)th->getData()+_firstChanOffset, _dt, _nchan, _ntxchan);
402 }
403 }
404 }
405 }
406 else {
407 k.apply(result, (char*)dh->getData()+_firstChanOffset, _dt, _nchan, _ntxchan);
408 }
409
410 if (tanvecMode) {
411 // rotate tangent-space vector data and update main result
412 switch (k.rot) {
413 case 0: // rot==0 included for completeness, but tanvecMode should be false in this case
414 _result[0] += result[0];
415 _result[1] += result[1];
416 break;
417 case 1:
418 _result[0] -= result[1];
419 _result[1] += result[0];
420 break;
421 case 2:
422 _result[0] -= result[0];
423 _result[1] -= result[1];
424 break;
425 case 3:
426 _result[0] += result[1];
427 _result[1] -= result[0];
428 break;
429 }
430 for (int i = 2; i < _nchan; i++) _result[i] += result[i];
431 }
432}
433
Platform-specific classes, functions, and includes.
#define PTEX_NAMESPACE_END
Definition PtexVersion.h:62
Smart-pointer for acquiring and releasing API objects.
Definition Ptexture.h:1067
void splitAndApply(PtexSeparableKernel &k, int faceid, const Ptex::FaceInfo &f)
void applyToCornerFace(PtexSeparableKernel &k, const Ptex::FaceInfo &f, int eid, int cfaceid, const Ptex::FaceInfo &cf, int ceid)
virtual void 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)
Apply filter to a ptex data file.
void apply(PtexSeparableKernel &k, int faceid, const Ptex::FaceInfo &f)
void applyAcrossEdge(PtexSeparableKernel &k, int faceid, const Ptex::FaceInfo &f, int eid)
void applyToCorner(PtexSeparableKernel &k, int faceid, const Ptex::FaceInfo &f, int eid)
virtual void buildKernel(PtexSeparableKernel &k, float u, float v, float uw, float vw, Res faceRes)=0
void mergeT(BorderMode mode)
void mergeB(BorderMode mode)
void apply(float *dst, void *data, DataType dt, int nChan, int nTxChan)
float makeSymmetric(float initialWeight)
void splitT(PtexSeparableKernel &k)
void adjustSubfaceToMain(int eid)
void splitB(PtexSeparableKernel &k)
void splitL(PtexSeparableKernel &k)
void mergeR(BorderMode mode)
void applyConst(float *dst, void *data, DataType dt, int nChan)
bool adjustMainToSubface(int eid)
void mergeL(BorderMode mode)
void splitR(PtexSeparableKernel &k)
void ConvertToFloat(float *dst, const void *src, Ptex::DataType dt, int numChannels)
Convert a number of data values from the given data type to float.
Information about a face, as stored in the Ptex file header.
Definition Ptexture.h:232
bool isSubface() const
Determine if face is a subface (by checking a flag).
Definition Ptexture.h:274
Res res
Resolution of face.
Definition Ptexture.h:233
EdgeId adjedge(int eid) const
Access an adjacent edge id. The eid value must be 0..3.
Definition Ptexture.h:259
bool isConstant() const
Determine if face is constant (by checking a flag).
Definition Ptexture.h:265
int adjface(int eid) const
Access an adjacent face id. The eid value must be 0..3.
Definition Ptexture.h:262
Pixel resolution of a given texture.
Definition Ptexture.h:159
int8_t ulog2
log base 2 of u resolution, in texels
Definition Ptexture.h:160
int v() const
V resolution in texels.
Definition Ptexture.h:176
int u() const
U resolution in texels.
Definition Ptexture.h:173
int8_t vlog2
log base 2 of v resolution, in texels
Definition Ptexture.h:161