437 lines
12 KiB
C++
437 lines
12 KiB
C++
///////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// 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.
|
|
//
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
// Apply CTL transforms
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
|
|
#include "applyCtl.h"
|
|
|
|
#if HAVE_CTL_INTERPRETER
|
|
|
|
#include <ImfCtlApplyTransforms.h>
|
|
#include <CtlSimdInterpreter.h>
|
|
#include <ImfStandardAttributes.h>
|
|
#include <ImfHeader.h>
|
|
#include <ImfFrameBuffer.h>
|
|
#include <stdlib.h>
|
|
#include <cassert>
|
|
#include <cstdio>
|
|
#include <iostream>
|
|
#include<stdio.h>
|
|
|
|
using namespace std;
|
|
using namespace Ctl;
|
|
using namespace IMATH;
|
|
|
|
#else
|
|
|
|
#include <ImfStandardAttributes.h>
|
|
#include <ImfHeader.h>
|
|
#include <stdlib.h>
|
|
#include <cassert>
|
|
#include <cstdio>
|
|
#include <iostream>
|
|
#include <stdio.h>
|
|
|
|
using namespace std;
|
|
using namespace IMATH;
|
|
|
|
#endif
|
|
|
|
|
|
using namespace IMF;
|
|
|
|
#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;
|
|
}
|
|
|
|
|
|
namespace {
|
|
|
|
Chromaticities
|
|
displayChromaticities ()
|
|
{
|
|
//
|
|
// 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).");
|
|
}
|
|
|
|
return c;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
#if HAVE_CTL_INTERPRETER
|
|
|
|
namespace {
|
|
|
|
void
|
|
initializeEnvHeader (Header &envHeader)
|
|
{
|
|
//
|
|
// Initialize the "environment header" for the CTL
|
|
// transforms by adding displayChromaticities,
|
|
// displayWhiteLuminance and displaySurroundLuminance
|
|
// attributes.
|
|
//
|
|
|
|
Chromaticities c = displayChromaticities();
|
|
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;
|
|
}
|
|
|
|
|
|
void
|
|
initializeInFrameBuffer
|
|
(int w,
|
|
int h,
|
|
const Array<Rgba> &pixels,
|
|
FrameBuffer &fb)
|
|
{
|
|
fb.insert ("R", Slice (HALF, // type
|
|
(char *)(&pixels[0].r), // base
|
|
sizeof (pixels[0]), // xStride
|
|
sizeof (pixels[0]) * w)); // yStride
|
|
|
|
fb.insert ("G", Slice (HALF, // type
|
|
(char *)(&pixels[0].g), // base
|
|
sizeof (pixels[0]), // xStride
|
|
sizeof (pixels[0]) * w)); // yStride
|
|
|
|
fb.insert ("B", Slice (HALF, // type
|
|
(char *)(&pixels[0].b), // base
|
|
sizeof (pixels[0]), // xStride
|
|
sizeof (pixels[0]) * w)); // yStride
|
|
}
|
|
|
|
|
|
void
|
|
initializeOutFrameBuffer
|
|
(int w,
|
|
int h,
|
|
const Array<Rgba> &pixels,
|
|
FrameBuffer &fb)
|
|
{
|
|
fb.insert ("R_display", Slice (HALF, // type
|
|
(char *)(&pixels[0].r), // base
|
|
sizeof (pixels[0]), // xStride
|
|
sizeof (pixels[0]) * w)); // yStride
|
|
|
|
fb.insert ("G_display", Slice (HALF, // type
|
|
(char *)(&pixels[0].g), // base
|
|
sizeof (pixels[0]), // xStride
|
|
sizeof (pixels[0]) * w)); // yStride
|
|
|
|
fb.insert ("B_display", Slice (HALF, // type
|
|
(char *)(&pixels[0].b), // base
|
|
sizeof (pixels[0]), // xStride
|
|
sizeof (pixels[0]) * w)); // yStride
|
|
}
|
|
|
|
} // namespace
|
|
|
|
|
|
void
|
|
applyCtl (vector<string> transformNames,
|
|
Header inHeader,
|
|
const Array<Rgba> &inPixels,
|
|
int w,
|
|
int h,
|
|
Array<Rgba> &outPixels)
|
|
{
|
|
//
|
|
// 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.
|
|
//
|
|
|
|
if (!hasChromaticities (inHeader))
|
|
addChromaticities (inHeader, Chromaticities());
|
|
|
|
if (!hasAdoptedNeutral (inHeader))
|
|
addAdoptedNeutral (inHeader, chromaticities(inHeader).white);
|
|
|
|
Header envHeader;
|
|
initializeEnvHeader (envHeader);
|
|
|
|
//
|
|
// Set up input and output FrameBuffer objects for the transforms.
|
|
//
|
|
|
|
FrameBuffer inFb;
|
|
initializeInFrameBuffer (w, h, inPixels, inFb);
|
|
|
|
FrameBuffer outFb;
|
|
initializeOutFrameBuffer (w, h, outPixels, outFb);
|
|
|
|
//
|
|
// Run the CTL transforms
|
|
//
|
|
|
|
Box2i transformWindow (V2i (0, 0), V2i (w - 1, h - 1));
|
|
|
|
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
|
|
|
|
Header outHeader;
|
|
|
|
ImfCtl::applyTransforms (interpreter,
|
|
transformNames,
|
|
transformWindow,
|
|
envHeader,
|
|
inHeader,
|
|
inFb,
|
|
outHeader,
|
|
outFb);
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
void
|
|
adjustChromaticities (const Header &header,
|
|
const Array<Rgba> &inPixels,
|
|
int w, int h,
|
|
Array<Rgba> &outPixels)
|
|
{
|
|
Chromaticities fileChroma; // default-initialized according to Rec. 709
|
|
|
|
if (hasChromaticities (header))
|
|
fileChroma = chromaticities (header);
|
|
|
|
Chromaticities displayChroma = displayChromaticities();
|
|
|
|
if (fileChroma.red == displayChroma.red &&
|
|
fileChroma.green == displayChroma.green &&
|
|
fileChroma.blue == displayChroma.blue &&
|
|
fileChroma.white == displayChroma.white)
|
|
{
|
|
return;
|
|
}
|
|
|
|
M44f M = RGBtoXYZ (fileChroma, 1) * XYZtoRGB (displayChroma, 1);
|
|
|
|
size_t numPixels = w * h;
|
|
|
|
for (size_t i = 0; i < numPixels; ++i)
|
|
{
|
|
const Rgba &in = inPixels[i];
|
|
Rgba &out = outPixels[i];
|
|
|
|
V3f rgb = V3f (in.r, in.g, in.b) * M;
|
|
|
|
out.r = rgb[0];
|
|
out.g = rgb[1];
|
|
out.b = rgb[2];
|
|
}
|
|
}
|