Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
d0f8853
feat: ACES Container writer for OpenEXR
Glowies Sep 25, 2025
73d1e75
refactor(openexr): follow clang-format recommendations for exroutput.cpp
Glowies Sep 25, 2025
fda980e
fix(openexr): do not write ACES Container metadata when non-compliant…
Glowies Sep 26, 2025
e89f01f
test(openexr): test ACES container output with various modes of failure
Glowies Sep 26, 2025
2deb446
docs(openexr): document the oiio:ACESContainer hint
Glowies Sep 26, 2025
ca010df
refactor(openexr): rename oiio:ACESContainer hint to openexr:ACESCont…
Glowies Sep 27, 2025
0b3967e
refactor(openexr): add static keyword to AP0 chromaticities array
Glowies Sep 27, 2025
f98e2ce
fix(openexr): remove printing warning to stderr
Glowies Sep 30, 2025
2571f37
fix(openexr): chromaticities get set to AP0 in relaxed mode of ACES C…
Glowies Oct 1, 2025
c9b40c0
docs(openexr): update permalink to st2065-4 standard
Glowies Oct 1, 2025
ee84d2a
fix(openexr): check for existence of correct channel names instead of…
Glowies Oct 5, 2025
0f2feb5
refactor(openexr): update comment in exroutput.cpp
Glowies Oct 6, 2025
9f23db7
fix(openexr): use static and const keywords for the allowed_sets
Glowies Oct 6, 2025
7322ff1
test(openexr): more explanatory test results for aces container
Glowies Oct 6, 2025
c30d8f8
fix(openexr): test for non-empty attributes in ACES container
Glowies Oct 7, 2025
e94bd5d
feat(openexr): check attributes with exact required values
Glowies Oct 7, 2025
5c3fd08
refactor(openexr): follow clang-format recommendations
Glowies Oct 7, 2025
ac1c654
refactor(openexr): make chromaticities check a bit more readable
Glowies Oct 7, 2025
2052c52
feat(openexr): rename ACES container flag to openexr:ACESContainerPolicy
Glowies Oct 7, 2025
1e89fa5
refactor(openexr): apply clang-format recommendations
Glowies Oct 7, 2025
ae19a4c
feat(openexr): set aces container attributes in relaxed mode as well
Glowies Oct 8, 2025
c177142
docs(openexr): update ACES container mode to reflect changes to relax…
Glowies Oct 11, 2025
25fac3c
feat(openexr): better error messages for each failure case of ACES Co…
Glowies Oct 11, 2025
d3530e4
fix(openexr): inconsistent attribute read behaviour with ParamValue
Glowies Oct 11, 2025
21dfe1b
refactor(openexr): apply clang-format recommendations
Glowies Oct 11, 2025
d725544
test(openexr): fix issue with using single quotes in windows tests
Glowies Oct 11, 2025
b61974a
test(openexr): improve readability of ACES container test outputs
Glowies Oct 12, 2025
5980084
fix(openexr): remove redundant search for found attributes
Glowies Oct 19, 2025
60fba1b
refactor(openexr): replace vector with a c array to avoid extra alloc…
Glowies Oct 19, 2025
9216912
refactor(openexr): rename flag parameter and apply clang-format sugge…
Glowies Oct 19, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions src/doc/builtinplugins.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1643,6 +1643,18 @@ control aspects of the writing itself:
* - Output Configuration Attribute
- Type
- Meaning
* - ``oiio:ACESContainer``
- string
- One of `none` (default), `strict`, or `relaxed`.
If not `none`, the spec will be checked to see if it is compliant
with the ACES Container format defined in `ST 2065-4`_. If it is,
`chromaticities` will be set to the ACES AP0 ones, and the
`acesImageContainerFlag` attribute will be set to 1.
In `strict` mode, if the spec is non-compliant, the output will
throw an error and avoid writing the image.
While in `relaxed` mode, if the spec in non-compliant, only a
warning will be printed and the attributes mentioned above will
*not* be written to the spec.
* - ``oiio:RawColor``
- int
- If nonzero, writing images with non-RGB color models (such as YCbCr)
Expand All @@ -1654,6 +1666,7 @@ control aspects of the writing itself:
- Pointer to a ``Filesystem::IOProxy`` that will handle the I/O, for
example by writing to a memory buffer.

.. _ST 2065-4: https://pub.smpte.org/doc/st2065-4/20130510-pub/st2065-4-2013.pdf

**Custom I/O Overrides**

Expand Down
79 changes: 79 additions & 0 deletions src/openexr.imageio/exroutput.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,71 @@ set_exr_threads()



constexpr float ACES_AP0_chromaticities[8] = {
0.7347f, 0.2653f, // red
0.0f, 1.0f, // green
0.0001f, -0.077f, // blue
0.32168f, 0.33767f // white
};



bool
is_aces_container_compliant(const OIIO::ImageSpec& spec)
{
// Check channels
std::vector<std::string> allowed_sets
= { "B,G,R",
"A,B,G,R"
"B,G,R,left.B,left.G,left.R",
"A,B,G,R,left.A,left.B,left.G,left.R" };
std::string channels;
for (int c = 0; c < spec.nchannels; ++c) {
if (c > 0)
channels += ",";
channels += spec.channelnames[c];
}
if (std::find(allowed_sets.begin(), allowed_sets.end(), channels)
== allowed_sets.end())
return false;

// Check data type
if (spec.format != OIIO::TypeDesc::HALF)
return false;

// Check compression
std::string compression = spec.get_string_attribute("compression", "zip");
if (compression != "none")
return false;

return true;
}



