From 8add983449e6fa6355d8b800b43406207c7bc76d Mon Sep 17 00:00:00 2001 From: Glenn Maynard Date: Sun, 21 Aug 2016 00:29:01 -0500 Subject: [PATCH] Organize a bit. --- Makefile | 2 +- exrflatten.cpp | 185 +------------------------------------ exrflatten.vcxproj | 6 ++ exrflatten.vcxproj.filters | 6 ++ exrsamples.cpp | 123 ++++++++++++++++++++++++ exrsamples.h | 56 +++++++++++ helpers.cpp | 71 ++++++++++++++ helpers.h | 13 +++ 8 files changed, 279 insertions(+), 183 deletions(-) create mode 100644 exrsamples.cpp create mode 100644 exrsamples.h create mode 100644 helpers.cpp create mode 100644 helpers.h diff --git a/Makefile b/Makefile index 43de5ff..162e401 100644 --- a/Makefile +++ b/Makefile @@ -3,5 +3,5 @@ LDFLAGS=-lIlmImf -g CC=g++ all: exrflatten -exrflatten: exrflatten.o +exrflatten: exrflatten.o helpers.o exrsamples.o diff --git a/exrflatten.cpp b/exrflatten.cpp index bdfce8c..9249e86 100644 --- a/exrflatten.cpp +++ b/exrflatten.cpp @@ -18,7 +18,9 @@ #include #include #include -#include +#include + +#include "helpers.h" using namespace std; using namespace Imf; @@ -38,72 +40,6 @@ const int NO_OBJECT_ID = 0; // - separate per-color alpha (RA, GA, BA) // - (and lots of other stuff, EXR is "too general") -string vssprintf(const char *fmt, va_list va) -{ - // Work around a gcc bug: passing a va_list to vsnprintf alters it. va_list is supposed - // to be by value. - va_list vc; - va_copy(vc, va); - - int iBytes = vsnprintf(NULL, 0, fmt, vc); - char *pBuf = (char*) alloca(iBytes + 1); - vsnprintf(pBuf, iBytes + 1, fmt, va); - return string(pBuf, iBytes); -} - -string ssprintf(const char *fmt, ...) -{ - va_list va; - va_start(va, fmt); - return vssprintf(fmt, va); -} - -string subst(string s, string from, string to) -{ - int start = 0; - while(1) - { - auto pos = s.find(from, start); - if(pos == string::npos) - break; - - string before = s.substr(0, pos); - string after = s.substr(pos + from.size()); - s = before + to + after; - start = pos + to.size(); - } - - return s; -} - -/* - * Return the last named component of dir: - * a/b/c -> c - * a/b/c/ -> c - */ -string basename(const string &dir) -{ - size_t end = dir.find_last_not_of("/\\"); - if( end == dir.npos ) - return ""; - - size_t start = dir.find_last_of("/\\", end); - if(start == dir.npos) - start = 0; - else - ++start; - - return dir.substr(start, end-start+1); -} - -string setExtension(string path, const string &ext) -{ - auto pos = path.rfind('.'); - if(pos != string::npos) - path = path.substr(0, pos); - - return path + ext; -} // Given a filename like "abcdef.1234.exr", return "1234". string getFrameNumberFromFilename(string s) @@ -119,121 +55,6 @@ string getFrameNumberFromFilename(string s) return frameString; } -// http://www.openexr.com/TechnicalIntroduction.pdf: -void splitVolumeSample( - float a, float c, // Opacity and color of original sample - float zf, float zb, // Front and back of original sample - float z, // Position of split - float& af, float& cf, // Opacity and color of part closer than z - float& ab, float& cb) // Opacity and color of part further away than z -{ - // Given a volume sample whose front and back are at depths zf and zb respectively, split the - // sample at depth z. Return the opacities and colors of the two parts that result from the split. - // - // The code below is written to avoid excessive rounding errors when the opacity of the original - // sample is very small: - // - // The straightforward computation of the opacity of either part requires evaluating an expression - // of the form - // - // 1 - pow (1-a, x). - // - // However, if a is very small, then 1-a evaluates to 1.0 exactly, and the entire expression - // evaluates to 0.0. - // - // We can avoid this by rewriting the expression as - // - // 1 - exp (x * log (1-a)), - // - // and replacing the call to log() with a call to the function log1p(), which computes the logarithm - // of 1+x without attempting to evaluate the expression 1+x when x is very small. - // - // Now we have - // - // 1 - exp (x * log1p (-a)). - // - // However, if a is very small then the call to exp() returns 1.0, and the overall expression still - // evaluates to 0.0. We can avoid that by replacing the call to exp() with a call to expm1(): - // - // -expm1 (x * log1p (-a)) - // - // expm1(x) computes exp(x) - 1 in such a way that the result is accurate even if x is very small. - - assert (zb > zf && z >= zf && z <= zb); - a = max (0.0f, min (a, 1.0f)); - if (a == 1) - { - af = ab = 1; - cf = cb = c; - } - else - { - float xf = (z - zf) / (zb - zf); - float xb = (zb - z) / (zb - zf); - if (a > numeric_limits::min()) - { - af = -expm1 (xf * log1p (-a)); - cf = (af / a) * c; - ab = -expm1 (xb * log1p (-a)); - cb = (ab / a) * c; - } - else - { - af = a * xf; - cf = c * xf; - ab = a * xb; - cb = c * xb; - } - } -} - -void mergeOverlappingSamples( - float a1, float c1, // Opacity and color of first sample - float a2, float c2, // Opacity and color of second sample - float &am, float &cm) // Opacity and color of merged sample -{ - // This function merges two perfectly overlapping volume or point samples. Given the color and - // opacity of two samples, it returns the color and opacity of the merged sample. - // - // The code below is written to avoid very large rounding errors when the opacity of one or both - // samples is very small: - // - // * The merged opacity must not be computed as 1 - (1-a1) * (1-a2)./ If a1 and a2 are less than - // about half a floating-point epsilon, the expressions (1-a1) and (1-a2) evaluate to 1.0 exactly, - // and the merged opacity becomes 0.0. The error is amplified later in the calculation of the - // merged color. - // - // Changing the calculation of the merged opacity to a1 + a2 - a1*a2 avoids the excessive rounding - // error. - // - // * For small x, the logarithm of 1+x is approximately equal to x, but log(1+x) returns 0 because - // 1+x evaluates to 1.0 exactly. This can lead to large errors in the calculation of the merged - // color if a1 or a2 is very small. - // - // The math library function log1p(x) returns the logarithm of 1+x, but without attempting to - // evaluate the expression 1+x when x is very small. - - a1 = max (0.0f, min (a1, 1.0f)); - a2 = max (0.0f, min (a2, 1.0f)); - am = a1 + a2 - a1 * a2; - if (a1 == 1 && a2 == 1) - cm = (c1 + c2) / 2; - else if (a1 == 1) - cm = c1; - else if (a2 == 1) - cm = c2; - else - { - static const float MAX = numeric_limits::max(); - float u1 = -log1p (-a1); - float v1 = (u1 < a1 * MAX)? u1 / a1: 1; - float u2 = -log1p (-a2); - float v2 = (u2 < a2 * MAX)? u2 / a2: 1; - float u = u1 + u2; - float w = (u > 1 || am < u * MAX)? am / u: 1; - cm = (c1 * v1 + c2 * v2) * w; - } -} template class FBArray { diff --git a/exrflatten.vcxproj b/exrflatten.vcxproj index e585e7e..357d491 100644 --- a/exrflatten.vcxproj +++ b/exrflatten.vcxproj @@ -94,6 +94,12 @@ + + + + + + diff --git a/exrflatten.vcxproj.filters b/exrflatten.vcxproj.filters index bd7abc7..146b6b7 100644 --- a/exrflatten.vcxproj.filters +++ b/exrflatten.vcxproj.filters @@ -2,5 +2,11 @@ + + + + + + \ No newline at end of file diff --git a/exrsamples.cpp b/exrsamples.cpp new file mode 100644 index 0000000..d930a6c --- /dev/null +++ b/exrsamples.cpp @@ -0,0 +1,123 @@ +#include "exrsamples.h" + +#include +#include + +#include +using namespace std; + +// http://www.openexr.com/TechnicalIntroduction.pdf: +void splitVolumeSample( + float a, float c, // Opacity and color of original sample + float zf, float zb, // Front and back of original sample + float z, // Position of split + float& af, float& cf, // Opacity and color of part closer than z + float& ab, float& cb) // Opacity and color of part further away than z +{ + // Given a volume sample whose front and back are at depths zf and zb respectively, split the + // sample at depth z. Return the opacities and colors of the two parts that result from the split. + // + // The code below is written to avoid excessive rounding errors when the opacity of the original + // sample is very small: + // + // The straightforward computation of the opacity of either part requires evaluating an expression + // of the form + // + // 1 - pow (1-a, x). + // + // However, if a is very small, then 1-a evaluates to 1.0 exactly, and the entire expression + // evaluates to 0.0. + // + // We can avoid this by rewriting the expression as + // + // 1 - exp (x * log (1-a)), + // + // and replacing the call to log() with a call to the function log1p(), which computes the logarithm + // of 1+x without attempting to evaluate the expression 1+x when x is very small. + // + // Now we have + // + // 1 - exp (x * log1p (-a)). + // + // However, if a is very small then the call to exp() returns 1.0, and the overall expression still + // evaluates to 0.0. We can avoid that by replacing the call to exp() with a call to expm1(): + // + // -expm1 (x * log1p (-a)) + // + // expm1(x) computes exp(x) - 1 in such a way that the result is accurate even if x is very small. + + assert (zb > zf && z >= zf && z <= zb); + a = max (0.0f, min (a, 1.0f)); + if (a == 1) + { + af = ab = 1; + cf = cb = c; + } + else + { + float xf = (z - zf) / (zb - zf); + float xb = (zb - z) / (zb - zf); + if (a > numeric_limits::min()) + { + af = -expm1 (xf * log1p (-a)); + cf = (af / a) * c; + ab = -expm1 (xb * log1p (-a)); + cb = (ab / a) * c; + } + else + { + af = a * xf; + cf = c * xf; + ab = a * xb; + cb = c * xb; + } + } +} + +void mergeOverlappingSamples( + float a1, float c1, // Opacity and color of first sample + float a2, float c2, // Opacity and color of second sample + float &am, float &cm) // Opacity and color of merged sample +{ + // This function merges two perfectly overlapping volume or point samples. Given the color and + // opacity of two samples, it returns the color and opacity of the merged sample. + // + // The code below is written to avoid very large rounding errors when the opacity of one or both + // samples is very small: + // + // * The merged opacity must not be computed as 1 - (1-a1) * (1-a2)./ If a1 and a2 are less than + // about half a floating-point epsilon, the expressions (1-a1) and (1-a2) evaluate to 1.0 exactly, + // and the merged opacity becomes 0.0. The error is amplified later in the calculation of the + // merged color. + // + // Changing the calculation of the merged opacity to a1 + a2 - a1*a2 avoids the excessive rounding + // error. + // + // * For small x, the logarithm of 1+x is approximately equal to x, but log(1+x) returns 0 because + // 1+x evaluates to 1.0 exactly. This can lead to large errors in the calculation of the merged + // color if a1 or a2 is very small. + // + // The math library function log1p(x) returns the logarithm of 1+x, but without attempting to + // evaluate the expression 1+x when x is very small. + + a1 = max (0.0f, min (a1, 1.0f)); + a2 = max (0.0f, min (a2, 1.0f)); + am = a1 + a2 - a1 * a2; + if (a1 == 1 && a2 == 1) + cm = (c1 + c2) / 2; + else if (a1 == 1) + cm = c1; + else if (a2 == 1) + cm = c2; + else + { + static const float MAX = numeric_limits::max(); + float u1 = -log1p (-a1); + float v1 = (u1 < a1 * MAX)? u1 / a1: 1; + float u2 = -log1p (-a2); + float v2 = (u2 < a2 * MAX)? u2 / a2: 1; + float u = u1 + u2; + float w = (u > 1 || am < u * MAX)? am / u: 1; + cm = (c1 * v1 + c2 * v2) * w; + } +} diff --git a/exrsamples.h b/exrsamples.h new file mode 100644 index 0000000..3bd41fb --- /dev/null +++ b/exrsamples.h @@ -0,0 +1,56 @@ +#ifndef EXRSAMPLES_H +#define EXRSAMPLES_H + +// These are helpers to split and merge samples. This would be needed to implement +// EXR sample tidying. +// +// This code is from the OpenEXR library documentation, in "InterpretingDeepPixels.pdf". +void splitVolumeSample( + float a, float c, // Opacity and color of original sample + float zf, float zb, // Front and back of original sample + float z, // Position of split + float& af, float& cf, // Opacity and color of part closer than z + float& ab, float& cb); // Opacity and color of part further away than z + +void mergeOverlappingSamples( + float a1, float c1, // Opacity and color of first sample + float a2, float c2, // Opacity and color of second sample + float &am, float &cm); // Opacity and color of merged sample + +#endif + +/* + * Copyright (c) 2006, Industrial Light & Magic, a division of Lucasfilm + * Entertainment Company Ltd. Portions contributed and copyright held by + * others as indicated. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * * Neither the name of Industrial Light & Magic nor the names of + * any other contributors to this software may be used to endorse or + * promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ diff --git a/helpers.cpp b/helpers.cpp new file mode 100644 index 0000000..efb2032 --- /dev/null +++ b/helpers.cpp @@ -0,0 +1,71 @@ +#include + +#include +using namespace std; + +string vssprintf(const char *fmt, va_list va) +{ + // Work around a gcc bug: passing a va_list to vsnprintf alters it. va_list is supposed + // to be by value. + va_list vc; + va_copy(vc, va); + + int iBytes = vsnprintf(NULL, 0, fmt, vc); + char *pBuf = (char*) alloca(iBytes + 1); + vsnprintf(pBuf, iBytes + 1, fmt, va); + return string(pBuf, iBytes); +} + +string ssprintf(const char *fmt, ...) +{ + va_list va; + va_start(va, fmt); + return vssprintf(fmt, va); +} + +string subst(string s, string from, string to) +{ + int start = 0; + while(1) + { + auto pos = s.find(from, start); + if(pos == string::npos) + break; + + string before = s.substr(0, pos); + string after = s.substr(pos + from.size()); + s = before + to + after; + start = pos + to.size(); + } + + return s; +} + +/* + * Return the last named component of dir: + * a/b/c -> c + * a/b/c/ -> c + */ +string basename(const string &dir) +{ + size_t end = dir.find_last_not_of("/\\"); + if( end == dir.npos ) + return ""; + + size_t start = dir.find_last_of("/\\", end); + if(start == dir.npos) + start = 0; + else + ++start; + + return dir.substr(start, end-start+1); +} + +string setExtension(string path, const string &ext) +{ + auto pos = path.rfind('.'); + if(pos != string::npos) + path = path.substr(0, pos); + + return path + ext; +} diff --git a/helpers.h b/helpers.h new file mode 100644 index 0000000..edde368 --- /dev/null +++ b/helpers.h @@ -0,0 +1,13 @@ +#ifndef HELPERS_H +#define HELPERS_H + +#include +using namespace std; + +string vssprintf(const char *fmt, va_list va); +string ssprintf(const char *fmt, ...); +string subst(string s, string from, string to); +string basename(const string &dir); +string setExtension(string path, const string &ext); + +#endif