diff --git a/src/cmake/testing.cmake b/src/cmake/testing.cmake index 0e6c042769..aefb69d31c 100644 --- a/src/cmake/testing.cmake +++ b/src/cmake/testing.cmake @@ -475,6 +475,9 @@ macro (oiio_add_all_tests) # Self-contained decompression-bomb regression (ships its own tiny fixture); # exercises both the C++ and C-API readers via the openexr:core attribute. oiio_add_tests (openexr-bomb) + # Self-contained multi-part colorInteropID inheritance test (ships its own + # tiny fixture); exercises both the C++ and C-API readers. + oiio_add_tests (openexr-multipart-colorspace) # if (NOT DEFINED ENV{${PROJECT_NAME}_CI}) # oiio_add_tests (openexr-damaged # IMAGEDIR openexr-images diff --git a/src/doc/builtinplugins.md b/src/doc/builtinplugins.md index 8179fa9610..a354c56050 100644 --- a/src/doc/builtinplugins.md +++ b/src/doc/builtinplugins.md @@ -1746,6 +1746,14 @@ control aspects of the writing itself: While in `relaxed` mode, if the spec is non-compliant, `chromaticities` and `colorInteropID` will be set, but `acesImageContainerFlag` will NOT. + * - ``openexr:ColorInteropIDPolicy`` + - string + - One of `none` (default) or `strict`, taken from the first subimage. + In `strict` mode, if the spec is non-compliant with the ASWF Color + Interop Forum Recommendation for OpenEXR files, the output will + throw an error and avoid writing the image. For compliance, the + `colorInteropID` in all subimages must be either equal to the first + subimage, or be unspecified or set to `data`. * - ``oiio:RawColor`` - int - If nonzero, writing images with non-RGB color models (such as YCbCr) diff --git a/src/openexr.imageio/exr_pvt.h b/src/openexr.imageio/exr_pvt.h index 627634d65a..9ba0ba7c91 100644 --- a/src/openexr.imageio/exr_pvt.h +++ b/src/openexr.imageio/exr_pvt.h @@ -348,6 +348,7 @@ class OpenEXRInput final : public ImageInput { int m_miplevel; ///< What MIP level are we looking at? std::vector m_missingcolor; ///< Color for missing tile/scanline std::string m_filename; // filename, if known + std::string m_file_color_interop_id; ExrChunkCache m_chunkcache; void init() @@ -366,6 +367,7 @@ class OpenEXRInput final : public ImageInput { m_local_io.reset(); m_missingcolor.clear(); m_filename.clear(); + m_file_color_interop_id.clear(); } // Read scanlines [ybegin,yend) out of the chunk [cbegin,cend), decoding diff --git a/src/openexr.imageio/exrinput.cpp b/src/openexr.imageio/exrinput.cpp index 597b96a241..ccacae24f4 100644 --- a/src/openexr.imageio/exrinput.cpp +++ b/src/openexr.imageio/exrinput.cpp @@ -219,6 +219,17 @@ OpenEXRInput::valid_file(Filesystem::IOProxy* ioproxy) const +// Color space shared by all parts of the file, taken from the first part. +static std::string +file_color_interop_id(const Imf::MultiPartInputFile* multipart) +{ + const Imf::StringAttribute* attr + = multipart->header(0).findTypedAttribute( + "colorInteropID"); + return attr ? attr->value() : std::string(); +} + + bool OpenEXRInput::open(const std::string& name, ImageSpec& newspec, const ImageSpec& config) @@ -327,6 +338,8 @@ OpenEXRInput::open(const std::string& name, ImageSpec& newspec, m_subimage = -1; m_miplevel = -1; + m_file_color_interop_id = file_color_interop_id(m_input_multipart); + // Set up for the first subimage ("part"). This will trigger reading // information about all the parts. bool ok = seek_subimage(0, 0); @@ -743,8 +756,15 @@ OpenEXRInput::PartInfo::parse_header(OpenEXRInput* in, // Try to figure out the color space for some unambiguous cases if (spec.get_int_attribute("acesImageContainerFlag") == 1) { spec.set_colorspace("lin_ap0_scene"); - } else if (auto c = spec.find_attribute("colorInteropID", TypeString)) { - spec.set_colorspace(c->get_ustring()); + } else { + // Follow the color interop forum recommendation for OpenEXR files, + // inheriting the colorInteropID from the first part. + string_view interop_id = spec.get_string_attribute("colorInteropID"); + if (!interop_id.empty()) { + spec.set_colorspace(interop_id); + } else if (!in->m_file_color_interop_id.empty()) { + spec.set_colorspace(in->m_file_color_interop_id); + } } // Squash some problematic texture metadata if we suspect it's wrong diff --git a/src/openexr.imageio/exrinput_c.cpp b/src/openexr.imageio/exrinput_c.cpp index 6f84301b5b..e263496b2a 100644 --- a/src/openexr.imageio/exrinput_c.cpp +++ b/src/openexr.imageio/exrinput_c.cpp @@ -217,6 +217,7 @@ class OpenEXRCoreInput final : public ImageInput { int m_nsubimages; ///< How many subimages are there? std::vector m_missingcolor; ///< Color for missing tile/scanline std::string m_filename; // filename, if known + std::string m_file_color_interop_id; void init() { @@ -226,6 +227,7 @@ class OpenEXRCoreInput final : public ImageInput { m_local_io.reset(); m_missingcolor.clear(); m_filename.clear(); + m_file_color_interop_id.clear(); m_chunkcache.clear(); } @@ -349,6 +351,21 @@ OpenEXRCoreInput::valid_file_or_proxy(const std::string& filename, +// Color space shared by all parts of the file, taken from the first part. +static std::string +file_color_interop_id(exr_context_t ctxt) +{ + int32_t length = 0; + const char* interop = nullptr; + if (exr_attr_get_string(ctxt, 0, "colorInteropID", &length, &interop) + != EXR_ERR_SUCCESS + || !interop) + return std::string(); + + return std::string(interop, size_t(length)); +} + + bool OpenEXRCoreInput::open(const std::string& name, ImageSpec& newspec, const ImageSpec& config) @@ -448,6 +465,8 @@ OpenEXRCoreInput::open(const std::string& name, ImageSpec& newspec, m_subimage = -1; m_miplevel = -1; + m_file_color_interop_id = file_color_interop_id(m_exr_context); + // Set up for the first subimage ("part"). This will trigger reading // information about all the parts. bool ok = seek_subimage(0, 0); @@ -845,8 +864,15 @@ OpenEXRCoreInput::PartInfo::parse_header(OpenEXRCoreInput* in, // Try to figure out the color space for some unambiguous cases if (spec.get_int_attribute("acesImageContainerFlag") == 1) { spec.set_colorspace("lin_ap0_scene"); - } else if (auto c = spec.find_attribute("colorInteropID", TypeString)) { - spec.set_colorspace(c->get_ustring()); + } else { + // Follow the color interop forum recommendation for OpenEXR files, + // inheriting the colorInteropID from the first part. + string_view interop_id = spec.get_string_attribute("colorInteropID"); + if (!interop_id.empty()) { + spec.set_colorspace(interop_id); + } else if (!in->m_file_color_interop_id.empty()) { + spec.set_colorspace(in->m_file_color_interop_id); + } } // Squash some problematic texture metadata if we suspect it's wrong diff --git a/src/openexr.imageio/exroutput.cpp b/src/openexr.imageio/exroutput.cpp index 031e23419e..3a30a41afc 100644 --- a/src/openexr.imageio/exroutput.cpp +++ b/src/openexr.imageio/exroutput.cpp @@ -187,6 +187,9 @@ class OpenEXROutput final : public ImageOutput { // spec a bit. bool spec_to_header(ImageSpec& spec, int subimage, Imf::Header& header); + // Validate color interop IDs if openexr:ColorInteropIDPolicy is set. + bool validate_color_interop_ids(); + // Compute an OpenEXR PixelType from an OIIO TypeDesc Imf::PixelType imfpixeltype(TypeDesc type); @@ -710,6 +713,51 @@ OpenEXROutput::open(const std::string& name, const ImageSpec& userspec, } +bool +OpenEXROutput::validate_color_interop_ids() +{ + string_view policy = m_subimagespecs[0].get_string_attribute( + "openexr:ColorInteropIDPolicy", "none"); + if (policy == "none") + return true; + + if (policy != "strict") { + errorfmt("Unknown openexr:ColorInteropIDPolicy \"{}\"", policy); + return false; + } + + // Follow the color interop forum recommendation, where the colorInteropID + // of later parts must match the first part, except when "data" or missing. + // + // In the future, checkColorMetadata added in OpenEXR 3.5 can replace this. + string_view file_interop_id; + + for (size_t s = 0; s < m_headers.size(); ++s) { + const Imf::StringAttribute* attr + = m_headers[s].findTypedAttribute( + "colorInteropID"); + string_view interop_id = attr ? string_view(attr->value()) + : string_view(); + + if (s == 0) { + file_interop_id = interop_id; + continue; + } + + if (interop_id.empty() || interop_id == "data" + || interop_id == file_interop_id) + continue; + + errorfmt( + "OpenEXR subimage {} has color space \"{}\", different from \"{}\" in the first subimage", + s, interop_id, file_interop_id); + return false; + } + + return true; +} + + bool OpenEXROutput::open(const std::string& name, int subimages, @@ -758,6 +806,9 @@ OpenEXROutput::open(const std::string& name, int subimages, } } + if (!validate_color_interop_ids()) + return false; + m_spec = m_subimagespecs[0]; sanity_check_channelnames(); compute_pixeltypes(m_spec); @@ -1158,7 +1209,7 @@ static ExrMeta exr_meta_translation[] = { // user or from a file we read. ExrMeta("YResolution"), ExrMeta("planarconfig"), ExrMeta("type"), ExrMeta("tiles"), ExrMeta("chunkCount"), ExrMeta("maxSamplesPerPixel"), - ExrMeta("openexr:roundingmode") + ExrMeta("openexr:roundingmode"), ExrMeta("openexr:ColorInteropIDPolicy") }; diff --git a/testsuite/openexr-multipart-colorspace/ref/out.txt b/testsuite/openexr-multipart-colorspace/ref/out.txt new file mode 100644 index 0000000000..95d42526e1 --- /dev/null +++ b/testsuite/openexr-multipart-colorspace/ref/out.txt @@ -0,0 +1,338 @@ +Reading copy_from_first.exr +copy_from_first.exr : 4 x 4, 3 channel, half openexr + 4 subimages: 4x4 [h,h,h], 4x4 [h,h,h], 4x4 [h,h,h], 4x4 [h,h,h] + subimage 0: 4 x 4, 3 channel, half openexr + SHA-1: 2888D7692B43824B1F6B6AFEE923705DC3BE7B32 + channel list: R, G, B + colorInteropID: "lin_ap1_scene" + compression: "zip" + name: "beauty" + PixelAspectRatio: 1 + screenWindowCenter: 0, 0 + screenWindowWidth: 1 + oiio:ColorSpace: "lin_ap1_scene" + oiio:RowsPerChunk: 16 + oiio:subimagename: "beauty" + oiio:subimages: 4 + openexr:chunkCount: 1 + openexr:lineOrder: "increasingY" + subimage 1: 4 x 4, 3 channel, half openexr + SHA-1: 2C000460D7DBE3E3F8F015B585D48396D79A953C + channel list: R, G, B + compression: "zip" + name: "diffuse" + PixelAspectRatio: 1 + screenWindowCenter: 0, 0 + screenWindowWidth: 1 + oiio:ColorSpace: "lin_ap1_scene" + oiio:RowsPerChunk: 16 + oiio:subimagename: "diffuse" + oiio:subimages: 4 + openexr:chunkCount: 1 + openexr:lineOrder: "increasingY" + subimage 2: 4 x 4, 3 channel, half openexr + SHA-1: 09130898C63D0749C555654FBF1EF5CA59A79D07 + channel list: R, G, B + colorInteropID: "data" + compression: "zip" + name: "depth" + PixelAspectRatio: 1 + screenWindowCenter: 0, 0 + screenWindowWidth: 1 + oiio:ColorSpace: "data" + oiio:RowsPerChunk: 16 + oiio:subimagename: "depth" + oiio:subimages: 4 + openexr:chunkCount: 1 + openexr:lineOrder: "increasingY" + subimage 3: 4 x 4, 3 channel, half openexr + SHA-1: 5497DD3A644B9A44B5259A409346F9184A58891A + channel list: R, G, B + compression: "zip" + name: "specular" + PixelAspectRatio: 1 + screenWindowCenter: 0, 0 + screenWindowWidth: 1 + oiio:ColorSpace: "lin_ap1_scene" + oiio:RowsPerChunk: 16 + oiio:subimagename: "specular" + oiio:subimages: 4 + openexr:chunkCount: 1 + openexr:lineOrder: "increasingY" +Reading copy_from_first.exr +copy_from_first.exr : 4 x 4, 3 channel, half openexr + 4 subimages: 4x4 [h,h,h], 4x4 [h,h,h], 4x4 [h,h,h], 4x4 [h,h,h] + subimage 0: 4 x 4, 3 channel, half openexr + SHA-1: 2888D7692B43824B1F6B6AFEE923705DC3BE7B32 + channel list: R, G, B + colorInteropID: "lin_ap1_scene" + compression: "zip" + name: "beauty" + PixelAspectRatio: 1 + screenWindowCenter: 0, 0 + screenWindowWidth: 1 + oiio:ColorSpace: "lin_ap1_scene" + oiio:RowsPerChunk: 16 + oiio:subimagename: "beauty" + oiio:subimages: 4 + openexr:chunkCount: 1 + openexr:lineOrder: "increasingY" + subimage 1: 4 x 4, 3 channel, half openexr + SHA-1: 2C000460D7DBE3E3F8F015B585D48396D79A953C + channel list: R, G, B + compression: "zip" + name: "diffuse" + PixelAspectRatio: 1 + screenWindowCenter: 0, 0 + screenWindowWidth: 1 + oiio:ColorSpace: "lin_ap1_scene" + oiio:RowsPerChunk: 16 + oiio:subimagename: "diffuse" + oiio:subimages: 4 + openexr:chunkCount: 1 + openexr:lineOrder: "increasingY" + subimage 2: 4 x 4, 3 channel, half openexr + SHA-1: 09130898C63D0749C555654FBF1EF5CA59A79D07 + channel list: R, G, B + colorInteropID: "data" + compression: "zip" + name: "depth" + PixelAspectRatio: 1 + screenWindowCenter: 0, 0 + screenWindowWidth: 1 + oiio:ColorSpace: "data" + oiio:RowsPerChunk: 16 + oiio:subimagename: "depth" + oiio:subimages: 4 + openexr:chunkCount: 1 + openexr:lineOrder: "increasingY" + subimage 3: 4 x 4, 3 channel, half openexr + SHA-1: 5497DD3A644B9A44B5259A409346F9184A58891A + channel list: R, G, B + compression: "zip" + name: "specular" + PixelAspectRatio: 1 + screenWindowCenter: 0, 0 + screenWindowWidth: 1 + oiio:ColorSpace: "lin_ap1_scene" + oiio:RowsPerChunk: 16 + oiio:subimagename: "specular" + oiio:subimages: 4 + openexr:chunkCount: 1 + openexr:lineOrder: "increasingY" +Reading missing_first.exr +missing_first.exr : 4 x 4, 3 channel, half openexr + 3 subimages: 4x4 [h,h,h], 4x4 [h,h,h], 4x4 [h,h,h] + subimage 0: 4 x 4, 3 channel, half openexr + SHA-1: 2888D7692B43824B1F6B6AFEE923705DC3BE7B32 + channel list: R, G, B + compression: "zip" + name: "beauty" + PixelAspectRatio: 1 + screenWindowCenter: 0, 0 + screenWindowWidth: 1 + oiio:RowsPerChunk: 16 + oiio:subimagename: "beauty" + oiio:subimages: 3 + openexr:chunkCount: 1 + openexr:lineOrder: "increasingY" + subimage 1: 4 x 4, 3 channel, half openexr + SHA-1: 09130898C63D0749C555654FBF1EF5CA59A79D07 + channel list: R, G, B + colorInteropID: "data" + compression: "zip" + name: "depth" + PixelAspectRatio: 1 + screenWindowCenter: 0, 0 + screenWindowWidth: 1 + oiio:ColorSpace: "data" + oiio:RowsPerChunk: 16 + oiio:subimagename: "depth" + oiio:subimages: 3 + openexr:chunkCount: 1 + openexr:lineOrder: "increasingY" + subimage 2: 4 x 4, 3 channel, half openexr + SHA-1: 5497DD3A644B9A44B5259A409346F9184A58891A + channel list: R, G, B + compression: "zip" + name: "specular" + PixelAspectRatio: 1 + screenWindowCenter: 0, 0 + screenWindowWidth: 1 + oiio:RowsPerChunk: 16 + oiio:subimagename: "specular" + oiio:subimages: 3 + openexr:chunkCount: 1 + openexr:lineOrder: "increasingY" +Reading missing_first.exr +missing_first.exr : 4 x 4, 3 channel, half openexr + 3 subimages: 4x4 [h,h,h], 4x4 [h,h,h], 4x4 [h,h,h] + subimage 0: 4 x 4, 3 channel, half openexr + SHA-1: 2888D7692B43824B1F6B6AFEE923705DC3BE7B32 + channel list: R, G, B + compression: "zip" + name: "beauty" + PixelAspectRatio: 1 + screenWindowCenter: 0, 0 + screenWindowWidth: 1 + oiio:RowsPerChunk: 16 + oiio:subimagename: "beauty" + oiio:subimages: 3 + openexr:chunkCount: 1 + openexr:lineOrder: "increasingY" + subimage 1: 4 x 4, 3 channel, half openexr + SHA-1: 09130898C63D0749C555654FBF1EF5CA59A79D07 + channel list: R, G, B + colorInteropID: "data" + compression: "zip" + name: "depth" + PixelAspectRatio: 1 + screenWindowCenter: 0, 0 + screenWindowWidth: 1 + oiio:ColorSpace: "data" + oiio:RowsPerChunk: 16 + oiio:subimagename: "depth" + oiio:subimages: 3 + openexr:chunkCount: 1 + openexr:lineOrder: "increasingY" + subimage 2: 4 x 4, 3 channel, half openexr + SHA-1: 5497DD3A644B9A44B5259A409346F9184A58891A + channel list: R, G, B + compression: "zip" + name: "specular" + PixelAspectRatio: 1 + screenWindowCenter: 0, 0 + screenWindowWidth: 1 + oiio:RowsPerChunk: 16 + oiio:subimagename: "specular" + oiio:subimages: 3 + openexr:chunkCount: 1 + openexr:lineOrder: "increasingY" +Reading src/multipart_colorspace_data_first.exr +src/multipart_colorspace_data_first.exr : 4 x 4, 3 channel, half openexr + 4 subimages: 4x4 [h,h,h], 4x4 [h,h,h], 4x4 [h,h,h], 4x4 [h,h,h] + subimage 0: 4 x 4, 3 channel, half openexr + SHA-1: C49A9785B2243F2F080DAAD1747F119ACCECCFA5 + channel list: R, G, B + colorInteropID: "data" + compression: "zip" + name: "data0" + PixelAspectRatio: 1 + screenWindowCenter: 0, 0 + screenWindowWidth: 1 + oiio:ColorSpace: "data" + oiio:RowsPerChunk: 16 + oiio:subimagename: "data0" + oiio:subimages: 4 + openexr:chunkCount: 1 + openexr:lineOrder: "increasingY" + subimage 1: 4 x 4, 3 channel, half openexr + SHA-1: C49A9785B2243F2F080DAAD1747F119ACCECCFA5 + channel list: R, G, B + compression: "zip" + name: "missing1" + PixelAspectRatio: 1 + screenWindowCenter: 0, 0 + screenWindowWidth: 1 + oiio:ColorSpace: "data" + oiio:RowsPerChunk: 16 + oiio:subimagename: "missing1" + oiio:subimages: 4 + openexr:chunkCount: 1 + openexr:lineOrder: "increasingY" + subimage 2: 4 x 4, 3 channel, half openexr + SHA-1: C49A9785B2243F2F080DAAD1747F119ACCECCFA5 + channel list: R, G, B + colorInteropID: "lin_ap1_scene" + compression: "zip" + name: "color2" + PixelAspectRatio: 1 + screenWindowCenter: 0, 0 + screenWindowWidth: 1 + oiio:ColorSpace: "lin_ap1_scene" + oiio:RowsPerChunk: 16 + oiio:subimagename: "color2" + oiio:subimages: 4 + openexr:chunkCount: 1 + openexr:lineOrder: "increasingY" + subimage 3: 4 x 4, 3 channel, half openexr + SHA-1: C49A9785B2243F2F080DAAD1747F119ACCECCFA5 + channel list: R, G, B + compression: "zip" + name: "missing3" + PixelAspectRatio: 1 + screenWindowCenter: 0, 0 + screenWindowWidth: 1 + oiio:ColorSpace: "data" + oiio:RowsPerChunk: 16 + oiio:subimagename: "missing3" + oiio:subimages: 4 + openexr:chunkCount: 1 + openexr:lineOrder: "increasingY" +Reading src/multipart_colorspace_data_first.exr +src/multipart_colorspace_data_first.exr : 4 x 4, 3 channel, half openexr + 4 subimages: 4x4 [h,h,h], 4x4 [h,h,h], 4x4 [h,h,h], 4x4 [h,h,h] + subimage 0: 4 x 4, 3 channel, half openexr + SHA-1: C49A9785B2243F2F080DAAD1747F119ACCECCFA5 + channel list: R, G, B + colorInteropID: "data" + compression: "zip" + name: "data0" + PixelAspectRatio: 1 + screenWindowCenter: 0, 0 + screenWindowWidth: 1 + oiio:ColorSpace: "data" + oiio:RowsPerChunk: 16 + oiio:subimagename: "data0" + oiio:subimages: 4 + openexr:chunkCount: 1 + openexr:lineOrder: "increasingY" + subimage 1: 4 x 4, 3 channel, half openexr + SHA-1: C49A9785B2243F2F080DAAD1747F119ACCECCFA5 + channel list: R, G, B + compression: "zip" + name: "missing1" + PixelAspectRatio: 1 + screenWindowCenter: 0, 0 + screenWindowWidth: 1 + oiio:ColorSpace: "data" + oiio:RowsPerChunk: 16 + oiio:subimagename: "missing1" + oiio:subimages: 4 + openexr:chunkCount: 1 + openexr:lineOrder: "increasingY" + subimage 2: 4 x 4, 3 channel, half openexr + SHA-1: C49A9785B2243F2F080DAAD1747F119ACCECCFA5 + channel list: R, G, B + colorInteropID: "lin_ap1_scene" + compression: "zip" + name: "color2" + PixelAspectRatio: 1 + screenWindowCenter: 0, 0 + screenWindowWidth: 1 + oiio:ColorSpace: "lin_ap1_scene" + oiio:RowsPerChunk: 16 + oiio:subimagename: "color2" + oiio:subimages: 4 + openexr:chunkCount: 1 + openexr:lineOrder: "increasingY" + subimage 3: 4 x 4, 3 channel, half openexr + SHA-1: C49A9785B2243F2F080DAAD1747F119ACCECCFA5 + channel list: R, G, B + compression: "zip" + name: "missing3" + PixelAspectRatio: 1 + screenWindowCenter: 0, 0 + screenWindowWidth: 1 + oiio:ColorSpace: "data" + oiio:RowsPerChunk: 16 + oiio:subimagename: "missing3" + oiio:subimages: 4 + openexr:chunkCount: 1 + openexr:lineOrder: "increasingY" +oiiotool ERROR: -o : OpenEXR subimage 1 has color space "lin_rec709_scene", different from "lin_ap1_scene" in the first subimage +Full command line was: +> oiiotool --pattern constant:color=1,0,0 4x4 3 -d half --attrib oiio:ColorSpace lin_ap1_scene --attrib oiio:subimagename beauty -sattrib openexr:ColorInteropIDPolicy strict --pattern constant:color=0,1,0 4x4 3 -d half --attrib oiio:ColorSpace lin_rec709_scene --attrib oiio:subimagename specular --siappendall -o mismatched.exr +oiiotool ERROR: -o : OpenEXR subimage 1 has color space "lin_ap1_scene", different from "data" in the first subimage +Full command line was: +> oiiotool --pattern constant:color=0.25,0.25,0.25 4x4 3 -d half --attrib oiio:ColorSpace data --attrib oiio:subimagename depth -sattrib openexr:ColorInteropIDPolicy strict --pattern constant:color=1,0,0 4x4 3 -d half --attrib oiio:ColorSpace lin_ap1_scene --attrib oiio:subimagename beauty --pattern constant:color=0,1,0 4x4 3 -d half --attrib oiio:ColorSpace lin_ap1_scene --attrib oiio:subimagename specular --siappendall -o data_first.exr diff --git a/testsuite/openexr-multipart-colorspace/run.py b/testsuite/openexr-multipart-colorspace/run.py new file mode 100644 index 0000000000..9bc082c7d0 --- /dev/null +++ b/testsuite/openexr-multipart-colorspace/run.py @@ -0,0 +1,81 @@ +#!/usr/bin/env python + +# Copyright Contributors to the OpenImageIO project. +# SPDX-License-Identifier: Apache-2.0 +# https://github.com/AcademySoftwareFoundation/OpenImageIO + + +redirect = ' >> out.txt 2>&1 ' + +# Test handling of colorInteropID in multi-part files. + +# Parts: "lin_ap1_scene", missing, "data", missing +command += oiiotool("--pattern constant:color=1,0,0 4x4 3 -d half " + "--attrib oiio:ColorSpace lin_ap1_scene --attrib oiio:subimagename beauty " + "--pattern constant:color=0,1,0 4x4 3 -d half " + "--eraseattrib oiio:ColorSpace --attrib oiio:subimagename diffuse " + "--pattern constant:color=0.25,0.25,0.25 4x4 3 -d half " + "--attrib oiio:ColorSpace data --attrib oiio:subimagename depth " + "--pattern constant:color=0,0,1 4x4 3 -d half " + "--eraseattrib oiio:ColorSpace --attrib oiio:subimagename specular " + "--siappendall -o copy_from_first.exr") +command += info_command("copy_from_first.exr", + extraargs="-oiioattrib openexr:core 0", safematch=True) +command += info_command("copy_from_first.exr", + extraargs="-oiioattrib openexr:core 1", safematch=True) + +# Parts: missing, "data", missing +command += oiiotool("--pattern constant:color=1,0,0 4x4 3 -d half " + "--eraseattrib oiio:ColorSpace --attrib oiio:subimagename beauty " + "--pattern constant:color=0.25,0.25,0.25 4x4 3 -d half " + "--attrib oiio:ColorSpace data --attrib oiio:subimagename depth " + "--pattern constant:color=0,0,1 4x4 3 -d half " + "--eraseattrib oiio:ColorSpace --attrib oiio:subimagename specular " + "--siappendall -o missing_first.exr") +command += info_command("missing_first.exr", + extraargs="-oiioattrib openexr:core 0", safematch=True) +command += info_command("missing_first.exr", + extraargs="-oiioattrib openexr:core 1", safematch=True) + +# Parts: "data", missing, "lin_ap1_scene", missing +# Not valid according to the CIF recommendation, but can be read anyway. +command += info_command("src/multipart_colorspace_data_first.exr", + extraargs="-oiioattrib openexr:core 0", safematch=True) +command += info_command("src/multipart_colorspace_data_first.exr", + extraargs="-oiioattrib openexr:core 1", safematch=True) + +# Parts: "lin_ap1_scene", "lin_ap1_scene" +command += oiiotool("--pattern constant:color=1,0,0 4x4 3 -d half " + "--attrib oiio:ColorSpace lin_ap1_scene --attrib oiio:subimagename beauty " + "-sattrib openexr:ColorInteropIDPolicy strict " + "--pattern constant:color=0,1,0 4x4 3 -d half " + "--attrib oiio:ColorSpace lin_ap1_scene --attrib oiio:subimagename specular " + "--siappendall -o matched.exr") + +# Parts: "lin_ap1_scene", "lin_rec709_scene" +# Not valid according to the CIF recommendation, error with strict policy. +command += oiiotool("--pattern constant:color=1,0,0 4x4 3 -d half " + "--attrib oiio:ColorSpace lin_ap1_scene --attrib oiio:subimagename beauty " + "-sattrib openexr:ColorInteropIDPolicy strict " + "--pattern constant:color=0,1,0 4x4 3 -d half " + "--attrib oiio:ColorSpace lin_rec709_scene --attrib oiio:subimagename specular " + "--siappendall -o mismatched.exr", failureok=True) + +# Parts: "lin_ap1_scene", missing +command += oiiotool("--pattern constant:color=1,0,0 4x4 3 -d half " + "--attrib oiio:ColorSpace lin_ap1_scene --attrib oiio:subimagename beauty " + "-sattrib openexr:ColorInteropIDPolicy strict " + "--pattern constant:color=0,1,0 4x4 3 -d half " + "--attrib oiio:subimagename specular " + "--siappendall -o missing_second.exr") + +# Parts: "data", "lin_ap1_scene", "lin_ap1_scene" +# Not valid according to the CIF recommendation, error with strict policy. +command += oiiotool("--pattern constant:color=0.25,0.25,0.25 4x4 3 -d half " + "--attrib oiio:ColorSpace data --attrib oiio:subimagename depth " + "-sattrib openexr:ColorInteropIDPolicy strict " + "--pattern constant:color=1,0,0 4x4 3 -d half " + "--attrib oiio:ColorSpace lin_ap1_scene --attrib oiio:subimagename beauty " + "--pattern constant:color=0,1,0 4x4 3 -d half " + "--attrib oiio:ColorSpace lin_ap1_scene --attrib oiio:subimagename specular " + "--siappendall -o data_first.exr", failureok=True) diff --git a/testsuite/openexr-multipart-colorspace/src/multipart_colorspace_data_first.exr b/testsuite/openexr-multipart-colorspace/src/multipart_colorspace_data_first.exr new file mode 100644 index 0000000000..b6769d4b90 Binary files /dev/null and b/testsuite/openexr-multipart-colorspace/src/multipart_colorspace_data_first.exr differ