Skip to content

Commit

Permalink
Organize a bit.
Browse files Browse the repository at this point in the history
  • Loading branch information
zewt committed Aug 22, 2016
1 parent de6ba15 commit 8add983
Show file tree
Hide file tree
Showing 8 changed files with 279 additions and 183 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@ LDFLAGS=-lIlmImf -g
CC=g++

all: exrflatten
exrflatten: exrflatten.o
exrflatten: exrflatten.o helpers.o exrsamples.o

185 changes: 3 additions & 182 deletions exrflatten.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@
#include <OpenEXR/ImfOutputFile.h>
#include <OpenEXR/ImfPreviewImage.h>
#include <OpenEXR/ImfAttribute.h>
#include <OpenEXR/ImfStringAttribute.h>
#include <OpenEXR/ImfStringAttribute.h>

#include "helpers.h"

using namespace std;
using namespace Imf;
Expand All @@ -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)
Expand All @@ -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<float>::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<float>::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<typename T>
class FBArray {
Expand Down
6 changes: 6 additions & 0 deletions exrflatten.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,12 @@
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="exrflatten.cpp" />
<ClCompile Include="exrsamples.cpp" />
<ClCompile Include="helpers.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="exrsamples.h" />
<ClInclude Include="helpers.h" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
Expand Down
6 changes: 6 additions & 0 deletions exrflatten.vcxproj.filters
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,11 @@
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<ClCompile Include="exrflatten.cpp" />
<ClCompile Include="exrsamples.cpp" />
<ClCompile Include="helpers.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="exrsamples.h" />
<ClInclude Include="helpers.h" />
</ItemGroup>
</Project>
123 changes: 123 additions & 0 deletions exrsamples.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
#include "exrsamples.h"

#include <limits.h>
#include <assert.h>

#include <algorithm>
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<float>::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<float>::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;
}
}
Loading

0 comments on commit 8add983

Please sign in to comment.