bool
process_aces_container(OIIO::ImageSpec& spec, std::string mode)
{
bool is_compliant = is_aces_container_compliant(spec);

if (!is_compliant) {
std::cerr << "WARNING: Image spec is not ACES Container compliant\n";

// early out and return true iff in "relaxed" mode
// to indicate that the output can continue without
// throwing an error
return mode == "relaxed";
}

spec.attribute("chromaticities", OIIO::TypeDesc(OIIO::TypeDesc::FLOAT, 8),
ACES_AP0_chromaticities);
spec.attribute("acesImageContainerFlag", 1);

return true;
}



OpenEXROutput::OpenEXROutput()
{
pvt::set_exr_threads();
Expand Down Expand Up @@ -812,6 +877,20 @@ OpenEXROutput::spec_to_header(ImageSpec& spec, int subimage,
Imf::LevelMode(m_levelmode),
Imf::LevelRoundingMode(m_roundingmode)));

// Check ACES Container hint
std::string aces_mode = spec.get_string_attribute("oiio:ACESContainer",
"none");
if (aces_mode == "strict" || aces_mode == "relaxed") {
bool should_panic = !process_aces_container(spec, aces_mode);

if (should_panic) {
const char* non_compliance_message
= "Cannot output non-compliant ACES Container in 'strict' mode.";
errorfmt(non_compliance_message);
return false;
}
}

// Deal with all other params
for (const auto& p : spec.extra_attribs)
put_parameter(p.name().string(), p.type(), p.data(), header);
Expand Down
32 changes: 32 additions & 0 deletions testsuite/openexr-suite/ref/out.txt
Original file line number Diff line number Diff line change
Expand Up @@ -341,3 +341,35 @@ negoverscan.exr : 64 x 64, 3 channel, half openexr
screenWindowWidth: 1
oiio:subimages: 1
openexr:lineOrder: "increasingY"
1
WARNING: Image spec is not ACES Container compliant

WARNING: Image spec is not ACES Container compliant

WARNING: Image spec is not ACES Container compliant

Reading strict-out.exr
strict-out.exr : 4 x 4, 3 channel, half openexr
SHA-1: C49A9785B2243F2F080DAAD1747F119ACCECCFA5
channel list: R, G, B
acesImageContainerFlag: 1
chromaticities: 0.7347, 0.2653, 0, 1, 0.0001, -0.077, 0.32168, 0.33767
compression: "none"
PixelAspectRatio: 1
screenWindowCenter: 0, 0
screenWindowWidth: 1
oiio:ColorSpace: "lin_ap0_scene"
oiio:subimages: 1
openexr:lineOrder: "increasingY"
WARNING: Image spec is not ACES Container compliant
oiiotool ERROR: -o : Cannot output non-compliant ACES Container in 'strict' mode.
Full command line was:
> oiiotool --create 4x4 3 -d half --compression none --ch R,G,B -sattrib oiio:ACESContainer strict -o strict-fail.exr
WARNING: Image spec is not ACES Container compliant
oiiotool ERROR: -o : Cannot output non-compliant ACES Container in 'strict' mode.
Full command line was:
> oiiotool --create 4x4 3 -d half --compression zip --ch B,G,R -sattrib oiio:ACESContainer strict -o strict-fail.exr
WARNING: Image spec is not ACES Container compliant
oiiotool ERROR: -o : Cannot output non-compliant ACES Container in 'strict' mode.
Full command line was:
> oiiotool --create 4x4 3 -d float --compression none --ch B,G,R -sattrib oiio:ACESContainer strict -o strict-fail.exr
17 changes: 17 additions & 0 deletions testsuite/openexr-suite/run.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,20 @@
# Check writing overscan and negative range
command += oiiotool("--create 64x64-16-16 3 -d half -o negoverscan.exr")
command += info_command("negoverscan.exr", safematch=True)

# Check ACES Container output for relaxed mode
command += oiiotool("--create 4x4 3 -d half --compression none --ch B,G,R -sattrib oiio:ACESContainer relaxed -o relaxed-out.exr")
command += oiiotool("relaxed-out.exr --echo {TOP[acesImageContainerFlag]}", failureok=True) # should give 1
command += oiiotool("--create 4x4 3 -d half --compression none --ch R,G,B -sattrib oiio:ACESContainer relaxed -o fail.exr")
command += oiiotool("fail.exr --echo {TOP[acesImageContainerFlag]}", failureok=True) # should be empty
command += oiiotool("--create 4x4 3 -d half --compression zip --ch B,G,R -sattrib oiio:ACESContainer relaxed -o fail.exr")
command += oiiotool("fail.exr --echo {TOP[acesImageContainerFlag]}", failureok=True) # should be empty
command += oiiotool("--create 4x4 3 -d float --compression none --ch B,G,R -sattrib oiio:ACESContainer relaxed -o fail.exr")
command += oiiotool("fail.exr --echo {TOP[acesImageContainerFlag]}", failureok=True) # should be empty

# Check ACES Container output for strict mode
command += oiiotool("--create 4x4 3 -d half --compression none --ch B,G,R -sattrib oiio:ACESContainer strict -o strict-out.exr")
command += info_command("strict-out.exr", safematch=True)
command += oiiotool("--create 4x4 3 -d half --compression none --ch R,G,B -sattrib oiio:ACESContainer strict -o strict-fail.exr", failureok=True)
command += oiiotool("--create 4x4 3 -d half --compression zip --ch B,G,R -sattrib oiio:ACESContainer strict -o strict-fail.exr", failureok=True)
command += oiiotool("--create 4x4 3 -d float --compression none --ch B,G,R -sattrib oiio:ACESContainer strict -o strict-fail.exr", failureok=True)
Loading