Ptex: PtexSeparableKernel.h Source File

Ptex

- Main Page
- Related Pages
- Namespaces
- Classes
- Files

- File List
- File Members

PtexSeparableKernel.h
Go to the documentation of this file.
1 #ifndef PtexSeparableKernel_h
2 #define PtexSeparableKernel_h
3
4 /*
5 PTEX SOFTWARE
6 Copyright 2009 Disney Enterprises, Inc. All rights reserved
7
8 Redistribution and use in source and binary forms, with or without
9 modification, are permitted provided that the following conditions are
10 met:
11
12 * Redistributions of source code must retain the above copyright
13 notice, this list of conditions and the following disclaimer.
14
15 * Redistributions in binary form must reproduce the above copyright
16 notice, this list of conditions and the following disclaimer in
17 the documentation and/or other materials provided with the
18 distribution.
19
20 * The names "Disney", "Walt Disney Pictures", "Walt Disney Animation
21 Studios" or the names of its contributors may NOT be used to
22 endorse or promote products derived from this software without
23 specific prior written permission from Walt Disney Pictures.
24
25 Disclaimer: THIS SOFTWARE IS PROVIDED BY WALT DISNEY PICTURES AND
26 CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
27 BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS
28 FOR A PARTICULAR PURPOSE, NONINFRINGEMENT AND TITLE ARE DISCLAIMED.
29 IN NO EVENT SHALL WALT DISNEY PICTURES, THE COPYRIGHT HOLDER OR
30 CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
31 EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
32 PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
33 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND BASED ON ANY
34 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
35 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
36 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
37 */
38
39 #include <assert.h>
40 #include <algorithm>
41 #include <numeric>
42 #include "Ptexture.h"
43 #include "PtexUtils.h"
44
45 // Separable convolution kernel
46 class PtexSeparableKernel : public Ptex {
47 public:
48 Res res; // resolution that kernel was built for
49 int u, v; // uv offset within face data
50 int uw, vw; // kernel width
51 float* ku; // kernel weights in u
52 float* kv; // kernel weights in v
53 static const int kmax = 10; // max kernel width
54 float kubuff[kmax];
55 float kvbuff[kmax];
56
57 PtexSeparableKernel()
58 : res(0), u(0), v(0), uw(0), vw(0), ku(kubuff), kv(kvbuff) {}
59
60 PtexSeparableKernel(const PtexSeparableKernel& k)
61 {
62 set(k.res, k.u, k.v, k.uw, k.vw, k.ku, k.kv);
63 }
64
65 PtexSeparableKernel& operator= (const PtexSeparableKernel& k)
66 {
67 set(k.res, k.u, k.v, k.uw, k.vw, k.ku, k.kv);
68 return *this;
69 }
70
71 void set(Res resVal,
72 int uVal, int vVal,
73 int uwVal, int vwVal,
74 const float* kuVal, const float* kvVal)
75 {
76 assert(uwVal <= kmax && vwVal <= kmax);
77 res = resVal;
78 u = uVal;
79 v = vVal;
80 uw = uwVal;
81 vw = vwVal;
82 memcpy(kubuff, kuVal, sizeof(*ku)*uw);
83 memcpy(kvbuff, kvVal, sizeof(*kv)*vw);
84 ku = kubuff;
85 kv = kvbuff;
86 }
87
88 void stripZeros()
89 {
90 while (ku[0] == 0) { ku++; u++; uw--; }
91 while (ku[uw-1] == 0) { uw--; }
92 while (kv[0] == 0) { kv++; v++; vw--; }
93 while (kv[vw-1] == 0) { vw--; }
94 assert(uw > 0 && vw > 0);
95 }
96
97 float weight() const
98 {
99 return accumulate(ku, uw) * accumulate(kv, vw);
100 }
101
102 void mergeL(BorderMode mode)
103 {
104 int w = -u;
105 if (mode != m_black)
106 ku[w] += accumulate(ku, w);
107 ku += w;
108 uw -= w;
109 u = 0;
110 }
111
112 void mergeR(BorderMode mode)
113 {
114 int w = uw + u - res.u();
115 float* kp = ku + uw - w;
116 if (mode != m_black)
117 kp[-1] += accumulate(kp, w);
118 uw -= w;
119 }
120
121 void mergeB(BorderMode mode)
122 {
123 int w = -v;
124 if (mode != m_black)
125 kv[w] += accumulate(kv, w);
126 kv += w;
127 vw -= w;
128 v = 0;
129 }
130
131 void mergeT(BorderMode mode)
132 {
133 int w = vw + v - res.v();
134 float* kp = kv + vw - w;
135 if (mode != m_black)
136 kp[-1] += accumulate(kp, w);
137 vw -= w;
138 }
139
140 void splitL(PtexSeparableKernel& k)
141 {
142 // split off left piece of width w into k
143 int w = -u;
144
145 if (w < uw) {
146 // normal case - split off a portion
147 // res u v uw vw ku kv
148 k.set(res, res.u()-w, v, w, vw, ku, kv);
149
150 // update local
151 u = 0;
152 uw -= w;
153 ku += w;
154 }
155 else {
156 // entire kernel is split off
157 k = *this;
158 k.u += res.u();
159 u = 0; uw = 0;
160 }
161 }
162
163 void splitR(PtexSeparableKernel& k)
164 {
165 // split off right piece of width w into k
166 int w = u + uw - res.u();
167
168 if (w < uw) {
169 // normal case - split off a portion
170 // res u v uw vw ku kv
171 k.set(res, 0, v, w, vw, ku + uw - w, kv);
172
173 // update local
174 uw -= w;
175 }
176 else {
177 // entire kernel is split off
178 k = *this;
179 k.u -= res.u();
180 u = 0; uw = 0;
181 }
182 }
183
184 void splitB(PtexSeparableKernel& k)
185 {
186 // split off bottom piece of width w into k
187 int w = -v;
188 if (w < vw) {
189 // normal case - split off a portion
190 // res u v uw vw ku kv
191 k.set(res, u, res.v()-w, uw, w, ku, kv);
192
193 // update local
194 v = 0;
195 vw -= w;
196 kv += w;
197 }
198 else {
199 // entire kernel is split off
200 k = *this;
201 k.v += res.v();
202 v = 0; vw = 0;
203 }
204 }
205
206 void splitT(PtexSeparableKernel& k)
207 {
208 // split off top piece of width w into k
209 int w = v + vw - res.v();
210 if (w < vw) {
211 // normal case - split off a portion
212 // res u v uw vw ku kv
213 k.set(res, u, 0, uw, w, ku, kv + vw - w);
214
215 // update local
216 vw -= w;
217 }
218 else {
219 // entire kernel is split off
220 k = *this;
221 k.v -= res.v();
222 v = 0; vw = 0;
223 }
224 }
225
226 void flipU()
227 {
228 u = res.u() - u - uw;
229 std::reverse(ku, ku+uw);
230 }
231
232 void flipV()
233 {
234 v = res.v() - v - vw;
235 std::reverse(kv, kv+vw);
236 }
237
238 void swapUV()
239 {
240 res.swapuv();
241 std::swap(u, v);
242 std::swap(uw, vw);
243 std::swap(ku, kv);
244 }
245
246 void rotate(int rot)
247 {
248 // rotate kernel 'rot' steps ccw
249 switch (rot & 3) {
250 default: return;
251 case 1: flipU(); swapUV(); break;
252 case 2: flipU(); flipV(); break;
253 case 3: flipV(); swapUV(); break;
254 }
255 }
256
257 bool adjustMainToSubface(int eid)
258 {
259 // to adjust the kernel for the subface, we must adjust the res down and offset the uv coords
260 // however, if the res is already zero, we must upres the kernel first
261 if (res.ulog2 == 0) upresU();
262 if (res.vlog2 == 0) upresV();
263
264 if (res.ulog2 > 0) res.ulog2--;
265 if (res.vlog2 > 0) res.vlog2--;
266
267 // offset uv coords and determine whether target subface is the primary one
268 bool primary = 0;
269 int resu = res.u(), resv = res.v();
270 switch (eid&3) {
271 case e_bottom:
272 primary = (u < resu);
273 v -= resv;
274 if (!primary) u -= resu;
275 break;
276 case e_right:
277 primary = (v < resv);
278 if (!primary) v -= resv;
279 break;
280 case e_top:
281 primary = (u >= resu);
282 if (primary) u -= resu;
283 break;
284 case e_left:
285 primary = (v >= resv);
286 u -= resu;
287 if (primary) v -= resv;
288 break;
289 }
290 return primary;
291 }
292
293 void adjustSubfaceToMain(int eid)
294 {
295 switch (eid&3) {
296 case e_bottom: v += res.v(); break;
297 case e_right: break;
298 case e_top: u += res.u(); break;
299 case e_left: u += res.u(); v += res.v(); break;
300 }
301 res.ulog2++; res.vlog2++;
302 }
303
304 void downresU()
305 {
306 float* src = ku;
307 float* dst = ku;
308
309 // skip odd leading sample (if any)
310 if (u & 1) {
311 dst++;
312 src++;
313 uw--;
314 }
315
316 // combine even pairs
317 for (int i = uw/2; i > 0; i--) {
318 *dst++ = src[0] + src[1];
319 src += 2;
320 }
321
322 // copy odd trailing sample (if any)
323 if (uw & 1) {
324 *dst++ = *src++;
325 }
326
327 // update state
328 u /= 2;
329 uw = int(dst - ku);
330 res.ulog2--;
331 }
332
333 void downresV()
334 {
335 float* src = kv;
336 float* dst = kv;
337
338 // skip odd leading sample (if any)
339 if (v & 1) {
340 dst++;
341 src++;
342 vw--;
343 }
344
345 // combine even pairs
346 for (int i = vw/2; i > 0; i--) {
347 *dst++ = src[0] + src[1];
348 src += 2;
349 }
350
351 // copy odd trailing sample (if any)
352 if (vw & 1) {
353 *dst++ = *src++;
354 }
355
356 // update state
357 v /= 2;
358 vw = int(dst - kv);
359 res.vlog2--;
360 }
361
362 void upresU()
363 {
364 float* src = ku + uw-1;
365 float* dst = ku + uw*2-2;
366 for (int i = uw; i > 0; i--) {
367 dst[0] = dst[1] = *src-- / 2;
368 dst -=2;
369 }
370 uw *= 2;
371 u *= 2;
372 res.ulog2++;
373 }
374
375 void upresV()
376 {
377 float* src = kv + vw-1;
378 float* dst = kv + vw*2-2;
379 for (int i = vw; i > 0; i--) {
380 dst[0] = dst[1] = *src-- / 2;
381 dst -=2;
382 }
383 vw *= 2;
384 v *= 2;
385 res.vlog2++;
386 }
387
388 float makeSymmetric(float initialWeight)
389 {
390 assert(u == 0 && v == 0);
391
392 // downres higher-res dimension until equal
393 if (res.ulog2 > res.vlog2) {
394 do { downresU(); } while(res.ulog2 > res.vlog2);
395 }
396 else if (res.vlog2 > res.ulog2) {
397 do { downresV(); } while (res.vlog2 > res.ulog2);
398 }
399
400 // truncate excess samples in longer dimension
401 uw = vw = PtexUtils::min(uw, vw);
402
403 // combine corresponding u and v samples and compute new kernel weight
404 float newWeight = 0;
405 for (int i = 0; i < uw; i++) {
406 float sum = ku[i] + kv[i];
407 ku[i] = kv[i] = sum;
408 newWeight += sum;
409 }
410 newWeight *= newWeight; // equivalent to k.weight() ( = sum(ku)*sum(kv) )
411
412 // compute scale factor to compensate for weight change
413 float scale = newWeight == 0 ? 1.f : initialWeight / newWeight;
414
415 // Note: a sharpening kernel (like Mitchell) can produce
416 // negative weights which may cancel out when adding the two
417 // kernel axes together, and this can cause the compensation
418 // scale factor to spike up. We expect the scale factor to be
419 // less than one in "normal" cases (i.e. ku*kv <= (ku+kv)^2 if ku
420 // and kv are both positive), so clamping to -1..1 will have
421 // no effect on positive kernels. If there are negative
422 // weights, the clamping will just limit the amount of
423 // sharpening happening at the corners, and the result will
424 // still be smooth.
425
426 // clamp scale factor to -1..1 range
427 if (scale >= 1) {
428 // scale by 1 (i.e. do nothing)
429 }
430 else {
431 if (scale < -1) {
432 // a negative scale means the original kernel had an overall negative weight
433 // after making symmetric, the kernel will always be positive
434 // scale ku by -1
435 // note: choice of u is arbitrary; we could have scaled u or v (but not both)
436 for (int i = 0; i < uw; i++) ku[i] *= -1;
437 newWeight = -newWeight;
438 }
439 else {
440 // scale ku to restore initialWeight (again, choice of u instead of v is arbitrary)
441 for (int i = 0; i < uw; i++) ku[i] *= scale;
442 newWeight = initialWeight;
443 }
444 }
445 return newWeight;
446 }
447
448 void apply(float* dst, void* data, DataType dt, int nChan, int nTxChan)
449 {
450 // dispatch specialized apply function
451 ApplyFn fn = applyFunctions[(nChan!=nTxChan)*20 + ((unsigned)nChan<=4)*nChan*4 + dt];
452 fn(*this, dst, data, nChan, nTxChan);
453 }
454
455 void applyConst(float* dst, void* data, DataType dt, int nChan)
456 {
457 PtexUtils::applyConst(weight(), dst, data, dt, nChan);
458 }
459
460 private:
461 typedef void (*ApplyFn)(PtexSeparableKernel& k, float* dst, void* data, int nChan, int nTxChan);
462 typedef void (*ApplyConstFn)(float weight, float* dst, void* data, int nChan);
463 static ApplyFn applyFunctions[40];
464 static ApplyConstFn applyConstFunctions[20];
465 static inline float accumulate(const float* p, int n)
466 {
467 float result = 0;
468 for (const float* e = p + n; p != e; p++) result += *p;
469 return result;
470 }
471 };
472
473 #endif
PtexSeparableKernel::v
int v
Definition: PtexSeparableKernel.h:49
Ptex::e_left
Left edge, from UV (0,1) to (0,0)
Definition: Ptexture.h:103
PtexSeparableKernel::set
void set(Res resVal, int uVal, int vVal, int uwVal, int vwVal, const float *kuVal, const float *kvVal)
Definition: PtexSeparableKernel.h:71
PtexSeparableKernel::upresU
void upresU()
Definition: PtexSeparableKernel.h:362
PtexSeparableKernel::kmax
static const int kmax
Definition: PtexSeparableKernel.h:53
Ptex
Common data structures and enums used throughout the API.
Definition: Ptexture.h:73
PtexSeparableKernel::apply
void apply(float *dst, void *data, DataType dt, int nChan, int nTxChan)
Definition: PtexSeparableKernel.h:448
PtexSeparableKernel::applyConst
void applyConst(float *dst, void *data, DataType dt, int nChan)
Definition: PtexSeparableKernel.h:455
PtexSeparableKernel::kv
float * kv
Definition: PtexSeparableKernel.h:52
Ptex::e_top
Top edge, from UV (1,1) to (0,1)
Definition: Ptexture.h:102
PtexSeparableKernel
Definition: PtexSeparableKernel.h:46
PtexSeparableKernel::downresV
void downresV()
Definition: PtexSeparableKernel.h:333
PtexSeparableKernel::accumulate
static float accumulate(const float *p, int n)
Definition: PtexSeparableKernel.h:465
Ptex::DataType
DataType
Type of data stored in texture file.
Definition: Ptexture.h:83
PtexSeparableKernel::mergeL
void mergeL(BorderMode mode)
Definition: PtexSeparableKernel.h:102
PtexUtils::min
static T min(T a, T b)
Definition: PtexUtils.h:116
PtexSeparableKernel::mergeB
void mergeB(BorderMode mode)
Definition: PtexSeparableKernel.h:121
PtexSeparableKernel::downresU
void downresU()
Definition: PtexSeparableKernel.h:304
PtexSeparableKernel::operator=
PtexSeparableKernel & operator=(const PtexSeparableKernel &k)
Definition: PtexSeparableKernel.h:65
PtexSeparableKernel::adjustMainToSubface
bool adjustMainToSubface(int eid)
Definition: PtexSeparableKernel.h:257
PtexSeparableKernel::ku
float * ku
Definition: PtexSeparableKernel.h:51
PtexSeparableKernel::makeSymmetric
float makeSymmetric(float initialWeight)
Definition: PtexSeparableKernel.h:388
Ptex::Res::v
int v() const
V resolution in texels.
Definition: Ptexture.h:181
PtexSeparableKernel::PtexSeparableKernel
PtexSeparableKernel()
Definition: PtexSeparableKernel.h:57
PtexSeparableKernel::applyFunctions
static ApplyFn applyFunctions[40]
Definition: PtexSeparableKernel.h:463
Ptex::e_bottom
Bottom edge, from UV (0,0) to (1,0)
Definition: Ptexture.h:100
PtexSeparableKernel::applyConstFunctions
static ApplyConstFn applyConstFunctions[20]
Definition: PtexSeparableKernel.h:464
Ptex::BorderMode
BorderMode
How to handle mesh border when filtering.
Definition: Ptexture.h:91
Ptex::Res::u
int u() const
U resolution in texels.
Definition: Ptexture.h:178
Ptex::Res::vlog2
int8_t vlog2
log base 2 of v resolution, in texels
Definition: Ptexture.h:163
PtexSeparableKernel::uw
int uw
Definition: PtexSeparableKernel.h:50
PtexSeparableKernel::mergeR
void mergeR(BorderMode mode)
Definition: PtexSeparableKernel.h:112
PtexSeparableKernel::res
Res res
Definition: PtexSeparableKernel.h:48
PtexSeparableKernel::upresV
void upresV()
Definition: PtexSeparableKernel.h:375
PtexSeparableKernel::adjustSubfaceToMain
void adjustSubfaceToMain(int eid)
Definition: PtexSeparableKernel.h:293
PtexSeparableKernel::u
int u
Definition: PtexSeparableKernel.h:49
PtexSeparableKernel::flipU
void flipU()
Definition: PtexSeparableKernel.h:226
PtexSeparableKernel::kubuff
float kubuff[kmax]
Definition: PtexSeparableKernel.h:54
PtexSeparableKernel::ApplyFn
void(* ApplyFn)(PtexSeparableKernel &k, float *dst, void *data, int nChan, int nTxChan)
Definition: PtexSeparableKernel.h:461
PtexSeparableKernel::mergeT
void mergeT(BorderMode mode)
Definition: PtexSeparableKernel.h:131
PtexSeparableKernel::ApplyConstFn
void(* ApplyConstFn)(float weight, float *dst, void *data, int nChan)
Definition: PtexSeparableKernel.h:462
Ptex::m_black
texel beyond border are assumed to be black
Definition: Ptexture.h:93
PtexSeparableKernel::flipV
void flipV()
Definition: PtexSeparableKernel.h:232
PtexSeparableKernel::splitL
void splitL(PtexSeparableKernel &k)
Definition: PtexSeparableKernel.h:140
Ptex::Res::swapuv
void swapuv()
Swap the u and v resolution values in place.
Definition: Ptexture.h:205
PtexSeparableKernel::vw
int vw
Definition: PtexSeparableKernel.h:50
PtexSeparableKernel::splitB
void splitB(PtexSeparableKernel &k)
Definition: PtexSeparableKernel.h:184
Ptex::Res::ulog2
int8_t ulog2
log base 2 of u resolution, in texels
Definition: Ptexture.h:162
Ptex::e_right
Right edge, from UV (1,0) to (1,1)
Definition: Ptexture.h:101
PtexSeparableKernel::swapUV
void swapUV()
Definition: PtexSeparableKernel.h:238
PtexSeparableKernel::splitT
void splitT(PtexSeparableKernel &k)
Definition: PtexSeparableKernel.h:206
Ptex::Res
Pixel resolution of a given texture.
Definition: Ptexture.h:161
PtexSeparableKernel::PtexSeparableKernel
PtexSeparableKernel(const PtexSeparableKernel &k)
Definition: PtexSeparableKernel.h:60
PtexSeparableKernel::kvbuff
float kvbuff[kmax]
Definition: PtexSeparableKernel.h:55
PtexSeparableKernel::rotate
void rotate(int rot)
Definition: PtexSeparableKernel.h:246
PtexSeparableKernel::stripZeros
void stripZeros()
Definition: PtexSeparableKernel.h:88
PtexUtils.h
PtexSeparableKernel::weight
float weight() const
Definition: PtexSeparableKernel.h:97
Ptexture.h
Public API classes for reading, writing, caching, and filtering Ptex files.
PtexSeparableKernel::splitR
void splitR(PtexSeparableKernel &k)
Definition: PtexSeparableKernel.h:163
PtexUtils::applyConst
static void applyConst(float weight, float *dst, void *data, Ptex::DataType dt, int nChan)
Definition: PtexUtils.h:204