Ptex

PtexSeparableFilter.cpp
Go to the documentation of this file.
1/*
2 PTEX SOFTWARE
3 Copyright 2014 Disney Enterprises, Inc. All rights reserved
4
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions are
7 met:
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
22 Disclaimer: THIS SOFTWARE IS PROVIDED BY WALT DISNEY PICTURES AND
23 CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
24 BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS
25 FOR A PARTICULAR PURPOSE, NONINFRINGEMENT AND TITLE ARE DISCLAIMED.
26 IN NO EVENT SHALL WALT DISNEY PICTURES, THE COPYRIGHT HOLDER OR
27 CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
28 EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
29 PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
30 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND BASED ON ANY
31 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
32(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
33 OF 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
45 PTEX_NAMESPACE_BEGIN
46
47 void 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
92 PtexSeparableKernel k;
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);
112 assert(k.uw <= PtexSeparableKernel::kmax && k.vw <= PtexSeparableKernel::kmax);
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
132 void PtexSeparableFilter::splitAndApply(PtexSeparableKernel& k, int faceid, const Ptex::FaceInfo& f)
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
212 void PtexSeparableFilter::applyAcrossEdge(PtexSeparableKernel& k,
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
254 void PtexSeparableFilter::applyToCorner(PtexSeparableKernel& k, int faceid,
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
328 void PtexSeparableFilter::applyToCornerFace(PtexSeparableKernel& k, const Ptex::FaceInfo& f, int eid,
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
345 void PtexSeparableFilter::apply(PtexSeparableKernel& k, int faceid, const Ptex::FaceInfo& f)
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();
381 PtexSeparableKernel kt;
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
434 PTEX_NAMESPACE_END
PtexPlatform.h
Platform-specific classes, functions, and includes.
PtexSeparableFilter.h
PtexSeparableKernel.h
PtexUtils.h
PTEX_NAMESPACE_END
#define PTEX_NAMESPACE_END
Definition PtexVersion.h:62
PtexPtr
Smart-pointer for acquiring and releasing API objects.
Definition Ptexture.h:1067
PtexSeparableFilter::splitAndApply
void splitAndApply(PtexSeparableKernel &k, int faceid, const Ptex::FaceInfo &f)
Definition PtexSeparableFilter.cpp:132
PtexSeparableFilter::applyToCornerFace
void applyToCornerFace(PtexSeparableKernel &k, const Ptex::FaceInfo &f, int eid, int cfaceid, const Ptex::FaceInfo &cf, int ceid)
Definition PtexSeparableFilter.cpp:328
PtexSeparableFilter::eval
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.
Definition PtexSeparableFilter.cpp:47
PtexSeparableFilter::apply
void apply(PtexSeparableKernel &k, int faceid, const Ptex::FaceInfo &f)
Definition PtexSeparableFilter.cpp:345
PtexSeparableFilter::applyAcrossEdge
void applyAcrossEdge(PtexSeparableKernel &k, int faceid, const Ptex::FaceInfo &f, int eid)
Definition PtexSeparableFilter.cpp:212
PtexSeparableFilter::_result
float * _result
Definition PtexSeparableFilter.h:80
PtexSeparableFilter::_tx
PtexTexture * _tx
Definition PtexSeparableFilter.h:78
PtexSeparableFilter::_options
Options _options
Definition PtexSeparableFilter.h:79
PtexSeparableFilter::_uMode
BorderMode _uMode
Definition PtexSeparableFilter.h:86
PtexSeparableFilter::applyToCorner
void applyToCorner(PtexSeparableKernel &k, int faceid, const Ptex::FaceInfo &f, int eid)
Definition PtexSeparableFilter.cpp:254
PtexSeparableFilter::_ntxchan
int _ntxchan
Definition PtexSeparableFilter.h:84
PtexSeparableFilter::_weight
float _weight
Definition PtexSeparableFilter.h:81
PtexSeparableFilter::_firstChanOffset
int _firstChanOffset
Definition PtexSeparableFilter.h:82
PtexSeparableFilter::_efm
EdgeFilterMode _efm
Definition PtexSeparableFilter.h:87
PtexSeparableFilter::_dt
DataType _dt
Definition PtexSeparableFilter.h:85
PtexSeparableFilter::_vMode
BorderMode _vMode
Definition PtexSeparableFilter.h:86
PtexSeparableFilter::buildKernel
virtual void buildKernel(PtexSeparableKernel &k, float u, float v, float uw, float vw, Res faceRes)=0
PtexSeparableFilter::_nchan
int _nchan
Definition PtexSeparableFilter.h:83
PtexSeparableKernel
Definition PtexSeparableKernel.h:48
PtexSeparableKernel::mergeT
void mergeT(BorderMode mode)
Definition PtexSeparableKernel.h:139
PtexSeparableKernel::mergeB
void mergeB(BorderMode mode)
Definition PtexSeparableKernel.h:129
PtexSeparableKernel::stripZeros
void stripZeros()
Definition PtexSeparableKernel.h:96
PtexSeparableKernel::apply
void apply(float *dst, void *data, DataType dt, int nChan, int nTxChan)
Definition PtexSeparableKernel.h:457
PtexSeparableKernel::makeSymmetric
float makeSymmetric(float initialWeight)
Definition PtexSeparableKernel.h:397
PtexSeparableKernel::u
int u
Definition PtexSeparableKernel.h:51
PtexSeparableKernel::kv
float * kv
Definition PtexSeparableKernel.h:54
PtexSeparableKernel::splitT
void splitT(PtexSeparableKernel &k)
Definition PtexSeparableKernel.h:214
PtexSeparableKernel::rot
int rot
Definition PtexSeparableKernel.h:58
PtexSeparableKernel::res
Res res
Definition PtexSeparableKernel.h:50
PtexSeparableKernel::adjustSubfaceToMain
void adjustSubfaceToMain(int eid)
Definition PtexSeparableKernel.h:302
PtexSeparableKernel::splitB
void splitB(PtexSeparableKernel &k)
Definition PtexSeparableKernel.h:192
PtexSeparableKernel::vw
int vw
Definition PtexSeparableKernel.h:52
PtexSeparableKernel::v
int v
Definition PtexSeparableKernel.h:51
PtexSeparableKernel::splitL
void splitL(PtexSeparableKernel &k)
Definition PtexSeparableKernel.h:148
PtexSeparableKernel::downresV
void downresV()
Definition PtexSeparableKernel.h:342
PtexSeparableKernel::ku
float * ku
Definition PtexSeparableKernel.h:53
PtexSeparableKernel::uw
int uw
Definition PtexSeparableKernel.h:52
PtexSeparableKernel::mergeR
void mergeR(BorderMode mode)
Definition PtexSeparableKernel.h:120
PtexSeparableKernel::applyConst
void applyConst(float *dst, void *data, DataType dt, int nChan)
Definition PtexSeparableKernel.h:464
PtexSeparableKernel::adjustMainToSubface
bool adjustMainToSubface(int eid)
Definition PtexSeparableKernel.h:266
PtexSeparableKernel::mergeL
void mergeL(BorderMode mode)
Definition PtexSeparableKernel.h:110
PtexSeparableKernel::upresV
void upresV()
Definition PtexSeparableKernel.h:384
PtexSeparableKernel::upresU
void upresU()
Definition PtexSeparableKernel.h:371
PtexSeparableKernel::splitR
void splitR(PtexSeparableKernel &k)
Definition PtexSeparableKernel.h:171
PtexSeparableKernel::kmax
static const int kmax
Definition PtexSeparableKernel.h:55
PtexSeparableKernel::rotate
void rotate(int rotVal)
Definition PtexSeparableKernel.h:254
PtexSeparableKernel::weight
float weight() const
Definition PtexSeparableKernel.h:105
PtexSeparableKernel::downresU
void downresU()
Definition PtexSeparableKernel.h:313
PTEX_NAMESPACE_BEGIN
Definition PtexSeparableKernel.cpp:42
Ptex::ConvertToFloat
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.
Ptex::FaceInfo
Information about a face, as stored in the Ptex file header.
Definition Ptexture.h:232
Ptex::FaceInfo::isSubface
bool isSubface() const
Determine if face is a subface (by checking a flag).
Definition Ptexture.h:274
Ptex::FaceInfo::res
Res res
Resolution of face.
Definition Ptexture.h:233
Ptex::FaceInfo::adjedge
EdgeId adjedge(int eid) const
Access an adjacent edge id. The eid value must be 0..3.
Definition Ptexture.h:259
Ptex::FaceInfo::isConstant
bool isConstant() const
Determine if face is constant (by checking a flag).
Definition Ptexture.h:265
Ptex::FaceInfo::adjface
int adjface(int eid) const
Access an adjacent face id. The eid value must be 0..3.
Definition Ptexture.h:262
Ptex::Res
Pixel resolution of a given texture.
Definition Ptexture.h:159
Ptex::Res::ulog2
int8_t ulog2
log base 2 of u resolution, in texels
Definition Ptexture.h:160
Ptex::Res::v
int v() const
V resolution in texels.
Definition Ptexture.h:176
Ptex::Res::u
int u() const
U resolution in texels.
Definition Ptexture.h:173
Ptex::Res::vlog2
int8_t vlog2
log base 2 of v resolution, in texels
Definition Ptexture.h:161