466 lines
12 KiB
C++
Raw Normal View History

2022-04-07 18:46:57 +02:00
///////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2006, Industrial Light & Magic, a division of Lucas
// Digital Ltd. LLC
//
// 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
// its contributors 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.
//
///////////////////////////////////////////////////////////////////////////
//-----------------------------------------------------------------------------
//
// Run a set of CTL transforms to generate a color lookup table.
//
//-----------------------------------------------------------------------------
#include <stdio.h>
#include "ctlToLut.h"
#if HAVE_CTL_INTERPRETER
#include <ImfCtlApplyTransforms.h>
#include <CtlSimdInterpreter.h>
#include <ImfStandardAttributes.h>
#include <ImfHeader.h>
#include <stdlib.h>
#include <ImfFrameBuffer.h>
#include <cassert>
#include <iostream>
using namespace std;
using namespace Ctl;
using namespace OPENEXR_IMF_NAMESPACE;
using namespace IMATH_NAMESPACE;
#else
#include <ImfStandardAttributes.h>
#include <ImfHeader.h>
#include <stdlib.h>
#include <cassert>
#include <iostream>
using namespace std;
using namespace OPENEXR_IMF_NAMESPACE;
using namespace IMATH_NAMESPACE;
#endif
#define WARNING(message) (cerr << "Warning: " << message << endl)
float
displayVideoGamma ()
{
//
// Get the display's video gamma from an environment variable.
// If this fails, use a default value (1/2.2).
//
const char gammaEnv[] = "EXR_DISPLAY_VIDEO_GAMMA";
float g = 2.2f;
if (const char *gamma = getenv (gammaEnv))
{
float tmp;
int n = sscanf (gamma, " %f", &tmp);
if (n != 1)
WARNING ("Cannot parse environment variable " << gammaEnv << "; "
"using default value (" << g << ").");
else if (tmp < 1.f)
WARNING ("Display video gamma, specified in environment "
"variable " << gammaEnv << " is out of range; "
"using default value (" << g << ").");
else
g = tmp;
}
else
{
WARNING ("Environment variable " << gammaEnv << " is not set; "
"using default value (" << g << ").");
}
return 1.f / g;
}
#if HAVE_CTL_INTERPRETER
namespace {
void
initializeEnvHeader (Header &envHeader)
{
//
// Initialize the "environment header" for the CTL
// transforms by adding displayChromaticities,
// displayWhiteLuminance and displaySurroundLuminance
// attributes.
//
//
// Get the chromaticities of the display's primaries and
// white point from an environment variable. If this fails,
// assume chromaticities according to Rec. ITU-R BT.709.
//
static const char chromaticitiesEnv[] = "CTL_DISPLAY_CHROMATICITIES";
Chromaticities c; // default-initialized according to Rec. 709
if (const char *chromaticities = getenv (chromaticitiesEnv))
{
Chromaticities tmp;
int n = sscanf (chromaticities,
" red %f %f green %f %f blue %f %f white %f %f",
&tmp.red.x, &tmp.red.y,
&tmp.green.x, &tmp.green.y,
&tmp.blue.x, &tmp.blue.y,
&tmp.white.x, &tmp.white.y);
if (n == 8)
c = tmp;
else
WARNING ("Cannot parse environment variable " <<
chromaticitiesEnv << "; using default value "
"(chromaticities according to Rec. ITU-R BT.709).");
}
else
{
WARNING ("Environment variable " << chromaticitiesEnv << " is "
"not set; using default value (chromaticities according "
"to Rec. ITU-R BT.709).");
}
envHeader.insert ("displayChromaticities", ChromaticitiesAttribute (c));
//
// Get the display's white luminance from an environment variable.
// If this fails, assume 120 candelas per square meter.
// (Screen aim luminance according to SMPTE RP 166.)
//
static const char whiteLuminanceEnv[] = "CTL_DISPLAY_WHITE_LUMINANCE";
static const float whiteLuminanceDefault = 120.0;
float wl = whiteLuminanceDefault;
if (const char *whiteLuminance = getenv (whiteLuminanceEnv))
{
int n = sscanf (whiteLuminance, " %f", &wl);
if (n != 1)
WARNING ("Cannot parse environment variable " <<
whiteLuminanceEnv << "; using default value "
"(" << wl << " candelas per square meter).");
}
else
{
WARNING ("Environment variable " << whiteLuminanceEnv << " is "
"is not set; using default value (" << wl << " candelas "
"per square meter).");
}
envHeader.insert ("displayWhiteLuminance", FloatAttribute (wl));
//
// Get the display's surround luminance from an environment variable.
// If this fails, assume 10% of the display's white luminance.
// (Recommended setup according to SMPTE RP 166.)
//
static const char surroundLuminanceEnv[] = "CTL_DISPLAY_SURROUND_LUMINANCE";
float sl = wl * 0.1f;
if (const char *surroundLuminance = getenv (surroundLuminanceEnv))
{
int n = sscanf (surroundLuminance, " %f", &sl);
if (n != 1)
WARNING ("Cannot parse environment variable " <<
surroundLuminanceEnv << "; using default value "
"(" << sl << " candelas per square meter).");
}
else
{
WARNING ("Environment variable " << surroundLuminanceEnv << " is "
"is not set; using default value (" << sl << " candelas "
"per square meter).");
}
envHeader.insert ("displaySurroundLuminance", FloatAttribute (sl));
}
string
displayTransformName ()
{
//
// Get the name of the display transform from an environment
// variable. If this fails, use a default name.
//
static const char displayTransformEnv[] = "CTL_DISPLAY_TRANSFORM";
static const char displayTransformDefault[] = "transform_display_video";
const char *displayTransform = getenv (displayTransformEnv);
if (!displayTransform)
{
displayTransform = displayTransformDefault;
WARNING ("Environment variable " << displayTransformEnv << " "
"is not set; using default value "
"(\"" << displayTransform << "\").");
}
return displayTransform;
}
} // namespace
void
ctlToLut (vector<string> transformNames,
Header inHeader,
size_t lutSize,
const half pixelValues[/*lutSize*/],
half lut[/*lutSize*/])
{
//
// If we do not have an explicit set of transform names
// then find suitable look modification, rendering and
// display transforms.
//
if (transformNames.empty())
{
if (hasLookModTransform (inHeader))
transformNames.push_back (lookModTransform (inHeader));
if (hasRenderingTransform (inHeader))
transformNames.push_back (renderingTransform (inHeader));
else
transformNames.push_back ("transform_RRT");
transformNames.push_back (displayTransformName());
}
//
// Initialize an input and an environment header:
// Make sure that the headers contain information about the primaries
// and the white point of the image files an the display, and about
// the display's white luminance and surround luminance.
//
Header envHeader;
Header outHeader;
if (!hasChromaticities (inHeader))
addChromaticities (inHeader, Chromaticities());
if (!hasAdoptedNeutral (inHeader))
addAdoptedNeutral (inHeader, chromaticities(inHeader).white);
initializeEnvHeader (envHeader);
//
// Set up input and output FrameBuffer objects for the CTL transforms.
//
assert (lutSize % 4 == 0);
FrameBuffer inFb;
inFb.insert ("R",
Slice (HALF, // type
(char *)pixelValues, // base
4 * sizeof (half), // xStride
0)); // yStride
inFb.insert ("G",
Slice (HALF, // type
(char *)(pixelValues + 1), // base
4 * sizeof (half), // xStride
0)); // yStride
inFb.insert ("B",
Slice (HALF, // type
(char *)(pixelValues + 2), // base
4 * sizeof (half), // xStride
0)); // yStride
FrameBuffer outFb;
outFb.insert ("R_display",
Slice (HALF, // type
(char *)lut, // base
4 * sizeof (half), // xStride
0)); // yStride
outFb.insert ("G_display",
Slice (HALF, // type
(char *)(lut + 1), // base
4 * sizeof (half), // xStride
0)); // yStride
outFb.insert ("B_display",
Slice (HALF, // type
(char *)(lut + 2), // base
4 * sizeof (half), // xStride
0)); // yStride
//
// Run the CTL transforms.
//
SimdInterpreter interpreter;
#ifdef CTL_MODULE_BASE_PATH
//
// The configuration scripts has defined a default
// location for CTL modules. Include this location
// in the CTL module search path.
//
vector<string> paths = interpreter.modulePaths();
paths.push_back (CTL_MODULE_BASE_PATH);
interpreter.setModulePaths (paths);
#endif
ImfCtl::applyTransforms (interpreter,
transformNames,
Box2i (V2i (0, 0), V2i (lutSize / 4 - 1, 0)),
envHeader,
inHeader,
inFb,
outHeader,
outFb);
}
#else
#include <ImfStandardAttributes.h>
#include <ImfHeader.h>
#include <cassert>
#include <iostream>
using namespace std;
using namespace OPENEXR_IMF_NAMESPACE;
using namespace IMATH_NAMESPACE;
#define WARNING(message) (cerr << "Warning: " << message << endl)
void
ctlToLut (vector<string> transformNames,
Header inHeader,
size_t lutSize,
const half pixelValues[/*lutSize*/],
half lut[/*lutSize*/])
{
//
// This program has been compiled without CTL support.
//
// Our fallback solution is to build a lookup table that
// performs a coordinate transform from the primaries and
// white point of the input files to the primaries and
// white point of the display.
//
//
// Get the input file chromaticities
//
Chromaticities fileChroma;
if (hasChromaticities (inHeader))
fileChroma = chromaticities (inHeader);
//
// Get the display chromaticities
//
static const char chromaticitiesEnv[] = "CTL_DISPLAY_CHROMATICITIES";
Chromaticities displayChroma;
if (const char *chromaticities = getenv (chromaticitiesEnv))
{
Chromaticities tmp;
int n = sscanf (chromaticities,
" red %f %f green %f %f blue %f %f white %f %f",
&tmp.red.x, &tmp.red.y,
&tmp.green.x, &tmp.green.y,
&tmp.blue.x, &tmp.blue.y,
&tmp.white.x, &tmp.white.y);
if (n == 8)
displayChroma = tmp;
else
WARNING ("Cannot parse environment variable " <<
chromaticitiesEnv << "; using default value "
"(chromaticities according to Rec. ITU-R BT.709).");
}
else
{
WARNING ("Environment variable " << chromaticitiesEnv << " is "
"not set; using default value (chromaticities according "
"to Rec. ITU-R BT.709).");
}
//
// Do the coordinate transform
//
M44f M = RGBtoXYZ (fileChroma, 1) * XYZtoRGB (displayChroma, 1);
assert (lutSize % 4 == 0);
for (int i = 0; i < lutSize; i += 4)
{
V3f rgb (pixelValues[i], pixelValues[i + 1], pixelValues[i + 2]);
rgb = rgb * M;
lut[i + 0] = rgb[0];
lut[i + 1] = rgb[1];
lut[i + 2] = rgb[2];
lut[i + 3] = 0;
}
}
#endif