Compare commits

...
69 Commits
Author SHA1 Message Date
Cary Phillips a1a00ffeec Add PR 1674 to notes
Signed-off-by: Cary Phillips <cary@ilm.com>
2024-03-25 11:27:30 -07:00
Peter StenetegandCary Phillips edc86ed415 CMake packaging fix. (#1674)
* CMake packaging fix.

Add find_dependency for the new dependency libdeflate, and remove the REQUIRED flag. find_dependency will pickup the correct flag from the calling find_package call.

Signed-off-by: Peter Steneteg <peter@steneteg.se>

* find_dependency only if libdeflate_FOUND

Signed-off-by: Cary Phillips <cary@ilm.com>

* Need to expand the libdeflate_FOUND variable at configure_file time

Signed-off-by: Peter Steneteg <peter@steneteg.se>

---------

Signed-off-by: Peter Steneteg <peter@steneteg.se>
Signed-off-by: Cary Phillips <cary@ilm.com>
Co-authored-by: Cary Phillips <cary@ilm.com>
2024-03-25 11:20:18 -07:00
Cary Phillips 7239a2652f Fix merge of website_workflow.yml
Signed-off-by: Cary Phillips <cary@ilm.com>
2024-03-23 12:48:55 -07:00
Cary Phillips 53786883ac Add additional PRs to to release notes
Signed-off-by: Cary Phillips <cary@ilm.com>
2024-03-23 12:35:49 -07:00
Cary Phillips a830413002 Improve workflow filters (#1668)
Run the ci_workflow and website_workflow only on changes to the
appropriate source files and workflow files.

Signed-off-by: Cary Phillips <cary@ilm.com>
2024-03-23 12:31:32 -07:00
Aliaksei UrbanskiandCary Phillips c8b731abf5 🐛 Remove the OPENEXR_INSTALL_EXAMPLES CMake option (#1666)
The OPENEXR_INSTALL_EXAMPLES option is misleading and doesn't have any effect.
It might be safely removed in favor of the OPENEXR_BUILD_EXAMPLES option.

Signed-off-by: Aliaksei Urbanski <aliaksei.urbanski@gmail.com>
Co-authored-by: Cary Phillips <cary@ilm.com>
2024-03-23 12:29:54 -07:00
Cary Phillips cbb0ca61cd Initialize _ySampling to 0 (#1662)
Caught by the santizer as potential use-before-initialization

Signed-off-by: Cary Phillips <cary@ilm.com>
2024-03-23 12:29:45 -07:00
Cary Phillips 454cd4d62a Use size_t as iterator instead of int (#1659)
Caught by the sanitizer

Signed-off-by: Cary Phillips <cary@ilm.com>
2024-03-23 12:29:37 -07:00
Cary Phillips 1969623dd4 Bump version for v3.2.4
Signed-off-by: Cary Phillips <cary@ilm.com>
2024-03-23 12:06:59 -07:00
Cary Phillips afb5b6fc02 Release notes for v3.2.4
Signed-off-by: Cary Phillips <cary@ilm.com>
2024-03-23 12:06:24 -07:00
Kimball Thurston 8d25bedd83 Fix typo causing prefix len to be wrong (#1684)
When constructing the decompress side of dwaa/b compression support,
simple typo when computing the prefix length as distance between
pointers causing the length to be wrong, triggering csc decompression
values to be wrong when decompressed.

Signed-off-by: Kimball Thurston <kdt3rd@gmail.com>
2024-03-23 12:04:45 -07:00
Cary Phillips 1a59a1a7f6 Release notes for v3.1.13
Signed-off-by: Cary Phillips <cary@ilm.com>
2024-03-23 12:03:16 -07:00
Cary Phillips e4b8d0226a Remove debugging CIBW_BUILD: cp312
Signed-off-by: Cary Phillips <cary@ilm.com>
2024-03-06 16:38:54 -08:00
Cary Phillips d5c8b71dbe Update release notes
Signed-off-by: Cary Phillips <cary@ilm.com>
2024-03-04 09:02:33 -08:00
Cary Phillips cca5eb44a0 Bump Imath to 3.1.11
Signed-off-by: Cary Phillips <cary@ilm.com>
2024-03-04 09:02:16 -08:00
Cary Phillips f627f72dae Add recent releases to website news (#1651)
Signed-off-by: Cary Phillips <cary@ilm.com>
2024-03-04 08:54:19 -08:00
Cary Phillips 0ef34bbc6e Run release-sign workflow on published instead of created (#1650)
Signed-off-by: Cary Phillips <cary@ilm.com>
2024-03-04 08:54:10 -08:00
Kimball Thurston 58f2ab0fbb Allow partial chunk tables (#1653)
* fix sporadic UB in dwa

Signed-off-by: Kimball Thurston <kdt3rd@gmail.com>

* Allow partial chunk tables

This will fix #1642, re-enabling partial chunk tables during
reconstruction (at least for the last part read). Correctly handles
reconstructing tables where discovered tiles are shifted from expected
indices

Signed-off-by: Kimball Thurston <kdt3rd@gmail.com>

* Avoid OOB write

When reconstructing chunk table with corrupt chunks, avoid writing out
of bounds

Signed-off-by: Kimball Thurston <kdt3rd@gmail.com>

* avoid undefined behavior

unaligned reads are UB, use (__builtin_) memcpy

Signed-off-by: Kimball Thurston <kdt3rd@gmail.com>

---------

Signed-off-by: Kimball Thurston <kdt3rd@gmail.com>
2024-03-04 08:53:48 -08:00
Kimball Thurston efdfcff21a Fix 0 samples in deep data (#1652)
Add some additional safety checks, but properly handle when there are 0
samples in a deep scanline / tile (chunk)

Should fix #1639 and OSS-Fuzz 66612

Signed-off-by: Kimball Thurston <kdt3rd@gmail.com>
2024-03-04 08:53:41 -08:00
Cary Phillips 09914784e2 Remove -Dsonar.login from sonar-scanner command line (#1647)
Addresses #1646

Signed-off-by: Cary Phillips <cary@ilm.com>
2024-03-04 08:53:34 -08:00
Cary Phillips d9f606e484 Fix memory leaks in exrstdattr and example code (#1649)
Caught by the address sanitizer, worth addressing in the example code
as good programming practice.

Signed-off-by: Cary Phillips <cary@ilm.com>
2024-03-04 08:53:27 -08:00
Cary Phillips 9c3e18aade Add instructions for creating signed releases/tags (#1637)
* Add instructions for creating signed releases/tags

Signed-off-by: Cary Phillips <cary@ilm.com>

* Add openexr.keyring

Signed-off-by: Cary Phillips <cary@ilm.com>

* Sign & upload release artifacts via sigstore

Signed-off-by: Cary Phillips <cary@ilm.com>

* Add release-sign.yml

Signed-off-by: Cary Phillips <cary@ilm.com>

* Use gh-action-sigstore-python

Signed-off-by: Cary Phillips <cary@ilm.com>

---------

Signed-off-by: Cary Phillips <cary@ilm.com>
2024-03-04 08:53:22 -08:00
Jean-Christophe Morin 27fb7abe70 Use component for python (#1643)
* Add "COMPONENT python" to install() the python bindings and tell scikit-build-core to only install the python component

Signed-off-by: Jean-Christophe Morin <jean_christophe_morin@hotmail.com>

* Update scikit-build-core to 0.8.1

Signed-off-by: Jean-Christophe Morin <jean_christophe_morin@hotmail.com>

* Fix typo

Signed-off-by: Jean-Christophe Morin <jean_christophe_morin@hotmail.com>

---------

Signed-off-by: Jean-Christophe Morin <jean_christophe_morin@hotmail.com>
2024-03-04 08:53:16 -08:00
Cary Phillips 690ee3edf7 Fix version tag for python wheel sdist (#1640)
* Fix version tag for sdist

Signed-off-by: Cary Phillips <cary@ilm.com>

* Don't set OPENEXR_RELEASE_CANDIDATE_TAG for the non-test publish

Signed-off-by: Cary Phillips <cary@ilm.com>

---------

Signed-off-by: Cary Phillips <cary@ilm.com>
2024-03-04 08:53:08 -08:00
17811af04e Build python wheels via scikit-build-core (#1629)
* Build python wheels via scikit-build-core

This converts the setuptools configuration for building python wheels
to use scikit-build-core, which has better support for CMake. There is
no more setup.py; the configuration is entirely in `pyproject.toml`
and the compile/link is done exclusively via cmake.

The build/publish policy is:

* A PR that changes any of the python wheel source/configuration
  (src/wrappers/python/* or .github/workflows/python-wheels.yml)
  triggers a build as a check.

* PRs that change other library source do *not* trigger a build of the
  python wheels. Note that the primary CI workflow does build and test
  the bindings, although only for a single python version on a single
  arch for Linux/macOS/Windows. The wheel building validates multiple
  python versions and architectures, but involves signifant
  computation/time.  Currently, the python wheels are a thin wrapper
  about basic read/write functions that don't add significant
  additional functionality to the library. Any potential problem will
  almost certainly get caught by the primary CI.

* A tag of the form `v3.[0-9]+.[0-9]+-rc*` (e.g. `v3.2.4-rc`) triggers
  a full build of the wheels followed by a publish to
  `test.pypi.org`. This validates release candidates.

* Publishing a release triggers a full build of the wheels followed by
  a publish to `pypi.org`.

Signed-off-by: Cary Phillips <cary@ilm.com>

* Add custom README.md for pypi.org

Signed-off-by: Cary Phillips <cary@ilm.com>

* fix typo

Signed-off-by: Cary Phillips <cary@ilm.com>

* reference src/wrappers/python/README.md in pyproject.toml

Signed-off-by: Cary Phillips <cary@ilm.com>

* Add copyright notice

Signed-off-by: Cary Phillips <cary@ilm.com>

* Update pyproject.toml

Co-authored-by: Jean-Christophe Morin <38703886+JeanChristopheMorinPerso@users.noreply.github.com>
Signed-off-by: Cary Phillips <cary@ilm.com>

* Update pyproject.toml

Co-authored-by: Jean-Christophe Morin <38703886+JeanChristopheMorinPerso@users.noreply.github.com>
Signed-off-by: Cary Phillips <cary@ilm.com>

* Update src/wrappers/python/CMakeLists.txt

Co-authored-by: Jean-Christophe Morin <38703886+JeanChristopheMorinPerso@users.noreply.github.com>
Signed-off-by: Cary Phillips <cary@ilm.com>

* Add uninstall target (#1624)

* Add uninstall target

Satisfy the OpenSSF Best Practices Badge requirement for an
insta/uninstall process:
https://www.bestpractices.dev/en/criteria/1#1.installation_common

CMake does not support a standard "uninstall" target, but the
community recommends implementing an "uninstal" target that remove files named in the
`install_manifest.txt`:
https://gitlab.kitware.com/cmake/community/-/wikis/FAQ#can-i-do-make-uninstall-with-cmake

However, our existing process of installing the symlink to the "bare"
library, i.e. the symlink from libImath-3_2.so to libImath.so, fails
to add the symlink to the manifest, so "make uninstall" misses the
symlink. The existing mechanism use "install(CODE execute_process(cmake -E create_symlink))".

This changes that to use a simpler "file(CREATE_LINK)" and
"install(FILES)" to accomplish the same thing while also registering
the symlink the the manifest.

Also, this fixes an issue where `OpenEXRConfig.h` was passed to
`install()` twice, producing two entries in `install_manifest.txt`.

Signed-off-by: Cary Phillips <cary@ilm.com>

* mention uninstall in install instructions

Signed-off-by: Cary Phillips <cary@ilm.com>

* poke

Signed-off-by: Cary Phillips <cary@ilm.com>

* COPY_ON_ERROR

Signed-off-by: Cary Phillips <cary@ilm.com>

* clarify the uninstall instructions

Signed-off-by: Cary Phillips <cary@ilm.com>

---------

Signed-off-by: Cary Phillips <cary@ilm.com>

* Add cmake.targets and OPENEXR_INSTALL=OFF

Signed-off-by: Cary Phillips <cary@ilm.com>

* INSTALL_TOOLS=OFF

Signed-off-by: Cary Phillips <cary@ilm.com>

* propogate OPENEXR_INSTALL to Imath

Signed-off-by: Cary Phillips <cary@ilm.com>

* test1

Signed-off-by: Cary Phillips <cary@ilm.com>

* OPENEXR_INSTALL_PKG_CONFIG

Signed-off-by: Cary Phillips <cary@ilm.com>

* Fix CVE 2023 5841 (#1627)

* enable deep file checks for core

Signed-off-by: Kimball Thurston <kdt3rd@gmail.com>

* fix possible int overflow

Signed-off-by: Kimball Thurston <kdt3rd@gmail.com>

* fix validation of deep sample counts

Addresses CVE-2023-5841, fixing sample count check to not only check
against 0 but previous sample as well.

Signed-off-by: Kimball Thurston <kdt3rd@gmail.com>

* add clarifying comment

Signed-off-by: Kimball Thurston <kdt3rd@gmail.com>

---------

Signed-off-by: Kimball Thurston <kdt3rd@gmail.com>

* Bazel support: Bump Imath to 3.1.10 (#1626)

Signed-off-by: Vertexwahn <julian.amann@tum.de>

* Document security expectations (#1623)

* Document security expectations

Signed-off-by: Cary Phillips <cary@ilm.com>

* Menion Imath as a dependency

Signed-off-by: Cary Phillips <cary@ilm.com>

* Update SECURITY.md

Co-authored-by: Nick Porcino <meshula@hotmail.com>
Signed-off-by: Cary Phillips <cary@ilm.com>

* change 'Threat Model' to 'Potential Vulnerabilties'

Signed-off-by: Cary Phillips <cary@ilm.com>

* Mention GitHub issue as fallback security contact

Signed-off-by: Cary Phillips <cary@ilm.com>

* github security advisory

Signed-off-by: Cary Phillips <cary@ilm.com>

* mention exrcheck

Signed-off-by: Cary Phillips <cary@ilm.com>

---------

Signed-off-by: Cary Phillips <cary@ilm.com>
Co-authored-by: Nick Porcino <meshula@hotmail.com>

* Add uninstall target (#1624)

* Add uninstall target

Satisfy the OpenSSF Best Practices Badge requirement for an
insta/uninstall process:
https://www.bestpractices.dev/en/criteria/1#1.installation_common

CMake does not support a standard "uninstall" target, but the
community recommends implementing an "uninstal" target that remove files named in the
`install_manifest.txt`:
https://gitlab.kitware.com/cmake/community/-/wikis/FAQ#can-i-do-make-uninstall-with-cmake

However, our existing process of installing the symlink to the "bare"
library, i.e. the symlink from libImath-3_2.so to libImath.so, fails
to add the symlink to the manifest, so "make uninstall" misses the
symlink. The existing mechanism use "install(CODE execute_process(cmake -E create_symlink))".

This changes that to use a simpler "file(CREATE_LINK)" and
"install(FILES)" to accomplish the same thing while also registering
the symlink the the manifest.

Also, this fixes an issue where `OpenEXRConfig.h` was passed to
`install()` twice, producing two entries in `install_manifest.txt`.

Signed-off-by: Cary Phillips <cary@ilm.com>

* mention uninstall in install instructions

Signed-off-by: Cary Phillips <cary@ilm.com>

* poke

Signed-off-by: Cary Phillips <cary@ilm.com>

* COPY_ON_ERROR

Signed-off-by: Cary Phillips <cary@ilm.com>

* clarify the uninstall instructions

Signed-off-by: Cary Phillips <cary@ilm.com>

---------

Signed-off-by: Cary Phillips <cary@ilm.com>

* Remove snyk-scan-pr.yml (#1631)

This workflow is causing errors on each PR:

  Snyk is missing auth token in order to run inside CI. You must include your API token as an environment value: `SNYK_TOKEN=12345678`
  Error: Process completed with exit code 2.

As discussed on #1608, the preferred workflow will run weekly, not on PR.

Signed-off-by: Cary Phillips <cary@ilm.com>

* fix issue with unpacking sample counts (#1630)

When unpacking sample counts as "individual" counts (no longer
monotonic), it writes the total sample count to a value 1 past the
individual sample counts, but this is not in the packed data, so do not
expect to unpack that many values. The buffer just needs to be allocated
one value larger to avoid write past end of buffer which is taken care
of in the update_pack_unpack_ptrs function

Signed-off-by: Kimball Thurston <kdt3rd@gmail.com>

* adjust checks for core to better match c++ checks (#1632)

The core checks were not setting the same image / tile size limits and
not disabling reads at quite the same level.

Note: the core check does not read the entire image into a contiguous
slice, so does not replicate the maximum deep sample checks in the same
way, this is a source of potential false-negative failures

This should address OSS-Fuzz 66491 and 66489 (different forms of the
same failure where a large sample size allocation was happening), and
are only constrained memory (2.5Gb) issues.

Signed-off-by: Kimball Thurston <kdt3rd@gmail.com>

* Fix install of symlink (#1633)

PR #1624 caused the .so symlink without the `OPENEXR_LIB_SUFFIX`
(e.g. libOpenEXR.so which links to libOpenEXR-3_2.so) to get created
in the wrong directory. This caused certain invocations of cmake to
fail, even though the invocation in the CI succeeded. It's not at all
clear why.

This also changes the CI to invoke cmake in the way that previously
failed (e.g. from the top-level directory with `-B` and `-S`), as an additional check.

Signed-off-by: Cary Phillips <cary@ilm.com>

* adds a shortcut to avoid reconstructing every call (#1634)

When there is a loop trying to get scan / tile info that is ignoring
return values, add a shortcut to avoid trying to reconstruct the chunk
table every time. This will still respect the strict header flag, either
returning an error immediately (strict), or (non-strict) enabling a
multi-part file with only partially corrupt parts to work.

Signed-off-by: Kimball Thurston <kdt3rd@gmail.com>

* check and control reduceMemory and reduceTime in stream mode (#1635)

exrcheck by default uses file mode, but the fuzzer and exrcheck -s use
stream mode, need to respect the memory and time flags consistently on
that path as well.

Will address OSS-Fuzz 66612, although real fix underlying is in #1634

Signed-off-by: Kimball Thurston <kdt3rd@gmail.com>

* Update .github/workflows/python-wheels-publish-test.yml

Co-authored-by: Jean-Christophe Morin <38703886+JeanChristopheMorinPerso@users.noreply.github.com>
Signed-off-by: Cary Phillips <cary@ilm.com>

* Add sdist

Signed-off-by: Cary Phillips <cary@ilm.com>

* Update .github/workflows/python-wheels-publish-test.yml

Co-authored-by: Jean-Christophe Morin <38703886+JeanChristopheMorinPerso@users.noreply.github.com>
Signed-off-by: Cary Phillips <cary@ilm.com>

* fix sdist; remove debugging

Signed-off-by: Cary Phillips <cary@ilm.com>

---------

Signed-off-by: Cary Phillips <cary@ilm.com>
Signed-off-by: Kimball Thurston <kdt3rd@gmail.com>
Signed-off-by: Vertexwahn <julian.amann@tum.de>
Co-authored-by: Jean-Christophe Morin <38703886+JeanChristopheMorinPerso@users.noreply.github.com>
Co-authored-by: Kimball Thurston <kdt3rd@gmail.com>
Co-authored-by: Vertexwahn <julian.amann@tum.de>
Co-authored-by: Nick Porcino <meshula@hotmail.com>
2024-02-15 17:19:01 -08:00
John Mertic 2d3fce400b Do synk scans weekly on Sunday mornings (#1636)
Signed-off-by: John Mertic <jmertic@linuxfoundation.org>
2024-02-15 17:19:01 -08:00
Cary Phillips c3804276bb Release notes for v3.2.3
Signed-off-by: Cary Phillips <cary@ilm.com>
2024-02-15 17:19:01 -08:00
Cary Phillips 6a0a5c0143 Bump version for v3.2.3
Signed-off-by: Cary Phillips <cary@ilm.com>
2024-02-15 17:19:01 -08:00
Kimball Thurston 0e46554b2a adds a shortcut to avoid reconstructing every call (#1634)
When there is a loop trying to get scan / tile info that is ignoring
return values, add a shortcut to avoid trying to reconstruct the chunk
table every time. This will still respect the strict header flag, either
returning an error immediately (strict), or (non-strict) enabling a
multi-part file with only partially corrupt parts to work.

Signed-off-by: Kimball Thurston <kdt3rd@gmail.com>
2024-02-15 17:19:01 -08:00
Cary Phillips 441cf460a9 Fix install of symlink (#1633)
PR #1624 caused the .so symlink without the `OPENEXR_LIB_SUFFIX`
(e.g. libOpenEXR.so which links to libOpenEXR-3_2.so) to get created
in the wrong directory. This caused certain invocations of cmake to
fail, even though the invocation in the CI succeeded. It's not at all
clear why.

This also changes the CI to invoke cmake in the way that previously
failed (e.g. from the top-level directory with `-B` and `-S`), as an additional check.

Signed-off-by: Cary Phillips <cary@ilm.com>
2024-02-15 17:19:01 -08:00
Cary Phillips 4ae29178d3 Remove snyk-scan-pr.yml (#1631)
This workflow is causing errors on each PR:

  Snyk is missing auth token in order to run inside CI. You must include your API token as an environment value: `SNYK_TOKEN=12345678`
  Error: Process completed with exit code 2.

As discussed on #1608, the preferred workflow will run weekly, not on PR.

Signed-off-by: Cary Phillips <cary@ilm.com>
2024-02-15 17:19:01 -08:00
Cary Phillips daa66664e3 Add uninstall target (#1624)
* Add uninstall target

Satisfy the OpenSSF Best Practices Badge requirement for an
insta/uninstall process:
https://www.bestpractices.dev/en/criteria/1#1.installation_common

CMake does not support a standard "uninstall" target, but the
community recommends implementing an "uninstal" target that remove files named in the
`install_manifest.txt`:
https://gitlab.kitware.com/cmake/community/-/wikis/FAQ#can-i-do-make-uninstall-with-cmake

However, our existing process of installing the symlink to the "bare"
library, i.e. the symlink from libImath-3_2.so to libImath.so, fails
to add the symlink to the manifest, so "make uninstall" misses the
symlink. The existing mechanism use "install(CODE execute_process(cmake -E create_symlink))".

This changes that to use a simpler "file(CREATE_LINK)" and
"install(FILES)" to accomplish the same thing while also registering
the symlink the the manifest.

Also, this fixes an issue where `OpenEXRConfig.h` was passed to
`install()` twice, producing two entries in `install_manifest.txt`.

Signed-off-by: Cary Phillips <cary@ilm.com>

* mention uninstall in install instructions

Signed-off-by: Cary Phillips <cary@ilm.com>

* poke

Signed-off-by: Cary Phillips <cary@ilm.com>

* COPY_ON_ERROR

Signed-off-by: Cary Phillips <cary@ilm.com>

* clarify the uninstall instructions

Signed-off-by: Cary Phillips <cary@ilm.com>

---------

Signed-off-by: Cary Phillips <cary@ilm.com>
2024-02-15 17:19:01 -08:00
Cary PhillipsandNick Porcino b765658cfb Document security expectations (#1623)
* Document security expectations

Signed-off-by: Cary Phillips <cary@ilm.com>

* Menion Imath as a dependency

Signed-off-by: Cary Phillips <cary@ilm.com>

* Update SECURITY.md

Co-authored-by: Nick Porcino <meshula@hotmail.com>
Signed-off-by: Cary Phillips <cary@ilm.com>

* change 'Threat Model' to 'Potential Vulnerabilties'

Signed-off-by: Cary Phillips <cary@ilm.com>

* Mention GitHub issue as fallback security contact

Signed-off-by: Cary Phillips <cary@ilm.com>

* github security advisory

Signed-off-by: Cary Phillips <cary@ilm.com>

* mention exrcheck

Signed-off-by: Cary Phillips <cary@ilm.com>

---------

Signed-off-by: Cary Phillips <cary@ilm.com>
Co-authored-by: Nick Porcino <meshula@hotmail.com>
2024-02-15 17:19:01 -08:00
Vertexwahn 74f3a2d191 Bazel support: Bump Imath to 3.1.10 (#1626)
Signed-off-by: Vertexwahn <julian.amann@tum.de>
2024-02-15 17:19:01 -08:00
Cary Phillips c825a08bec Make 'Hello, World' example reader/writer downloadable (#1620)
Signed-off-by: Cary Phillips <cary@ilm.com>
2024-02-15 17:19:01 -08:00
Cary Phillips 4198d004ed Add a reference to building tools from source to the tools webpage. (#1622)
Signed-off-by: Cary Phillips <cary@ilm.com>
2024-02-15 17:19:01 -08:00
Cary PhillipsandNick Porcino e29a6dafe1 Add explanation of distinction between OpenEXR/OpenEXRCore to API section (#1621)
* Add explanation of the distinction between OpenEXR/OpenEXRCore to API section

This was described in the release notes but was never addressed in the
actual online documentation, where it was not, in fact, obvious.

Signed-off-by: Cary Phillips <cary@ilm.com>

* Update website/API.rst

Co-authored-by: Nick Porcino <meshula@hotmail.com>
Signed-off-by: Cary Phillips <cary@ilm.com>

---------

Signed-off-by: Cary Phillips <cary@ilm.com>
Co-authored-by: Nick Porcino <meshula@hotmail.com>
2024-02-15 17:19:01 -08:00
Brecht Van Lommel 2641f8b341 Support cmake config for libdeflate (#1613)
* Support cmake config and shared library for libdeflate

When not using the internal deflate, the only option was pkgconfig which
is not well supported on Windows. This adds support for using the
libdeflate cmake config files when available.

This change also affects the generated OpenEXR cmake config files. For
a static OpenEXR library, INTERFACE_LINK_LIBRARIES now appropriately
includes libdeflate along with the existing dependencies like Imath.

It also turns the internal libdeflate into a target for consistency.

Resolves #1588

Signed-off-by: Brecht Van Lommel <brecht@blender.org>

* Back out of adding a target for internal libdeflate

Signed-off-by: Brecht Van Lommel <brecht@blender.org>

* Use Requires.private for generated pkgconfig file

Signed-off-by: Brecht Van Lommel <brecht@blender.org>

---------

Signed-off-by: Brecht Van Lommel <brecht@blender.org>
2024-02-15 17:19:01 -08:00
Cary Phillips b6caee5b8b Update MacPorts install instructions (#1611)
* Update MacPorts install instructions

Signed-off-by: Cary Phillips <cary@ilm.com>

* remove sudo

Signed-off-by: Cary Phillips <cary@ilm.com>

* mention vcpkg for Windows

Signed-off-by: Cary Phillips <cary@ilm.com>

---------

Signed-off-by: Cary Phillips <cary@ilm.com>
2024-02-15 17:19:01 -08:00
Cary Phillips 6925579b36 Fix bswap on NetBSD (#1612)
The #includes were incorrect.

Signed-off-by: Cary Phillips <cary@ilm.com>
2024-02-15 17:19:01 -08:00
Vertexwahn d6c7eed7e7 Fix spelling of GitHub (#1615)
Signed-off-by: Vertexwahn <julian.amann@tum.de>
2024-02-15 17:19:01 -08:00
David Deal f37f7d1c43 CI/CD - Added Snyk C/C++ Scanning Job (#1608)
- added example C/C++ Code scanner using the Snyk GitHub Action.
  The `--unmanaged` flag indicates this is for a C/C++ codebase. In this
  example, it currently scans on a new pull request to the 'main'
  branch. The repository administrator should set both the SNYK_ORG and
  SNYK_TOKEN environment variables before merging this PR. The
  environment variables can be obtained from the LFX Security team.
- added *.h, *.c, *.cpp filter to only run the scan when source files
  are changed

Signed-off-by: David Deal <ddeal@linuxfoundation.org>
2024-02-15 17:19:01 -08:00
Vertexwahn e7a34c5e04 Bump skylib in workspace approach (#1605)
Default behavior enable_bzlmod was switch (https://github.com/bazelbuild/bazel/issues/18958)

Bump Bazel version to 7.0.0

Update Bazel modules

Apply buildifier on MODULE.bazel

Signed-off-by: Vertexwahn <julian.amann@tum.de>
2024-02-15 17:19:01 -08:00
Cary Phillips 93336dae74 Release notes and news for v2.5.10 (#1600)
* Release notes for v2.5.10

Signed-off-by: Cary Phillips <cary@ilm.com>

* News for v2.5.10

Signed-off-by: Cary Phillips <cary@ilm.com>

---------

Signed-off-by: Cary Phillips <cary@ilm.com>
2024-02-15 17:19:01 -08:00
Cary Phillips 162abaa322 Account for duplicate emails with .mailmap (#1597)
The .mailmap file maps author/committer names and emails to canonical
real names and email addresses.

This also updates the CONTRIBUTORS.md file with missing names from the
git logs.

This information is to the best of our knowledge, but please submit
any corrections if you prefer.

Signed-off-by: Cary Phillips <cary@ilm.com>
2024-02-15 17:19:01 -08:00
Megha S 5261e5ef19 Converting code-blocks to literalincludes in ReadingAndWritingImageFiles.rst (#1583)
* Converting code-blocks to literalincludes in ReadingAndWritingImageFiles.rst

Signed-off-by: Megha S <megha199501@gmail.com>

* Addressed review comments

Signed-off-by: Megha S <megha199501@gmail.com>

* Changing from std::clamp to Imath::clamp

Signed-off-by: Megha S <megha199501@gmail.com>

---------

Signed-off-by: Megha S <megha199501@gmail.com>
2024-02-15 17:19:01 -08:00
annguyen-ilm 467e7e58ff #1545 Fix formatting of sample exr file in OpenEXRFileLayout.rst - 3rd attempt (#1587)
* Replaced the sample exr file code blocks with a grid table formatted with bytes of the sample file in one column. The value in the second column and the third column being the description of the bytes and value.

Signed-off-by: An Nguyen <annguyen@ilm.com>

* * Updated so the ascii values for the bytes are aligned to each byte.
* Gave the table a title and reduce the width to 50%

Signed-off-by: An Nguyen <annguyen@ilm.com>

* Added missing 4 bytes missing from the dataWindow value and displayWindow value

Signed-off-by: An Nguyen <annguyen@ilm.com>

* Added the sample.exr file

Signed-off-by: An Nguyen <annguyen@ilm.com>

* Annotated the attribute values more for the header. Should be a bit clearer.

Signed-off-by: An Nguyen <annguyen@ilm.com>

* Adding the sample.exr file was documented.

Signed-off-by: An Nguyen <annguyen@ilm.com>

* Added download link to the sample.exr file.

Signed-off-by: An Nguyen <annguyen@ilm.com>

* Move the download link above the table. Updated the paragraph before the table to describe the table

Signed-off-by: An Nguyen <annguyen@ilm.com>

* Move the sample.exr image into the downloads directory to match the target destination naming scheme to avoid confusion where the files go when built

Signed-off-by: An Nguyen <annguyen@ilm.com>

* Updated the table to annotate the type and literal as type.

Signed-off-by: An Nguyen <annguyen@ilm.com>

---------

Signed-off-by: An Nguyen <annguyen@ilm.com>
2024-02-15 17:19:01 -08:00
peterhillman 55b199c13e add deep id/manifest tools and doc (#1595)
* add deep id/manifest tools and docs

Signed-off-by: Peter Hillman <peterh@wetafx.co.nz>

* fix const-ness in deepidexample

Signed-off-by: Peter Hillman <peterh@wetafx.co.nz>

* fix link on doc, better variable name

Signed-off-by: Peter Hillman <peterh@wetafx.co.nz>

* add discussion of cryptomatte and deep compositing

Signed-off-by: Peter Hillman <peterh@wetafx.co.nz>

* fix names of cryptomatte ID channels

Signed-off-by: Peter Hillman <peterh@wetafx.co.nz>

---------

Signed-off-by: Peter Hillman <peterh@wetafx.co.nz>
2024-02-15 17:19:01 -08:00
Dave Sawyer 8061f57626 Remove some dead code when writing (#1592)
The file version is set to 2 above for writing files (it is impossible write version 1 data). Please refer to the history of history of ImfDwaCompressor.cpp or look at the reading code for details on the version 1 format.

Signed-off-by: Dave Sawyer <kingsawyer@gmail.com>
2024-02-15 17:19:01 -08:00
Anton Dukhovnikov 88cd3c713b Fix issue with decompressing fp32 dwa files (#1591)
There was an issue with the data type not passed to the DCT decompressor in the DWA decoder. That caused 32 bit DWA images to look squashed, as the decompressed 16 bit values were not getting expanded back to 32 bit.

Signed-off-by: Anton Dukhovnikov <antond@wetafx.co.nz>
2024-02-15 17:19:01 -08:00
Ben Grimes 0414107b3f Add tests for the Header class. (#1577)
These are intended to exercise methods that are currently not covered by
other tests according to the coverage report.

Signed-off-by: Ben Grimes <bgrimes@ilm.com>
2024-02-15 17:19:01 -08:00
cdleu430 38c4b59e08 adding better error reporting for bin tests (#1578)
forgot a few things

Signed-off-by: cdleu430 <cleu@ilm.com>
2024-02-15 17:19:01 -08:00
Barnaby Robson 74776cbaf8 python-wheels.yml - add arm64 builds for macOS (#1579)
The CI platform that builds the wheels is Intel based and
by default only builds Intel wheels.

Modern Macs are all based on Apple Silicon so to support them
we need to also build arm64. This is done via cross compiling.

It is not possible to test when cross compiling according to:

https://cibuildwheel.readthedocs.io/en/stable/faq/#how-to-cross-compile

Signed-off-by: Barnaby Robson <brobson@ilm.com>
2024-02-15 17:19:01 -08:00
Barnaby Robson 00ee0fb31b python-wheels.yml - bumps cibuildwheel version (#1576) 2024-02-15 17:19:01 -08:00
Barnaby Robson d9360a608b fix typo in README.md (#1575)
* fix typo in README.md

change make -> cmake

Signed-off-by: Barnaby Robson <brobson@ilm.com>

* removes mkdir.

Suggested by Milos in review. I tested it and it works.

Signed-off-by: Barnaby Robson <brobson@ilm.com>

---------

Signed-off-by: Barnaby Robson <brobson@ilm.com>
2024-02-15 17:19:01 -08:00
ianianda 9a999c95b6 install.rst - update $ to % in the example shell prompts (#1570)
Signed-off-by: ianianda <wyking1030@gmail.com>
2024-02-15 17:19:01 -08:00
Kimball Thurston 0b3f83fde6 check and control reduceMemory and reduceTime in stream mode (#1635)
exrcheck by default uses file mode, but the fuzzer and exrcheck -s use
stream mode, need to respect the memory and time flags consistently on
that path as well.

Will address OSS-Fuzz 66612, although real fix underlying is in #1634

Signed-off-by: Kimball Thurston <kdt3rd@gmail.com>
2024-02-15 17:19:01 -08:00
Cary Phillips 7adf2c8f6f Remove duplicate line in release-notice.yml
Signed-off-by: Cary Phillips <cary@ilm.com>
2024-02-11 20:14:08 -08:00
Cary Phillips c0208feee9 Add PR 1630 and 1632 to release notes
Signed-off-by: Cary Phillips <cary@ilm.com>
2024-02-10 11:17:34 -08:00
Kimball Thurston d5cc7b7ebe adjust checks for core to better match c++ checks (#1632)
The core checks were not setting the same image / tile size limits and
not disabling reads at quite the same level.

Note: the core check does not read the entire image into a contiguous
slice, so does not replicate the maximum deep sample checks in the same
way, this is a source of potential false-negative failures

This should address OSS-Fuzz 66491 and 66489 (different forms of the
same failure where a large sample size allocation was happening), and
are only constrained memory (2.5Gb) issues.

Signed-off-by: Kimball Thurston <kdt3rd@gmail.com>
2024-02-10 11:11:59 -08:00
Kimball Thurston d8cb68dfd6 fix issue with unpacking sample counts (#1630)
When unpacking sample counts as "individual" counts (no longer
monotonic), it writes the total sample count to a value 1 past the
individual sample counts, but this is not in the packed data, so do not
expect to unpack that many values. The buffer just needs to be allocated
one value larger to avoid write past end of buffer which is taken care
of in the update_pack_unpack_ptrs function

Signed-off-by: Kimball Thurston <kdt3rd@gmail.com>
2024-02-10 11:11:33 -08:00
Cary Phillips 280606593f Add CVE-2023-5841
Signed-off-by: Cary Phillips <cary@ilm.com>
2024-02-04 12:34:04 -08:00
Cary Phillips 561969efe2 Bump version for v3.2.2
Signed-off-by: Cary Phillips <cary@ilm.com>
2024-02-04 12:27:07 -08:00
Cary Phillips e0cb398300 Release notes for v3.2.2
Signed-off-by: Cary Phillips <cary@ilm.com>
2024-02-04 12:26:24 -08:00
Cary Phillips 5c0381835d Add slack release notifier workflow
Signed-off-by: Cary Phillips <cary@ilm.com>
2024-02-04 12:23:16 -08:00
Cary Phillips 8902ae6e71 Require sphinx 5.0 (#1617)
Apparently now required by other dependencies

Signed-off-by: Cary Phillips <cary@ilm.com>
2024-02-04 12:23:04 -08:00
Cary Phillips f996a5f076 Release notes for v3.1.12
Signed-off-by: Cary Phillips <cary@ilm.com>
2024-02-04 12:22:27 -08:00
Kimball Thurston 8bedf106ef Fix CVE 2023 5841 (#1627)
* enable deep file checks for core

Signed-off-by: Kimball Thurston <kdt3rd@gmail.com>

* fix possible int overflow

Signed-off-by: Kimball Thurston <kdt3rd@gmail.com>

* fix validation of deep sample counts

Addresses CVE-2023-5841, fixing sample count check to not only check
against 0 but previous sample as well.

Signed-off-by: Kimball Thurston <kdt3rd@gmail.com>

* add clarifying comment

Signed-off-by: Kimball Thurston <kdt3rd@gmail.com>

---------

Signed-off-by: Kimball Thurston <kdt3rd@gmail.com>
2024-02-04 12:18:24 -08:00
737b2707a0 v3.2.1 commits and release notes (#1569)
* Propagate OPENEXR_INSTALL_PKG_CONFIG to internal Imath (#1531)

* Propagate OPENEXR_INSTALL_PKG_CONFIG to internal Imath

If OpenEXR is installing a pkg-config file, then the internal Imath
build (if there is one) should install it, too.

Also, add an explicit release version variable to the .pc file.

This is in preparation for the python wheel build to pick up the
version settings from the pkg-config files.

Signed-off-by: Cary Phillips <cary@ilm.com>

* remove version= from OpenEXR.pc

Signed-off-by: Cary Phillips <cary@ilm.com>

---------

Signed-off-by: Cary Phillips <cary@ilm.com>

* Remove check for _MSC_VER in internal_cpuid.h (#1528)

As noted in #1445, _MSC_VER should only be used to detect msvc, not
for Windows. The _WIN32 check should be sufficient. The extra check
for _MSC_VER fails when cross-compiling from Linux to Windows.

Signed-off-by: Cary Phillips <cary@ilm.com>

* Fix warnings in multipartExamples.cpp (#1533)

* Fix warnings in multipartExamples.cpp

Signed-off-by: Cary Phillips <cary@ilm.com>

* fix indentation

Signed-off-by: Cary Phillips <cary@ilm.com>

---------

Signed-off-by: Cary Phillips <cary@ilm.com>

* Fix warnings from cross-compiling with x86_64-w64-mingw32-gcc-posix (#1534)

* Fix compiler warnings from x86_64-w64-mingw32-g++-posix

* MSVCRT doesn't recognize printf("%l") so PRIu64 doesn't work, it
  expects "%I64u".

* Add to the cases that disable a strncpy warning

Signed-off-by: Cary Phillips <cary@ilm.com>

* %zu for size_t

Signed-off-by: Cary Phillips <cary@ilm.com>

* revert attempt to fix PRIu64 warnings

Signed-off-by: Cary Phillips <cary@ilm.com>

* revert blank line

Signed-off-by: Cary Phillips <cary@ilm.com>

---------

Signed-off-by: Cary Phillips <cary@ilm.com>

* Fix OPENEXR_VERSION_HEX (#1539)

* Fix OPENEXR_VERSION_HEX

* fix syntax error
* remove unnecessary uint32_t cast
* add validation test

Signed-off-by: Cary Phillips <cary@ilm.com>

* Add #if statement with OPENEXR_VERSION_HEX

Signed-off-by: Cary Phillips <cary@ilm.com>

---------

Signed-off-by: Cary Phillips <cary@ilm.com>

* OPENEXR_INSTALL_PKG_CONFIG is on by default, even on Windows (#1541)

There seems to be no downside to generating the OpenEXR.pc file even
on Windows, so for consistency, apply the setting to all platforms.

Signed-off-by: Cary Phillips <cary@ilm.com>

* Remove SPDX license identifier in LICENSE.md

Signed-off-by: Cary Phillips <cary@ilm.com>

* Python wheel setup gets version from OpenEXR.pc/Imath.pc (#1536)

* Python wheel setup gets version from OpenEXR.pc/Imath.pc

OpenEXR and Imath have indepdenent versions that may not match. The
.pc files appear to be the simplest way to extract the versions and
library suffixes.

Running pkg-config itself doesn't work on Windows, hence the manual
file parsing.

Signed-off-by: Cary Phillips <cary@ilm.com>

* =

Signed-off-by: Cary Phillips <cary@ilm.com>

* Remove -DOPENEXR_INSTALL_PKG_CONFIG=ON since it's now on by default

Signed-off-by: Cary Phillips <cary@ilm.com>

---------

Signed-off-by: Cary Phillips <cary@ilm.com>

* Default value for chromaticities attribute constructor in exrstdattr (#1540)

Signed-off-by: Cary Phillips <cary@ilm.com>

* Update openexr_deps.bzl (#1565)

Signed-off-by: Vertexwahn <julian.amann@tum.de>

* Bazel: Improve module (#1562)

Signed-off-by: Vertexwahn <julian.amann@tum.de>

* Set build-shared:OFF for Static build (#1557)

Looks like this somehow we've been building shared when we meant to be
building static.

Signed-off-by: Cary Phillips <cary@ilm.com>

* Omit OPENEXR_IMAGES_TAG from test image url if empty (#1560)

This allows the url for the test images to reference a local file:

  cmake -DOPENEXR_IMAGES_REPO=file:///my/clone/of/openexr-images -DOPENEXR_IMAGES_TAG=""

which avoids the remote downloading of the test images at cmake time.

Also, mention the test images in the install docs.

Signed-off-by: Cary Phillips <cary@ilm.com>

* Clean up handling of libdeflate when linking static  (#1561)

* Set EXR_DEFLATE_LIB properly for static linking

This leads OpenEXRCore/CMakeLists.txt to do:

    target_link_libraries(OpenEXRCore PUBLIC libdeflate)

instead of:

    target_link_libraries(OpenEXRCore PUBLIC PkgConfig::deflate)

which is not recognized. With a target link library of
`PkgConfig::deflate`, the static build of the OpenEXR libraries builds
successfully, but configuring an application against those static
libraries fails, saying it can't find `PkgConfig::deflate`.

Signed-off-by: Cary Phillips <cary@ilm.com>

* Add -ldeflate to pkgconfig for static builds

When static linking and referencing an external libdeflate (i.e. not
fetching and building internally), OpenEXR.pc needs -ldeflate.

This also adds a test for this condition to the CI's validation script.

Signed-off-by: Cary Phillips <cary@ilm.com>

---------

Signed-off-by: Cary Phillips <cary@ilm.com>

* Don't trigger ci/bazel/ossfuzz builds on pushes/PRs to src/wrappers (#1532)

No need to rebuild/test OpenEXR on changes to the python wheels.

Signed-off-by: Cary Phillips <cary@ilm.com>

* Set minimal permissions for workflow python-wheels.yml (#1530)

The exact same changes done on other workflows through the PR #1417

Signed-off-by: Diogo Teles Sant'Anna <diogoteles@google.com>

* Release notes for 3.2.1

Signed-off-by: Cary Phillips <cary@ilm.com>

* Fix Imf/Iex/IlmThread namespaces in python bindings and website code (#1568)

* Fix Imf/Iex/IlmThread namespaces in python bindings and website code

Use OPENEXR_NAMESPACE, IEX_NAMESPACE, ILMTHREAD_NAMESPACE instead of
Imf, Iex, IlmThread, to support custom settings.

Signed-off-by: Cary Phillips <cary@ilm.com>

* Use Iex:: namespace in example code instead of IEX_NAMESPACE

Requires this in all.cpp:

  namespace Iex = IEX_NAMESPACE;

Signed-off-by: Cary Phillips <cary@ilm.com>

* use #define Iex

Signed-off-by: Cary Phillips <cary@ilm.com>

* remove explicit Iex namespace

Signed-off-by: Cary Phillips <cary@ilm.com>

---------

Signed-off-by: Cary Phillips <cary@ilm.com>

* Bump version to 3.2.1

Signed-off-by: Cary Phillips <cary@ilm.com>

---------

Signed-off-by: Cary Phillips <cary@ilm.com>
Signed-off-by: Vertexwahn <julian.amann@tum.de>
Signed-off-by: Diogo Teles Sant'Anna <diogoteles@google.com>
Co-authored-by: Vertexwahn <julian.amann@tum.de>
Co-authored-by: Diogo Teles Sant'Anna <diogoteles@google.com>
2023-09-24 22:46:06 -04:00
121 changed files with 6499 additions and 1810 deletions
+1 -1
View File
@@ -1 +1 @@
6.3.2
7.0.2
+1 -1
View File
@@ -86,7 +86,7 @@ jobs:
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
run: sonar-scanner -X -Dsonar.login=$SONAR_TOKEN
run: sonar-scanner -X
# ------------------------------------------------------------------------------
# Valgrind memcheck test
+38 -15
View File
@@ -22,6 +22,7 @@ on:
- '!**.md'
- '!website/**'
- 'website/src/**'
- '!src/wrappers/**'
pull_request:
branches-ignore:
- RB-2.*
@@ -33,6 +34,7 @@ on:
- '!**.md'
- '!website/**'
- 'website/src/**'
- '!src/wrappers/**'
permissions:
contents: read
@@ -43,10 +45,10 @@ jobs:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4.1.1
- name: Mount Bazel cache
uses: actions/cache@v3
uses: actions/cache@v4.0.0
with:
path: "/home/runner/.cache/bazel"
key: bazel-ubuntu-22
@@ -55,19 +57,19 @@ jobs:
run: |
bazelisk build //...
bazelisk test //...
# Test bzlmod
bazelisk build --enable_bzlmod -- //...
bazelisk test --enable_bzlmod -- //...
# Test without bzlmod
bazelisk build --noenable_bzlmod -- //...
bazelisk test --noenable_bzlmod -- //...
build_and_test_windows:
name: Windows Server 2022 build <Visual Studio 2022>
runs-on: windows-2022
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4.1.1
- name: Mount Bazel cache
uses: actions/cache@v3
uses: actions/cache@v4.0.0
with:
path: "/home/runner/.cache/bazel"
key: bazel-windows-2022
@@ -76,19 +78,19 @@ jobs:
run: |
bazelisk build //...
bazelisk test //...
# Test bzlmod
bazelisk build --enable_bzlmod -- //...
bazelisk test --enable_bzlmod -- //...
# Test without bzlmod
bazelisk build --noenable_bzlmod -- //...
bazelisk test --noenable_bzlmod -- //...
build_and_test_macos:
name: macOS 13 Bazel build <Apple Clang14>
runs-on: macos-13
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4.1.1
- name: Mount Bazel cache
uses: actions/cache@v3
uses: actions/cache@v4.0.0
with:
path: "/home/runner/.cache/bazel"
key: bazel-macos-13
@@ -97,6 +99,27 @@ jobs:
run: |
bazelisk build //...
bazelisk test //...
# Test bzlmod
bazelisk build --enable_bzlmod -- //...
bazelisk test --enable_bzlmod -- //...
# Test without bzlmod
bazelisk build --noenable_bzlmod -- //...
bazelisk test --noenable_bzlmod -- //...
build_and_test_macos_M1:
name: macOS 14 Bazel build <Apple Clang14>
runs-on: macos-14
steps:
- uses: actions/checkout@v4.1.1
- name: Mount Bazel cache
uses: actions/cache@v4.0.0
with:
path: "/home/runner/.cache/bazel"
key: bazel-macos-14
- name: Build
run: |
bazelisk build //...
bazelisk test //...
# Test without bzlmod
bazelisk build --noenable_bzlmod -- //...
bazelisk test --noenable_bzlmod -- //...
+17 -20
View File
@@ -6,36 +6,35 @@
name: CI
# Skip the run on *.md changes and on changes to the website, *except*
# for "website/src", since that code needs validation. There's a
# separate workflow for with website. Also, no need to run on changes
# to the bazel setup.
# Run on all changes except:
# - doc file changes
# - changes to the website, *except* for "website/src", since that
# code needs validation. The website has a separate workflow
# - changes to the bazel config, since it has its own workflow
# - changes to the python bindings
# - changes to workflows other than this one
on:
push:
branches-ignore:
- RB-2.*
tags-ignore:
- v1.*
- v2.*
paths:
- '**'
- '!**.md'
- '!website/**'
- 'website/src/**'
- '!bazel/**'
- '!src/wrappers/**'
- '!.github/workflows/**'
- '.github/workflows/ci_workflow.yml'
pull_request:
branches-ignore:
- RB-2.*
tags-ignore:
- v1.*
- v2.*
paths:
- '**'
- '!**.md'
- '!website/**'
- 'website/src/**'
- '!bazel/**'
- '!src/wrappers/**'
- '!.github/workflows/**'
- '.github/workflows/ci_workflow.yml'
permissions:
contents: read
@@ -118,7 +117,7 @@ jobs:
# Static, Release
- build: 4
build-type: Release
build-shared: 'ON'
build-shared: 'OFF'
cxx-standard: 17
cxx-compiler: g++
cc-compiler: gcc
@@ -236,8 +235,8 @@ jobs:
run: yum install -y help2man
- name: Configure
run: |
cmake .. \
-DCMAKE_INSTALL_PREFIX=../_install \
cmake -B _build -S . \
-DCMAKE_INSTALL_PREFIX=_install \
-DCMAKE_BUILD_TYPE=${{ matrix.build-type }} \
-DCMAKE_CXX_STANDARD=${{ matrix.cxx-standard }} \
-DCMAKE_CXX_FLAGS=${{ matrix.cxx-flags }} \
@@ -247,13 +246,11 @@ jobs:
-DOPENEXR_INSTALL_DOCS='ON' \
-DOPENEXR_RUN_FUZZ_TESTS='OFF' \
-DOPENEXR_ENABLE_THREADING=${{ matrix.threads-enabled }}
working-directory: _build
- name: Build
run: |
cmake --build . \
cmake --build _build \
--target install \
--config ${{ matrix.build-type }}
working-directory: _build
- name: Validate
run: |
share/ci/scripts/linux/validate_openexr_libs.sh _install
+2
View File
@@ -18,6 +18,7 @@ on:
- '!**.md'
- '!website/**'
- '!bazel/**'
- '!src/wrappers/**'
pull_request:
branches-ignore:
- RB-2.*
@@ -29,6 +30,7 @@ on:
- '!**.md'
- '!website/**'
- '!bazel/**'
- '!src/wrappers/**'
permissions:
contents: read
@@ -0,0 +1,104 @@
# SPDX-License-Identifier: BSD-3-Clause
# Copyright (c) Contributors to the OpenEXR Project.
name: Publish python distribution 📦 to TestPyPI
on:
# Publish python wheels to test.pypi when a release candidate is tagged,
# e.g. v3.4.5-rc, v3.4.5-rc6, etc.
push:
tags:
- v3.[0-9]+.[0-9]+-rc*
workflow_dispatch:
permissions:
contents: read
jobs:
build:
name: Python Wheels - ${{ matrix.os }}
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
environment:
name: testpypi
url: https://test.pypi.org/p/openexr
permissions:
id-token: write
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install Python
uses: actions/setup-python@v5
with:
python-version: '3.x'
- name: Create sdist
# Only create it once.
if: ${{ matrix.os == 'ubuntu-latest' }}
env:
OPENEXR_RELEASE_CANDIDATE_TAG: ${{ github.ref_name }}
run: pipx run build --sdist . --outdir wheelhouse
- name: Build wheel
uses: pypa/cibuildwheel@v2.16
with:
output-dir: wheelhouse
env:
CIBW_ARCHS_LINUX: x86_64
CIBW_ARCHS_MACOS: x86_64 arm64 universal2
# Skip python 3.6 since scikit-build-core requires 3.7+
# Skip 32-bit wheels builds on Windows
# Also skip the PyPy builds, since they fail the unit tests
CIBW_SKIP: cp36-* *-win32 *_i686 pp*
CIBW_TEST_SKIP: "*-macosx_universal2:arm64"
CIBW_ENVIRONMENT: OPENEXR_RELEASE_CANDIDATE_TAG="${{ github.ref_name }}"
- name: Upload artifact
uses: actions/upload-artifact@v4.0.0
with:
name: wheels-${{ matrix.os }}
path: |
./wheelhouse/*.whl
./wheelhouse/*.tar.gz
publish-to-testpypi:
name: Publish Python 🐍 distribution 📦 to TestPyPI
needs:
- build
runs-on: ubuntu-latest
environment:
name: testpypi
url: https://test.pypi.org/p/openexr
permissions:
id-token: write
steps:
- name: Download Linux artifacts
uses: actions/download-artifact@v4.0.0
with:
name: wheels-ubuntu-latest
path: dist
- name: Download macOS artifacts
uses: actions/download-artifact@v4.0.0
with:
name: wheels-macos-latest
path: dist
- name: Download Windows artifacts
uses: actions/download-artifact@v4.0.0
with:
name: wheels-windows-latest
path: dist
- name: Publish distribution 📦 to TestPyPI
uses: pypa/gh-action-pypi-publish@release/v1
with:
repository-url: https://test.pypi.org/legacy/
@@ -0,0 +1,95 @@
# SPDX-License-Identifier: BSD-3-Clause
# Copyright (c) Contributors to the OpenEXR Project.
name: Publish python distribution 📦 to PyPI
on:
# Publish wheels to pypi on release
release:
types: [published]
workflow_dispatch:
permissions:
contents: read
jobs:
build:
name: Python Wheels - ${{ matrix.os }}
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
environment:
name: pypi
url: https://pypi.org/p/openexr
permissions:
id-token: write
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install Python
uses: actions/setup-python@v5
with:
python-version: '3.x'
- name: Create sdist
# Only create it once.
if: ${{ matrix.os == 'ubuntu-latest' }}
run: pipx run build --sdist . --outdir wheelhouse
- name: Build wheel
uses: pypa/cibuildwheel@v2.16
with:
output-dir: wheelhouse
env:
CIBW_ARCHS_LINUX: x86_64
CIBW_ARCHS_MACOS: x86_64 arm64 universal2
# Skip python 3.6 since scikit-build-core requires 3.7+
# Skip 32-bit wheels builds on Windows
# Also skip the PyPy builds, since they fail the unit tests
CIBW_SKIP: cp36-* *-win32 *_i686 pp*
CIBW_TEST_SKIP: "*arm64"
- name: Upload artifact
uses: actions/upload-artifact@v4.0.0
with:
name: wheels-${{ matrix.os }}
path: |
./wheelhouse/*.whl
./wheelhouse/*.tar.gz
publish-to-pypi:
name: Publish Python 🐍 distribution 📦 to PyPI
needs:
- build
runs-on: ubuntu-latest
environment:
name: pypi
url: https://pypi.org/p/openexr
permissions:
id-token: write
steps:
- name: Download Linux artifacts
uses: actions/download-artifact@v4.0.0
with:
name: wheels-ubuntu-latest
path: dist
- name: Download macOS artifacts
uses: actions/download-artifact@v4.0.0
with:
name: wheels-macos-latest
path: dist
- name: Download Windows artifacts
uses: actions/download-artifact@v4.0.0
with:
name: wheels-windows-latest
path: dist
- name: Publish distribution 📦 to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
+49 -57
View File
@@ -1,82 +1,74 @@
# SPDX-License-Identifier: BSD-3-Clause
# Copyright (c) Contributors to the OpenEXR Project.
#
name: Python wheels
name: Python Wheels
on:
# Run on all changes (PR and push) to the python binding
# source/configuration files, except on the release branches, which
# have their own workflow, which also publish to pypi/test.pypi.
# Note that changes to the core libraries will *not*
# trigger building the wheels. However, the main ci workflow does
# build and test the bindings (for a single python version on a
# single arch)
push:
branches-ignore:
- RB-2.*
tags-ignore:
- v1.*
- v2.*
- RB-*
paths:
- '**'
- '!**.md'
- '!website/**'
- 'website/src/**'
- '!bazel/**'
- 'src/wrappers/python/**'
- 'pyproject.toml'
- '.github/workflows/python-wheels.yml'
pull_request:
branches-ignore:
- RB-2.*
tags-ignore:
- v1.*
- v2.*
- RB-*
paths:
- '**'
- '!**.md'
- '!website/**'
- 'website/src/**'
- '!bazel/**'
- 'src/wrappers/python/**'
- 'pyproject.toml'
- '.github/workflows/python-wheels.yml'
permissions:
contents: read
jobs:
build_wheels:
name: Build Python wheels
name: Python Wheels - ${{ matrix.os }}
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-22.04, windows-latest, macOS-latest]
env:
# Skip 32-bit wheels builds.
# Also skip the PyPy builds, since they fail the unittests
CIBW_SKIP: "*-win32 *_i686 pp*"
CIBW_BEFORE_BUILD: >
echo "Installing OpenEXR..." &&
cd openexr.build &&
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=../openexr.install -DCMAKE_VERBOSE_MAKEFILE:BOOL=ON -DCMAKE_PREFIX_PATH=../openexr.install -DCMAKE_INSTALL_LIBDIR=lib -DBUILD_TESTING=OFF -DOPENEXR_INSTALL_EXAMPLES=OFF -DOPENEXR_BUILD_TOOLS=OFF -DBUILD_SHARED_LIBS=OFF -DOPENEXR_FORCE_INTERNAL_DEFLATE=ON -DOPENEXR_FORCE_INTERNAL_IMATH=ON -DCMAKE_POSITION_INDEPENDENT_CODE=ON ../ &&
cmake --build ./ --config Release --clean-first &&
cmake --install ./ --config Release &&
cd ..
CIBW_TEST_REQUIRES: pytest
CIBW_TEST_COMMAND: pytest {project}/src/wrappers/python/tests/
os: [ubuntu-latest, macos-latest, windows-latest]
steps:
- uses: actions/checkout@v3
# Used to host cibuildwheel
- uses: actions/setup-python@v4
- name: Checkout
uses: actions/checkout@v4
- name: Install Python
uses: actions/setup-python@v5
with:
python-version: '3.x'
python-version: '3.x'
- name: Install cibuildwheel
run: python -m pip install cibuildwheel==2.8.1
- name: Create sdist
# Only create it once.
if: ${{ matrix.os == 'ubuntu-latest' }}
run: pipx run build --sdist . --outdir wheelhouse
- name: Create setup.py
run: |
mv ${{github.workspace}}/src/wrappers/python/setup.py ${{github.workspace}}/setup.py
mv ${{github.workspace}}/src/wrappers/python/Imath.py ${{github.workspace}}/Imath.py
mv ${{github.workspace}}/src/wrappers/python/OpenEXR.cpp ${{github.workspace}}/OpenEXR.cpp
- name: Build wheel
uses: pypa/cibuildwheel@v2.16
env:
CIBW_ARCHS_MACOS: x86_64 arm64 universal2
# Skip python 3.6 since scikit-build-core requires 3.7+
# Skip 32-bit wheels builds on Windows
# Also skip the PyPy builds, since they fail the unit tests
CIBW_SKIP: cp36-* *-win32 *_i686 pp*
CIBW_TEST_SKIP: "*-macosx*arm64"
- name: Create folders
run: |
mkdir -p ${{github.workspace}}/openexr.build
mkdir -p ${{github.workspace}}/openexr.install
- name: Build wheels
run: python -m cibuildwheel --output-dir wheelhouse
- uses: actions/upload-artifact@v3
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: "Python wheels"
path: ./wheelhouse/*.whl
name: wheels-${{ matrix.os }}
path: |
./wheelhouse/*.whl
./wheelhouse/*.tar.gz
+35
View File
@@ -0,0 +1,35 @@
# SPDX-License-Identifier: BSD-3-Clause
# Copyright (c) Contributors to the OpenEXR Project.
name: Publish Release Notice to ASWF Slack
on:
release:
types:
- published
# published should cover both 'released' and 'prereleased'
jobs:
publish:
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- name: 'Notify Slack #release-announcements'
id: slack1
with:
project_name: "OpenEXR"
slack_bot_token: ${{ secrets.SLACK_BOT_TOKEN }}
slack_channel: "#release-announcements"
project_logo: "https://artwork.aswf.io/projects/openexr/icon/color/openexr-icon-color.png"
uses: jmertic/slack-release-notifier@main
- name: 'Notify Slack #openexr'
id: slack2
with:
project_name: "OpenEXR"
slack_bot_token: ${{ secrets.SLACK_BOT_TOKEN }}
slack_channel: "#openexr"
project_logo: "https://artwork.aswf.io/projects/openexr/icon/color/openexr-icon-color.png"
uses: jmertic/slack-release-notifier@main
+54
View File
@@ -0,0 +1,54 @@
# SPDX-License-Identifier: BSD-3-Clause
# Copyright (c) Contributors to the OpenEXR Project.
#
# Releases are signed via https://github.com/sigstore/sigstore-python.
# See https://docs.sigstore.dev for information about sigstore.
#
# This action creates a .tar.gz of the complete OpenEXR source tree at
# the given release tag, signs it via sigstore, and uploads the
# .tar.gz and the associated .tar.gz.sigstore credential bundle.
#
# To verify a downloaded release at a given tag:
#
# % pip install sigstore
# % sigstore verify github --cert-identity https://github.com/AcademySoftwareFoundation/openexr/.github/workflows/release-sign.yml@refs/tags/<tag> openexr-<tag>.tar.gz
#
name: Sign Release
on:
release:
types: [published]
permissions:
contents: write
id-token: write
repository-projects: write
jobs:
release:
name: Sign & upload release artifacts
runs-on: ubuntu-latest
env:
tarball: openexr-${{ github.ref_name }}.tar.gz
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Create archive
run: git archive --format=tar.gz -o ${{ env.tarball }} ${{ github.ref_name }}
- name: Sign archive with Sigstore
uses: sigstore/gh-action-sigstore-python@v2.1.1
with:
inputs: ${{ env.tarball }}
- name: Upload release archive
env:
GH_TOKEN: ${{ github.token }}
run: gh release upload ${{ github.ref_name }} ${{ env.tarball }} ${{ env.tarball }}.sigstore
+40
View File
@@ -0,0 +1,40 @@
---
# SPDX-License-Identifier: BSD-3-Clause
# Copyright (c) Contributors to the OpenEXR Project.
name: Snyk Scan Code
on:
# https://docs.github.com/en/actions/learn-github-actions/workflow-syntax-for-github-actions
schedule:
- cron: "0 4 * * 0"
jobs:
snyk-scan-pr:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: snyk/actions/setup@master
id: snyk
- name: Snyk version
run: echo "${{ steps.snyk.outputs.version }}"
- name: Snyk Auth
run: snyk auth ${{ secrets.SNYK_TOKEN }}
- name: Snyk Scan Code
# Scan the C/C++ code for vulnerabilities using the Snyk CLI with the unmanaged flag
# https://docs.snyk.io/scan-using-snyk/supported-languages-and-frameworks/c-c++ for options
run: snyk test --unmanaged --print-dep-paths --org=${{ secrets.SNYK_ORG }}
env:
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
continue-on-error: true # optional
- name: Monitor for Vulnerabilities
# To import the test results (issues and dependencies) in the Snyk CLI, run the snyk monitor --unmanaged command:
run: snyk monitor --unmanaged --org=${{ secrets.SNYK_ORG }}
env:
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
continue-on-error: true # optional
+6 -5
View File
@@ -6,23 +6,24 @@
name: Website
# Run only on changes in the "website" directory.
# Run only on changes in the "website" directory or workflow file.
# Skip the release branches, since the website is built from main.
on:
push:
branches:-ignore:
- RB-2.*
- RB-3.*
- RB-*
paths:
- 'website/**'
- '.github/workflows/website_workflow.yml'
pull_request:
branches:-ignore:
- RB-2.*
- RB-3.*
- RB-*
paths:
- 'website/**'
- '.github/workflows/website_workflow.yml'
permissions:
contents: read
+1
View File
@@ -57,3 +57,4 @@ docs/_test_images/
# Ignore Bazel generated files
bazel-*
MODULE.bazel.lock
+163
View File
@@ -0,0 +1,163 @@
# SPDX-License-Identifier: BSD-3-Clause
# Copyright (c) Contributors to the OpenEXR Project.
#
# This file establishes email equivalences so we can resolve what look like
# multiple authors, but actually are the same author who has used multiple
# emails over the course of their involvement with the project.
#
# The format is any of the following:
# <CANONICAL-EMAIL> <alternate-email>
# CANONICAL-NAME <CANONICAL-EMAIL> <alternate-email>
# CANONICAL-NAME <CANONICAL-EMAIL> alternate-name <alternate-email>
#
# You can check for duplicates with this command:
# git shortlog -sne --all
# That command (and others) will use this file to collapse the duplicates.
#
# If you see any duplicates we don't account for here, or if you look at your
# own entry here and want a different name or email to be your canonical one
# (we may not have guessed correctly and matched your preferences), please
# file a PR with the edits to this file.
Aaron Demolder <aaron.demolder@gmail.com>
Abe Fettig <abe@fettig.net>
Aloys Baillet <aloys.baillet+github@gmail.com> aloysb <aloysb@al.com.au>
Andrew Kunz <akunz@ilm.com>
Anton Dukhovnikov <antond@wetafx.co.nz> Anton Dukhovnikov <131838425+antond-weta@users.noreply.github.com>
Antonio Rojas <arojas@archlinux.org>
Aras Pranckevičius <nearaz@gmail.com> Aras Pranckevičius <aras@nesnausk.org>
Aras Pranckevičius <nearaz@gmail.com> Aras Pranckevičius <aras@unity3d.com>
Arkady Shapkin <arkady.shapkin@gmail.com>
Arkell Rasiah <arkellrasiah@gmail.com> Arkell Rasiah <arasiah@ilm.com>
Arkell Rasiah <arkellrasiah@gmail.com> Arkell Rasiah <arasiah@pixsystem.com>
Axel Waggershauser <awagger@gmail.com>
axxel <awagger@gmail.com>
Balázs Oroszi <ignus2@users.noreply.github.com>
Barnaby Robson <brobson@ilm.com> Barnaby Robson <mail@barnaby.org>
Ben Grimes <72311676+MrGlobby@users.noreply.github.com>
Bernd <waebbl-gentoo@posteo.net>
Brendan Bolles <brendan@fnordware.com>
CAHEK7 <ghosts.in.a.box@gmail.com>
Cary Phillips <cary@ilm.com> cary-ilm <cary@ilm.com>
Cary Phillips <cary@ilm.com> Cary Phillips <cary@rnd-build7-sf-38.lucasfilm.com>
Cary Phillips <cary@ilm.com> seabeepea <seabeepea@gmail.com>
Catherine <catyeo18@users.noreply.github.com>
Chris Leu <cdleu430@gmail.com> cdleu430 <cdleu430@gmail.com>
Christina Tempelaar-Lietz <xlietz@gmail.com> ¨Christina Tempelaar-Lietz¨ <xlietz@gmail.com>
Christina Tempelaar-Lietz <xlietz@gmail.com> xlietz <31363633+xlietz@users.noreply.github.com>
Christopher Horvath <blackencino+git@gmail.com>
Christopher Kulla <fpsunflower@users.noreply.github.com>
Christoph Gohlke <cgohlke@cgohlke.com>
cia-rana <kiwamura0314@gmail.com>
Cristian Martínez <cfuga@cfuga.mx>
Dan Horák <dan@danny.cz>
Daniel Kaneider <danielkaneider@users.sf.net>
Darby Johnston <darbyjohnston@yahoo.com>
Dave Sawyer <kingsawyer@gmail.com>
David Korczynski <david@adalogics.com>
Developer-Ecosystem-Engineering <65677710+Developer-Ecosystem-Engineering@users.noreply.github.com>
dgmzc <dorian.gmz@hotmail.com>
Diogo Teles Sant'Anna <diogoteles@google.com>
Dirk Lemstra <dirk@lemstra.org> dirk <dirk@git.imagemagick.org>
dracwyrm <j.scruggs@gmail.com>
Drew Hess <dhess@ilm.com>
Ed Hanway <ehanway@ilm.com> Ed Hanway <ehanway-ilm@users.noreply.github.com>
Edward Kmett <ekmett@gmail.com>
Eric Sommerlade <es0m@users.noreply.github.com>
Eric Wimmer <ewimmer@ilm.com>
E Sommerlade <es0m@users.noreply.github.com>
fgc <fgc>
Florian Kainz <kainz@ilm.com>
Grant Kim <6302240+enpinion@users.noreply.github.com>
Gregorio Litenstein <g.litenstein@gmail.com>
Gyula Gubacsi <gyula.gubacsi@foundry.com>
Halfdan Ingvarsson <halfdan@sidefx.com>
Harry Mallon <hjmallon@gmail.com>
Huibean Luo <huibean.luo@gmail.com>
ianianda <wyking1030@gmail.com>
Ibraheem Alhashim <ibraheem.alhashim@gmail.com>
Jack Kingsman <jack.kingsman@gmail.com>
Jamie Kenyon <jamie.kenyon@thefoundry.co.uk>
Jan Tojnar <jtojnar@gmail.com>
jbradley <jbradley@dreamworks.com>
Jean-Francois Panisset <32653482+jfpanisset@users.noreply.github.com>
Jens Lindgren <lindgren_jens@hotmail.com>
Ji Hun Yu <jihun@ilm.com>
Johannes Vollmer <32042925+johannesvollmer@users.noreply.github.com>
John Loy <jloy@pixar.com>
John Mertic <jmertic@linuxfoundation.org>
Jonathan Stone <stonej@github.com>
Jose Luis Cercos-Pita <jlcercos@gmail.com> Jose Luis Cercós Pita <lacigarracomunista@gmail.com>
Joseph Goldstone <joseph.goldstone@mac.com> Joseph Goldstone <jgoldstone@arri.com>
Juha Reunanen <juha.reunanen@tomaattinen.com>
Julian Amann <julian.amann@tum.de>
Juri Abramov <openexr@dr-abramov.de> Juri Abramov <gabramov@nvidia.com>
Juri Abramov <openexr@dr-abramov.de> JuriAbramov <openexr@dr-abramov.de>
Karl Hendrikse <karlhendrikse@gmail.com> karlhendrikse <karlhendrikse@gmail.com>
Karl Rasche <karl.rasche@dreamworks.com> Karl Rasche <karlrasche@users.noreply.github.com>
Kevin Wheatley <kevin.wheatley@framestore.com>
Kimball Thurston <kdt3rd@gmail.com>
kwizart <kwizart@gmail.com>
Larry Gritz <lg@larrygritz.com>
Laurens Voerman <l.voerman@rug.nl>
L. E. Segovia <amy@amyspark.me>
Liam Fernandez <liam@utexas.edu>
lilinjie <lilinjie@uniontech.com>
Lucy Wilkes <lucywilkes@users.noreply.github.com>
luzpaz <luzpaz@users.noreply.github.com>
mancoast <RobertPancoast77@gmail.com>
mandree <mandree@users.noreply.github.com>
Mark Reid <mindmark@gmail.com>
Mark Sisson <5761292+marksisson@users.noreply.github.com>
Martin Aumüller <aumuell@reserv.at>
Martin Husemann <martin@NetBSD.org>
Matthäus G. Chajdas <Anteru@users.noreply.github.com>
Matthias C. M. Troffaes <matthias.troffaes@gmail.com>
Matt Pharr <matt@pharr.org>
Md Sadman Chowdhury <61442059+SpicyCatGames@users.noreply.github.com>
Michael Thomas (malinka) <malinka@entropy-development.com>
Nicholas Yue <yue.nicholas@gmail.com>
Nick Porcino <meshula@hotmail.com> meshula <nick.porcino@gmail.com>
Nick Porcino <meshula@hotmail.com> Nick Porcino <nick.porcino@oculus.com>
Nick Porcino <meshula@hotmail.com> Nick Porcino <nporcino@pixar.com>
Nick Porcino <meshula@hotmail.com> nporcino-pixar <78001580+nporcino-pixar@users.noreply.github.com>
Nick Rasmussen <nick@ilm.com> Nick Rasmussen <nick@jive.org>
Nick Rasmussen <nick@ilm.com> Nick Rasmussen <nick.rasmussen@gmail.com>
Nicolas Chauvet <kwizart@gmail.com>
Niklas Hambüchen <mail@nh2.me>
OgreTransporter <OgreTransporter@users.noreply.github.com>
oleksii.vorobiov <oleksii.vorobiov@globallogic.com>
Owen Thompson <oxt3479@rit.edu>
patlefort <northon_patrick3@yahoo.ca>
Paul Schneider <pauls@ilm.com>
Peter Hillman <peterh@wetafx.co.nz> peterhillman <peterh@wetafx.co.nz>
Peter Hillman <peterh@wetafx.co.nz> peterhillman <peter@peterhillman.org.uk>
Peter Steneteg <peter@steneteg.se>
Peter Urbanec <peterurbanec@users.noreply.github.com>
Phyrexian <jarko.paska@gmail.com>
Piotr Stanczyk <piotr.stanczyk@gmail.com> Piotr <pstanczyk@ilm.com>
Piotr Stanczyk <piotr.stanczyk@gmail.com> Piotr Stanczyk <pstanczyk@ilm.com>
Piotr Stanczyk <piotr.stanczyk@gmail.com> pstanczyk <pstanczyk@ilm.com>
Ralph Potter <r.potter@bath.ac.uk>
r-a-sattarov <r.a.sattarov@yandex.ru>
Rémi Achard <remiachard@gmail.com>
Reto Kromer <retokromer@users.noreply.github.com>
Richard Goedeken <Richard@fascinationsoftware.com>
Sergey Fedorov <vital.had@gmail.com>
Shawn Walker-Salas <shawn.walker@oracle.com>
Simon Boorer <sboorer@ilm.com>
Simon Otter <skurmedel@gmail.com>
Srinath Ravichandran <srinath.pop@gmail.com>
Thanh Ha <thanh.ha@linuxfoundation.org>
Thomas Debesse <dev@illwieckz.net>
Thorsten Kaufmann <thorsten.kaufmann@mackevision.de>
Timothy Lyanguzov <theta682@gmail.com>
Transporter <ogre.transporter@gmail.com>
Vertexwahn <julian.amann@tum.de>
Wenzel Jakob <wenzel@inf.ethz.ch>
Wojciech Jarosz <wkjarosz@users.noreply.github.com> wjarosz <wjarosz>
Xo Wang <xow@google.com>
Yaakov Selkowitz <yselkowi@redhat.com>
Yining Karl Li <betajippity@gmail.com>
Yujie Shu <yshu@ilm.com> Yujie Shu (Intern) <yshu@ilm.com>
zengwei2000 <102871671+zengwei2000@users.noreply.github.com>
+828 -579
View File
File diff suppressed because it is too large Load Diff
+40 -10
View File
@@ -48,8 +48,35 @@ set(OPENEXR_LIB_VERSION "${OPENEXR_LIB_SOVERSION}.${OPENEXR_VERSION}") # e.g. "3
option(OPENEXR_INSTALL "Install OpenEXR libraries" ON)
option(OPENEXR_INSTALL_TOOLS "Install OpenEXR tools" ON)
if(OPENEXR_INSTALL_TOOLS AND NOT OPENEXR_INSTALL)
message(SEND_ERROR "OPENEXR_INSTALL_TOOLS requires OPENEXR_INSTALL")
# uninstall target
if(NOT TARGET uninstall)
configure_file(
"${CMAKE_CURRENT_SOURCE_DIR}/cmake/cmake_uninstall.cmake.in"
"${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake"
IMMEDIATE @ONLY)
add_custom_target(uninstall
COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake)
endif()
# uninstall target
if(NOT TARGET uninstall)
configure_file(
"${CMAKE_CURRENT_SOURCE_DIR}/cmake/cmake_uninstall.cmake.in"
"${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake"
IMMEDIATE @ONLY)
add_custom_target(uninstall
COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake)
endif()
# uninstall target
if(NOT TARGET uninstall)
configure_file(
"${CMAKE_CURRENT_SOURCE_DIR}/cmake/cmake_uninstall.cmake.in"
"${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake"
IMMEDIATE @ONLY)
add_custom_target(uninstall
COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake)
endif()
include(cmake/LibraryDefine.cmake)
@@ -73,8 +100,13 @@ if(BUILD_TESTING AND NOT OPENEXR_IS_SUBPROJECT)
endif()
# Include these two modules without enable/disable options
add_subdirectory(src/lib)
add_subdirectory(src/bin)
if (OPENEXR_BUILD_LIBS)
add_subdirectory(src/lib)
endif()
if(OPENEXR_BUILD_TOOLS AND OPENEXR_BUILD_LIBS)
add_subdirectory(src/bin)
endif()
# Tell CMake where to find the OpenEXRConfig.cmake file. Makes it possible to call
# find_package(OpenEXR) in downstream projects
@@ -83,8 +115,7 @@ set(OpenEXR_DIR "${CMAKE_CURRENT_BINARY_DIR}/cmake" CACHE PATH "" FORCE)
# Can be empty since we already defined the targets in add_subdirectory
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/cmake/OpenEXRTargets.cmake" "# Dummy file")
option(OPENEXR_INSTALL_EXAMPLES "Install OpenEXR examples" ON)
if(OPENEXR_INSTALL_EXAMPLES)
if(OPENEXR_BUILD_EXAMPLES AND OPENEXR_BUILD_LIBS)
add_subdirectory( src/examples )
endif()
@@ -112,7 +143,7 @@ endif()
#set(CTEST_DROP_SITE_CDASH TRUE)
include(CTest)
if(BUILD_TESTING AND NOT OPENEXR_IS_SUBPROJECT)
if(BUILD_TESTING AND OPENEXR_BUILD_LIBS AND NOT OPENEXR_IS_SUBPROJECT)
add_subdirectory(src/test)
endif()
@@ -132,12 +163,11 @@ if (BUILD_WEBSITE AND NOT OPENEXR_IS_SUBPROJECT)
add_subdirectory(website)
endif()
if (NOT OPENEXR_IS_SUBPROJECT)
if (OPENEXR_BUILD_LIBS AND NOT OPENEXR_IS_SUBPROJECT)
# Even if not building the website, still make sure the website example code compiles.
add_subdirectory(website/src)
endif()
option(OPENEXR_BUILD_PYTHON "Set ON to build python bindings")
if (OPENEXR_BUILD_PYTHON AND NOT OPENEXR_IS_SUBPROJECT)
if (OPENEXR_BUILD_PYTHON AND OPENEXR_BUILD_LIBS AND NOT OPENEXR_IS_SUBPROJECT)
add_subdirectory(src/wrappers/python)
endif()
+26 -14
View File
@@ -211,7 +211,7 @@ openexr-dev@lists.aswf.io mail list.
### Pull Requests
Contributions should be submitted as Github pull requests. See
Contributions should be submitted as GitHub pull requests. See
[Creating a pull request](https://help.github.com/articles/creating-a-pull-request/)
if you're unfamiliar with this concept.
@@ -227,7 +227,7 @@ with a separate pull request.
3. Push commits to your fork.
4. Create a Github pull request from your topic branch.
4. Create a GitHub pull request from your topic branch.
5. Pull requests will be reviewed by project committers and contributors,
who may discuss, offer constructive feedback, request changes, or approve
@@ -591,14 +591,26 @@ The preferred workflow is:
d. Send an email update to ``openexr-dev@lists.aswf.io`` notifying
the community of the addition and the new tag.
7. Publish the release
7. Create a signed release tag
a. Make sure you have a [GPG
key](https://docs.github.com/en/authentication/managing-commit-signature-verification/generating-a-new-gpg-key)
and it is
[registered](https://docs.github.com/en/authentication/managing-commit-signature-verification/telling-git-about-your-signing-key)
with your GitHub account and git config.
b. Create a signed tag with the release name via `git tag -s v3.1.9`.
c. Push the tag via `git push --tags`
8. Publish the release
a. Click the "Publish release" button on the GitHub release draft
b. Send an email to ``openexr-dev@lists.aswf.io`` officially
annoucing the release.
8. Update the ``release`` branch, which should always point to the
9. Update the ``release`` branch, which should always point to the
most recent patch of the most recent minor release, i.e. the most
preferred release.
@@ -608,24 +620,24 @@ The preferred workflow is:
% git merge RB-3.1
% git push
9. Submit a PR that adds the release notes to [CHANGES.md](CHANGES.md)
on the main branch. Cherry-pick the release notes commit from
the release branch.
10. Submit a PR that adds the release notes to [CHANGES.md](CHANGES.md)
on the main branch. Cherry-pick the release notes commit from
the release branch.
- If any changes have gone into [SECURITY.md](SECURITY), cherry-pick
the associated commit as well.
- If any changes have gone into [SECURITY.md](SECURITY), cherry-pick
the associated commit as well.
- Also include in this PR edits to [``docs/news.rst``](docs/news.rst)
that add an announcment of the release.
- Also include in this PR edits to [``docs/news.rst``](docs/news.rst)
that add an announcment of the release.
10. After review/merge of the updates to ``docs/news.rst``, build the
11. After review/merge of the updates to ``docs/news.rst``, build the
website at https://readthedocs.org/projects/openexr.
11. If the release has resolved any OSS-Fuzz issues, update the
12. If the release has resolved any OSS-Fuzz issues, update the
associated pages at https://bugs.chromium.org/p/oss-fuzz with a
reference to the release.
12. If the release has resolved any public CVE's, request an update
13. If the release has resolved any public CVE's, request an update
from the registry service providing the release and a link to the
release notes.
+47 -2
View File
@@ -4,20 +4,37 @@
This is a list of contributors to the OpenEXR project, sorted
alphabetically by first name.
If you know of missing, please email: info@openexr.com.
If you know of missing, please email info@openexr.com or submit a PR.
* Aaron Demolder
* Abe Fettig
* Aloys Baillet
* Andre Mazzone
* Andrew Kunz
* Anton Dukhovnikov
* Antonio Rojas
* Aras Pranckevičius
* Arkady Shapkin
* Arkell Rasiah
* Axel Waggershauser
* Balázs Oroszi
* Barnaby Robson
* Ben Grimes
* Brendan Bolles
* Cary Phillips
* Chris Leu
* Christina Tempelaar-Lietz
* Christopher Horvath
* Christopher Kulla
* Christoph Gohlke
* Cristian Martínez
* Dan Horák
* Daniel Kaneider
* Darby Johnston
* Dave Sawyer
* David Korczynski
* Diogo Teles Sant'Anna
* Dirk Lemstra
* Drew Hess
* Ed Hanway
* Edward Kmett
@@ -25,47 +42,75 @@ If you know of missing, please email: info@openexr.com.
* Eric Wimmer
* E Sommerlade
* Florian Kainz
* Grant Kim
* Gregorio Litenstein
* Gyula Gubacsi
* Halfdan Ingvarsson
* Harry Mallon
* Huibean Luo
* Ibraheem Alhashim
* Jack Kingsman
* Jamie Kenyon
* Jan Tojnar
* Jean-Francois Panisset
* Jens Lindgren
* Ji Hun Yu
* Johannes Vollmer
* John Loy
* John Mertic
* Jonathan Stone
* Jose Luis Cercos-Pita
* Joseph Goldstone
* Juha Reunanen
* Julian Amann
* Juri Abramov
* Karl Hendrikse
* Karl Rasche
* Kevin Wheatley
* Kimball Thurston
* Larry Gritz
* Laurens Voerman
* L. E. Segovia
* Liam Fernandez
* Lucy Wilkes
* Mark Reid
* Mark Sisson
* Martin Aumüller
* Martin Husemann
* Matthäus G. Chajdas
* Matthias C. M. Troffaes
* Matt Pharr
* Md Sadman Chowdhury
* Michael Thomas
* Nicholas Yue
* Nick Porcino
* Nick Rasmussen
* Nicolas Chauvet
* Niklas Hambüchen
* OgreTransporter
* Owen Thompson
* Paul Schneider
* Peter Hillman
* Peter Steneteg
* Peter Urbanec
* Phil Barrett
* Piotr Stanczyk
* Ralph Potter
* Rémi Achard
* Reto Kromer
* Richard Goedeken
* Sergey Fedorov
* Shawn Walker-Salas
* Simon Boorer
* Simon Otter
* Srinath Ravichandran
* Thanh Ha
* Thomas Debesse
* Thorsten Kaufmann
* Timothy Lyanguzov
* Wenzel Jakob
* Wojciech Jarosz
* Xo Wang
* Yaakov Selkowitz
* Yining Karl Li
* Yujie Shu
-3
View File
@@ -1,6 +1,3 @@
<!-- SPDX-License-Identifier: BSD-3-Clause -->
<!-- Copyright (c) Contributors to the OpenEXR Project -->
Copyright (c) Contributors to the OpenEXR Project. All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+7 -4
View File
@@ -1,9 +1,12 @@
# SPDX-License-Identifier: BSD-3-Clause
# Copyright Contributors to the OpenEXR Project.
module(
name = "openexr",
version = "0.0.0", # The version "0.0.0" is set here and will be patched within the Bazel Central Registry
compatibility_level = 1,
)
bazel_dep(name = "bazel_skylib", version = "1.4.2")
bazel_dep(name = "imath", version = "3.1.9")
bazel_dep(name = "libdeflate", version = "1.18")
bazel_dep(name = "bazel_skylib", version = "1.5.0")
bazel_dep(name = "imath", version = "3.1.10", repo_name = "Imath")
bazel_dep(name = "libdeflate", version = "1.19")
bazel_dep(name = "platforms", version = "0.0.8")
+6 -7
View File
@@ -58,7 +58,7 @@ for more information.
# Quick Start
See the [technical documentation](https://openexr.readthedocs.io) for
complete details, but to get started, the "hello, world" `.exr` writer program is:
complete details, but to get started, the "Hello, world" [`exrwriter.cpp`](https://raw.githubusercontent.com/AcademySoftwareFoundation/openexr/main/website/src/exrwriter/exrwriter.cpp) writer program is:
#include <ImfRgbaFile.h>
#include <ImfArray.h>
@@ -86,20 +86,19 @@ complete details, but to get started, the "hello, world" `.exr` writer program i
return 0;
}
The `CMakeLists.txt` to build:
The [`CMakeLists.txt`](https://raw.githubusercontent.com/AcademySoftwareFoundation/openexr/main/website/src/exrwriter/CMakeLists.txt) to build:
cmake_minimum_required(VERSION 3.10)
cmake_minimum_required(VERSION 3.12)
project(exrwriter)
find_package(OpenEXR REQUIRED)
add_executable(${PROJECT_NAME} writer.cpp)
add_executable(${PROJECT_NAME} exrwriter.cpp)
target_link_libraries(${PROJECT_NAME} OpenEXR::OpenEXR)
To build:
$ mkdir _build
$ cmake -S . -B _build
$ make --build _build
$ cmake -S . -B _build -DCMAKE_PREFIX_PATH=<path to OpenEXR libraries/includes>
$ cmake --build _build
For more details, see [The OpenEXR
API](https://openexr.readthedocs.io/en/latest/API.html#the-openexr-api).
+125 -7
View File
@@ -6,19 +6,21 @@
## Reporting a Vulnerability
If you think you've found a potential vulnerability in OpenEXR, please
report it by emailing security@openexr.com. Only Technical Steering
Committee members and Academy Software Foundation project management
have access to these messages. Include detailed steps to reproduce the
issue, and any other information that could aid an investigation. Our
policy is to respond to vulnerability reports within 14 days.
report it by filing a GitHub [security
advisory](https://github.com/AcademySoftwareFoundation/openexr/security/advisories/new). Alternatively,
email security@openexr.com and provide your contact info for further
private/secure discussion. If your email does not receive a prompt
acknowledgement, your address may be blocked.
Our policy is to address critical security vulnerabilities rapidly and
post patches as quickly as possible.
Our policy is to acknowledge the receipt of vulnerability reports
within 48 hours. Our policy is to address critical security vulnerabilities
rapidly and post patches within 14 days if possible.
## Known Vulnerabilities
These vulnerabilities are present in the given versions:
* [CVE-2023-5841](https://takeonme.org/cves/CVE-2023-5841.html) 3.1.0, 3.1.1, 3.1.2, 3.1.3 3.1.4 3.1.5 3.1.6 3.1.7 3.1.8 3.1.9 3.1.10 3.1.11 3.2.0 3.2.1
* [CVE-2021-45942](https://nvd.nist.gov/vuln/detail/CVE-2021-45942) 3.1.0, 3.1.1, 3.1.2, 3.1.3
* [CVE-2021-20296](https://nvd.nist.gov/vuln/detail/CVE-2021-20296) 2.4.0, 2.4.1, 2.4.2, 2.5.0, 2.5.1, 2.5.2, 2.5.3
* [CVE-2021-3479](https://nvd.nist.gov/vuln/detail/CVE-2021-3479) 2.4.0, 2.4.1, 2.4.2, 2.5.0, 2.5.1, 2.5.2, 2.5.3
@@ -57,3 +59,119 @@ These vulnerabilities are present in the given versions:
See the [release notes](CHANGES.md) for more information.
## Supported Versions
This gives guidance about which branches are supported with patches to
security vulnerabilities.
| Version / branch | Supported |
| --------- | ---------------------------------------------------- |
| main | :white_check_mark: :construction: ALL fixes immediately, but this is a branch under development with a frequently unstable ABI and occasionally unstable API. |
| 3.2.x | :white_check_mark: All fixes that can be backported without breaking ABI compatibility. |
| 3.1.x | :warning: Only the most critical fixes, only if they can be easily backported. |
| 3.0.x | :warning: Only the most critical fixes, only if they can be easily backported. |
| 2.5.x | :warning: Only the most critical fixes, only if they can be easily backported. |
| <= 1.x | :x: No longer receiving patches of any kind. |
## Signed Releases
Releases artifacts are signed via
[sigstore](https://www.sigstore.dev). See
[release-sign.yml](.github/workflows/release-sign.yml) for details.
To verify a downloaded release at a given tag:
% pip install sigstore
% sigstore verify github --cert-identity https://github.com/AcademySoftwareFoundation/openexr/.github/workflows/release-sign.yml@refs/tags/<tag> openexr-<tag>.tar.gz
## Security Expectations
### Software Features
- The OpenEXR project implements the EXR image file format, used
throughout the motion picture industry and beyond, on Linux, macOS,
and Windows.
- The project consists of a software run-time library, implemented in
C/C++ and built via cmake, that reads and writes image data
files. The project also distributes python wrappings for the C/C++
I/O API.
- The library reads and writes binary image data and text-based
metadata, treated as blind data, none of which is executable code.
- Other than the website and online technical documentation, the
project implements no web/online services or network communication
protocols. The library never requests any security or
authentication credentials or login information from users.
The website implements no interactive features and requires no login
credentials.
- The library reads and writes only to file paths specificly requested
via the C/C++ API. The runtime library uses no system configuration
files or sidecar data files. Access to data files uses only standard
file I/O system calls.
- The library compresses/decompresses data via standard compression
algorithms but uses no cryptographic or confidentiality protocols.
### Software Dependencies
OpenEXR depends on
[Imath](https://github.com/AcademySoftwareFoundation/Imath), a library
of basic math operations also maintained and distributed by the
OpenEXR project. Imath follows the same security conventions
documented here for OpenEXR itself. The core Imath library has no
external dependencies. The Imath python bindings depend on python and
boost.
The only
external library dependency of OpenEXR is
[libdeflate](https://github.com/ebiggers/libdeflate), which implements
standard deflate/zlib/gzip compression and decompression.
The project uses
[Snyk](https://github.com/AcademySoftwareFoundation/openexr/blob/main/.github/workflows/snyk-scan-pr.yml)
to scan for dependency vulnerability.
### Potential Vulnerabilities
Potential entry points are images being loaded using the
library. Malformed images could caused issues such as heap buffer
overflows, out-of-memory faults, or segmentation faults that could be
exploitable as denial-of-service attacks.
### Hardening
#### Testing
The OpenEXR project implements a comprehensive suite of validation
tests, including fuzz testing to harden against malicious input
data. Note that fuzz testing hardens only against *small* input data
files and is not a comprehensive test against all potential input.
Note that the
[exrcheck](https://github.com/AcademySoftwareFoundation/openexr/tree/main/src/bin/exrcheck)
utility is intended to be used by testers to demonstrate a particular
proof-of-concept input file exposes a vulnerability, and it is very
helpful to let us know if a vulnerability can be reproduced using that
tool.
The project also uses the [OSS
Fuzz](https://bugs.chromium.org/p/oss-fuzz) service for continuous
fuzz testing.
#### Development Cycle and Distribution
OpenEXR is downloadable and buildable by C/C++ source via GitHub. Only
members of the project's Technical Steering Committee, all veteran
software engineers at major motion picture studios or vendors, have
write permissions on the source code repository. All critical software
changes are reviewed by multiple TSC members.
The library is distributed in binary form via many common package
managers across all platforms.
+9 -9
View File
@@ -13,26 +13,26 @@ def openexr_deps():
http_archive,
name = "libdeflate",
build_file = "@com_openexr//:bazel/third_party/libdeflate.BUILD",
sha256 = "225d982bcaf553221c76726358d2ea139bb34913180b20823c782cede060affd",
strip_prefix = "libdeflate-1.18",
urls = ["https://github.com/ebiggers/libdeflate/archive/refs/tags/v1.18.tar.gz"],
sha256 = "27bf62d71cd64728ff43a9feb92f2ac2f2bf748986d856133cc1e51992428c25",
strip_prefix = "libdeflate-1.19",
urls = ["https://github.com/ebiggers/libdeflate/archive/refs/tags/v1.19.tar.gz"],
)
maybe(
http_archive,
name = "Imath",
build_file = "@com_openexr//:bazel/third_party/Imath.BUILD",
strip_prefix = "Imath-3.1.9",
sha256 = "f1d8aacd46afed958babfced3190d2d3c8209b66da451f556abd6da94c165cf3",
urls = ["https://github.com/AcademySoftwareFoundation/Imath/archive/refs/tags/v3.1.9.tar.gz"],
strip_prefix = "Imath-3.1.10",
sha256 = "f2943e86bfb694e216c60b9a169e5356f8a90f18fbd34d7b6e3450be14f60b10",
urls = ["https://github.com/AcademySoftwareFoundation/Imath/archive/refs/tags/v3.1.10.tar.gz"],
)
maybe(
http_archive,
name = "bazel_skylib",
sha256 = "66ffd9315665bfaafc96b52278f57c7e2dd09f5ede279ea6d39b2be471e7e3aa",
sha256 = "cd55a062e763b9349921f0f5db8c3933288dc8ba4f76dd9416aac68acee3cb94",
urls = [
"https://mirror.bazel.build/github.com/bazelbuild/bazel-skylib/releases/download/1.4.2/bazel-skylib-1.4.2.tar.gz",
"https://github.com/bazelbuild/bazel-skylib/releases/download/1.4.2/bazel-skylib-1.4.2.tar.gz",
"https://mirror.bazel.build/github.com/bazelbuild/bazel-skylib/releases/download/1.5.0/bazel-skylib-1.5.0.tar.gz",
"https://github.com/bazelbuild/bazel-skylib/releases/download/1.5.0/bazel-skylib-1.5.0.tar.gz",
],
)
+2 -10
View File
@@ -74,15 +74,6 @@ configure_file(OpenEXRConfigInternal.h.in ${CMAKE_CURRENT_BINARY_DIR}/OpenEXRCon
# make a temp copy in the binary dir for OpenEXRConfig.h to include
configure_file(../src/lib/OpenEXRCore/openexr_version.h ${CMAKE_CURRENT_BINARY_DIR}/OpenEXRCore/openexr_version.h COPYONLY)
if(OPENEXR_INSTALL)
install(
FILES
${CMAKE_CURRENT_BINARY_DIR}/OpenEXRConfig.h
DESTINATION
${CMAKE_INSTALL_INCLUDEDIR}/${OPENEXR_OUTPUT_SUBDIR}
)
endif()
###################################################
####### IexConfig.h and IexConfigInternal.h
@@ -219,8 +210,9 @@ if(OPENEXR_INSTALL_PKG_CONFIG)
)
endfunction()
openexr_pkg_config_help(OpenEXR.pc.in)
message(STATUS "OpenEXR pkg-config generation enabled")
else()
message(STATUS "-- pkg-config generation disabled")
message(STATUS "OpenEXR pkg-config generation disabled")
endif()
###################################################
+2 -1
View File
@@ -93,7 +93,8 @@ function(OPENEXR_DEFINE_LIBRARY libname)
string(TOUPPER "${CMAKE_BUILD_TYPE}" uppercase_CMAKE_BUILD_TYPE)
set(verlibname ${CMAKE_SHARED_LIBRARY_PREFIX}${libname}${OPENEXR_LIB_SUFFIX}${CMAKE_${uppercase_CMAKE_BUILD_TYPE}_POSTFIX}${CMAKE_SHARED_LIBRARY_SUFFIX})
set(baselibname ${CMAKE_SHARED_LIBRARY_PREFIX}${libname}${CMAKE_${uppercase_CMAKE_BUILD_TYPE}_POSTFIX}${CMAKE_SHARED_LIBRARY_SUFFIX})
install(CODE "execute_process(COMMAND ${CMAKE_COMMAND} -E chdir \"\$ENV\{DESTDIR\}${CMAKE_INSTALL_FULL_LIBDIR}\" ${CMAKE_COMMAND} -E create_symlink ${verlibname} ${baselibname})")
file(CREATE_LINK ${verlibname} ${CMAKE_CURRENT_BINARY_DIR}/${baselibname} SYMBOLIC)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${baselibname} DESTINATION ${CMAKE_INSTALL_FULL_LIBDIR})
install(CODE "message(STATUS \"Creating symlink ${CMAKE_INSTALL_FULL_LIBDIR}/${baselibname} -> ${verlibname}\")")
set(verlibname)
set(baselibname)
+1
View File
@@ -17,4 +17,5 @@ Version: @OPENEXR_VERSION@
Libs: @exr_pthread_libs@ -L${libdir} -lOpenEXR${libsuffix} -lOpenEXRUtil${libsuffix} -lOpenEXRCore${libsuffix} -lIex${libsuffix} -lIlmThread${libsuffix}
Cflags: -I${includedir} -I${OpenEXR_includedir} @exr_pthread_cflags@
Requires: Imath
Requires.private: @EXR_DEFLATE_PKGCONFIG_REQUIRES@
+6 -2
View File
@@ -8,11 +8,15 @@ include(CMakeFindDependencyMacro)
set(openexr_needthreads @OPENEXR_ENABLE_THREADING@)
if (openexr_needthreads)
set(THREADS_PREFER_PTHREAD_FLAG ON)
find_dependency(Threads REQUIRED)
find_dependency(Threads)
endif()
unset(openexr_needthreads)
find_dependency(Imath REQUIRED)
find_dependency(Imath)
if (@libdeflate_FOUND@)
find_dependency(libdeflate)
endif()
include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake")
check_required_components("@PROJECT_NAME@")
+4 -4
View File
@@ -68,10 +68,10 @@
// clang-format on
// Version as a single hex number, e.g. 0x01000300 == 1.0.3
#define OPENEXR_VERSION_HEX \
((uint32_t (OPENEXR_VERSION_MAJOR) << 24) | \
(uint32_t (OPENEXR_VERSION_MINOR) << 16) | \
(uint32_t (OPENEXR_VERSION_PATCH) << 8))
#define OPENEXR_VERSION_HEX \
(((OPENEXR_VERSION_MAJOR) << 24) | \
((OPENEXR_VERSION_MINOR) << 16) | \
((OPENEXR_VERSION_PATCH) << 8))
// On modern versions of gcc & clang, __has_attribute can test support for
// __attribute__((attr)). Make sure it's safe for other compilers.
+60 -16
View File
@@ -36,11 +36,7 @@ set(IEX_INTERNAL_NAMESPACE "Iex_${OPENEXR_VERSION_API}" CACHE STRING "Real names
set(IEX_NAMESPACE "Iex" CACHE STRING "Public namespace alias for Iex")
# Whether to generate and install a pkg-config file OpenEXR.pc
if (WIN32)
option(OPENEXR_INSTALL_PKG_CONFIG "Install OpenEXR.pc file" OFF)
else()
option(OPENEXR_INSTALL_PKG_CONFIG "Install OpenEXR.pc file" ON)
endif()
# Whether to enable threading. This can be disabled, although thread pool and tasks
# are still used, just processed immediately
@@ -55,8 +51,22 @@ option(OPENEXR_ENABLE_LARGE_STACK "Enables code to take advantage of large stack
########################
## Build related options
# Whether to build & install the various command line utility programs
option(OPENEXR_INSTALL "Install OpenEXR libraries/binaries/bindings" ON)
# Whether to build & install the main libraries
option(OPENEXR_BUILD_LIBS "Enables building of main libraries" ON)
# Whether to build the various command line utility programs
option(OPENEXR_BUILD_TOOLS "Enables building of utility programs" ON)
option(OPENEXR_INSTALL_TOOLS "Install OpenEXR tools" ON)
option(OPENEXR_BUILD_EXAMPLES "Build and install OpenEXR examples" ON)
option(OPENEXR_BUILD_PYTHON "Build python bindings" OFF)
option(OPENEXR_TEST_LIBRARIES "Run library tests" ON)
option(OPENEXR_TEST_TOOLS "Run tool tests" ON)
option(OPENEXR_TEST_PYTHON "Run python binding tests" ON)
# This is a variable here for use in controlling where include files are
# installed. Care must be taken when changing this, as many things
@@ -142,6 +152,10 @@ if(OPENEXR_USE_CLANG_TIDY)
)
endif()
if (NOT OPENEXR_BUILD_LIBS)
return()
endif()
###############################
# Dependent libraries
@@ -164,12 +178,40 @@ set(OPENEXR_DEFLATE_TAG "v1.18" CACHE STRING "Tag to use for libdeflate source r
if(NOT OPENEXR_FORCE_INTERNAL_DEFLATE)
#TODO: ^^ Release should not clone from main, this is a place holder
set(CMAKE_IGNORE_PATH "${CMAKE_CURRENT_BINARY_DIR}/_deps/deflate-src/config;${CMAKE_CURRENT_BINARY_DIR}/_deps/deflate-build/config")
include(FindPkgConfig)
pkg_check_modules(deflate IMPORTED_TARGET GLOBAL libdeflate)
# First try cmake config
find_package(libdeflate CONFIG QUIET)
if(libdeflate_FOUND)
if(TARGET libdeflate::libdeflate_shared)
set(EXR_DEFLATE_LIB libdeflate::libdeflate_shared)
else()
set(EXR_DEFLATE_LIB libdeflate::libdeflate_static)
endif()
set(EXR_DEFLATE_VERSION ${libdeflate_VERSION})
message(STATUS "Using libdeflate from ${libdeflate_DIR}")
else()
# If not found, try pkgconfig
find_package(PkgConfig)
if(PKG_CONFIG_FOUND)
include(FindPkgConfig)
pkg_check_modules(deflate IMPORTED_TARGET GLOBAL libdeflate)
if(deflate_FOUND)
set(EXR_DEFLATE_LIB PkgConfig::deflate)
set(EXR_DEFLATE_VERSION ${deflate_VERSION})
message(STATUS "Using libdeflate from ${deflate_LINK_LIBRARIES}")
endif()
endif()
endif()
set(CMAKE_IGNORE_PATH)
endif()
if(NOT TARGET PkgConfig::deflate AND NOT deflate_FOUND)
if(EXR_DEFLATE_LIB)
# Using external library
set(EXR_DEFLATE_SOURCES)
set(EXR_DEFLATE_INCLUDE_DIR)
# For OpenEXR.pc.in for static build
set(EXR_DEFLATE_PKGCONFIG_REQUIRES "libdeflate >= ${EXR_DEFLATE_VERSION}")
else()
# Using internal deflate
if(OPENEXR_FORCE_INTERNAL_DEFLATE)
message(STATUS "libdeflate forced internal, installing from ${OPENEXR_DEFLATE_REPO} (${OPENEXR_DEFLATE_TAG})")
else()
@@ -214,10 +256,6 @@ if(NOT TARGET PkgConfig::deflate AND NOT deflate_FOUND)
list(TRANSFORM EXR_DEFLATE_SOURCES PREPEND ${deflate_SOURCE_DIR}/)
set(EXR_DEFLATE_INCLUDE_DIR ${deflate_SOURCE_DIR})
set(EXR_DEFLATE_LIB)
else()
set(EXR_DEFLATE_INCLUDE_DIR)
set(EXR_DEFLATE_LIB PkgConfig::deflate)
set(EXR_DEFLATE_SOURCES)
endif()
#######################################
@@ -226,10 +264,8 @@ endif()
option(OPENEXR_FORCE_INTERNAL_IMATH "Force using an internal imath" OFF)
# Check to see if Imath is installed outside of the current build directory.
set(OPENEXR_IMATH_REPO "https://github.com/AcademySoftwareFoundation/Imath.git" CACHE STRING
"Repo for auto-build of Imath")
set(OPENEXR_IMATH_TAG "v3.1.9" CACHE STRING
"Tag for auto-build of Imath (branch, tag, or SHA)")
set(OPENEXR_IMATH_REPO "https://github.com/AcademySoftwareFoundation/Imath.git" CACHE STRING "Repo for auto-build of Imath")
set(OPENEXR_IMATH_TAG "v3.1.11" CACHE STRING "Tag for auto-build of Imath (branch, tag, or SHA)")
if(NOT OPENEXR_FORCE_INTERNAL_IMATH)
#TODO: ^^ Release should not clone from main, this is a place holder
set(CMAKE_IGNORE_PATH "${CMAKE_CURRENT_BINARY_DIR}/_deps/imath-src/config;${CMAKE_CURRENT_BINARY_DIR}/_deps/imath-build/config")
@@ -253,6 +289,14 @@ if(NOT TARGET Imath::Imath AND NOT Imath_FOUND)
FetchContent_GetProperties(Imath)
if(NOT Imath_POPULATED)
FetchContent_Populate(Imath)
# Propagate OpenEXR's install setting to Imath
set(IMATH_INSTALL ${OPENEXR_INSTALL})
# Propagate OpenEXR's setting for pkg-config generation to Imath:
# If OpenEXR is generating it, the internal Imath should, too.
set(IMATH_INSTALL_PKG_CONFIG ${OPENEXR_INSTALL_PKG_CONFIG})
# hrm, cmake makes Imath lowercase for the properties (to imath)
add_subdirectory(${imath_SOURCE_DIR} ${imath_BINARY_DIR})
endif()
+23
View File
@@ -0,0 +1,23 @@
# Source: https://gitlab.kitware.com/cmake/community/-/wikis/FAQ#can-i-do-make-uninstall-with-cmake
if(NOT EXISTS "@CMAKE_BINARY_DIR@/install_manifest.txt")
message(FATAL_ERROR "Cannot find install manifest: @CMAKE_BINARY_DIR@/install_manifest.txt")
endif()
file(READ "@CMAKE_BINARY_DIR@/install_manifest.txt" files)
string(REGEX REPLACE "\n" ";" files "${files}")
foreach(file ${files})
message(STATUS "Uninstalling $ENV{DESTDIR}${file}")
if(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}")
exec_program(
"@CMAKE_COMMAND@" ARGS
"-E remove \"$ENV{DESTDIR}${file}\""
OUTPUT_VARIABLE rm_out
RETURN_VALUE rm_retval)
if(NOT "${rm_retval}" STREQUAL 0)
message(FATAL_ERROR "Problem when removing $ENV{DESTDIR}${file}")
endif()
else(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}")
message(STATUS "File $ENV{DESTDIR}${file} does not exist.")
endif()
endforeach()
+66
View File
@@ -0,0 +1,66 @@
# SPDX-License-Identifier: BSD-3-Clause
# Copyright (c) Contributors to the OpenEXR Project.
[build-system]
requires = ["scikit-build-core==0.8.1"]
build-backend = "scikit_build_core.build"
[project]
name = "OpenEXR"
dynamic = ["version"]
description="Python bindings for the OpenEXR image file format"
readme = "src/wrappers/python/README.md"
authors = [
{ name="Contributors to the OpenEXR project", email="info@openexr.com" },
]
requires-python = ">=3.7"
[project.urls]
"Homepage" = "https://openexr.com"
"Source" = "https://github.com/AcademySoftwareFoundation/OpenEXR"
"Bug Tracker" = "https://github.com/AcademySoftwareFoundation/OpenEXR/issues"
[project.optional-dependencies]
test = ["pytest"]
[tool.scikit-build]
wheel.expand-macos-universal-tags = true
sdist.exclude = [".github", "src/test", "src/examples", "website", "ASWF", "bazel", "share"]
# Only build the PyOpenEXR (cmake --build --target PyOpenEXR).
cmake.targets = ["PyOpenEXR"]
# Only install the "python" component (cmake --install --component python).
# This makes sure that only files marked as "python" component are installed.
install.components = ["python"]
# Enable experimental features if any are available
# In this case we need custom local plugin to get
# the project version from cmake.
experimental = true
metadata.version.provider = "openexr_skbuild_plugin"
metadata.version.provider-path = "./src/wrappers/python"
[tool.scikit-build.cmake.define]
OPENEXR_INSTALL = 'OFF'
OPENEXR_BUILD_PYTHON = 'ON'
OPENEXR_BUILD_EXAMPLES = 'OFF'
OPENEXR_BUILD_TOOLS = 'OFF'
OPENEXR_INSTALL_TOOLS = 'OFF'
OPENEXR_INSTALL_PKG_CONFIG = 'OFF'
OPENEXR_FORCE_INTERNAL_DEFLATE = 'ON'
OPENEXR_FORCE_INTERNAL_IMATH = 'ON'
OPENEXR_TEST_LIBRARIES = 'OFF'
BUILD_SHARED_LIBS = 'OFF'
CMAKE_POSITION_INDEPENDENT_CODE = 'ON'
[tool.cibuildwheel]
test-command = "ctest -R PyOpenEXR"
test-extras = ["test"]
test-skip = ["*universal2:arm64"]
build-verbosity = 1
manylinux-x86_64-image = "manylinux2014"
manylinux-i686-image = "manylinux2014"
manylinux-aarch64-image = "manylinux2014"
+28 -11
View File
@@ -21,26 +21,34 @@ if [[ $# == "0" ]]; then
exit -1
fi
set -x
BUILD_ROOT=$1
SRC_ROOT=$2
# Locate OpenEXR.pc and set PKG_CONFIG_PATH accordingly
pkgconfig=$(find $BUILD_ROOT -name OpenEXR.pc)
pkgconfig=$(find $BUILD_ROOT -name OpenEXR.pc | head -1)
if [[ "$pkgconfig" == "" ]]; then
echo "Can't find OpenEXR.pc"
exit -1
fi
cat $pkgconfig
export PKG_CONFIG_PATH=$(dirname $pkgconfig)
# Build the validation program
CXX_FLAGS=$(pkg-config OpenEXR --cflags)
LD_FLAGS=$(pkg-config OpenEXR --libs --static)
CXX_FLAGS=$(env PKG_CONFIG_PATH=$PKG_CONFIG_PATH pkg-config OpenEXR --cflags)
LD_FLAGS=$(env PKG_CONFIG_PATH=$PKG_CONFIG_PATH pkg-config OpenEXR --libs --static)
VALIDATE_CPP=$(mktemp --tmpdir "validate_XXX.cpp")
VALIDATE_BIN=$(mktemp --tmpdir "validate_XXX")
trap "rm -rf $VALIDATE_CPP $VALIDATE_BIN" exit
VALIDATE_CPP=$(mktemp --tmpdir "validate_cpp_XXX.cpp")
VALIDATE_BIN=$(mktemp --tmpdir "validate_bin_XXX")
VALIDATE_BUILD=$(mktemp -d --tmpdir "validate_build_XXX")
trap "rm -rf $VALIDATE_CPP $VALIDATE_BIN $VALIDATE_BUILD" exit
echo -e '#include <ImfHeader.h>\n#include <OpenEXRConfig.h>\n#include <stdio.h>\nint main() { puts(OPENEXR_PACKAGE_STRING); Imf::Header h; return 0; }' > $VALIDATE_CPP
@@ -48,7 +56,7 @@ g++ $CXX_FLAGS $VALIDATE_CPP -o $VALIDATE_BIN $LD_FLAGS
# Execute the program
LIB_DIR=$(pkg-config OpenEXR --variable=libdir)
LIB_DIR=$(env PKG_CONFIG_PATH=$PKG_CONFIG_PATH pkg-config OpenEXR --variable=libdir)
export LD_LIBRARY_PATH=$LIB_DIR
validate=`$VALIDATE_BIN`
@@ -62,7 +70,7 @@ if [[ "$status" != "0" ]]; then
fi
# Get the suffix, e.g. -2_5_d, and determine if there's also a _d
libsuffix=$(pkg-config OpenEXR --variable=libsuffix)
libsuffix=$(env PKG_CONFIG_PATH=$PKG_CONFIG_PATH pkg-config OpenEXR --variable=libsuffix)
if [[ $libsuffix != $(basename ./$libsuffix _d) ]]; then
_d="_d"
else
@@ -70,7 +78,7 @@ else
fi
# Validate each of the libs
libs=$(pkg-config OpenEXR --libs-only-l | sed -e s/-l//g)
libs=$(env PKG_CONFIG_PATH=$PKG_CONFIG_PATH pkg-config OpenEXR --libs-only-l | sed -e s/-l//g)
for lib in $libs; do
base=$(echo $lib | cut -d- -f1)
@@ -105,7 +113,7 @@ if [[ "$?" == "0" ]]; then
fi
if [[ "$SRC_ROOT" != "" ]]; then
version=$(pkg-config OpenEXR --modversion)
version=$(env PKG_CONFIG_PATH=$PKG_CONFIG_PATH pkg-config OpenEXR --modversion)
notes=$(grep "\* \[Version $version\]" $SRC_ROOT/CHANGES.md | head -1)
if [[ "$notes" == "" ]]; then
echo "No release notes."
@@ -113,5 +121,14 @@ if [[ "$SRC_ROOT" != "" ]]; then
echo "Release notes: $notes"
fi
fi
# Confirm that the example programs build, link, and run
cd $VALIDATE_BUILD
cmake -DCMAKE_PREFIX_PATH=$BUILD_ROOT $SRC_ROOT/src/examples
cmake --build .
./bin/OpenEXRExamples
echo "ok."
+17 -15
View File
@@ -1,18 +1,20 @@
# SPDX-License-Identifier: BSD-3-Clause
# Copyright (c) Contributors to the OpenEXR Project.
##########################
# Binaries / Utilities
##########################
if(OPENEXR_BUILD_TOOLS)
add_subdirectory( exr2aces )
add_subdirectory( exrheader )
add_subdirectory( exrinfo )
add_subdirectory( exrmaketiled )
add_subdirectory( exrstdattr )
add_subdirectory( exrmakepreview )
add_subdirectory( exrenvmap )
add_subdirectory( exrmultiview )
add_subdirectory( exrmultipart )
add_subdirectory( exrcheck )
endif()
##################
# Binaries / Tools
##################
message(STATUS "Building OpenEXR tools")
add_subdirectory( exr2aces )
add_subdirectory( exrheader )
add_subdirectory( exrinfo )
add_subdirectory( exrmaketiled )
add_subdirectory( exrstdattr )
add_subdirectory( exrmakepreview )
add_subdirectory( exrenvmap )
add_subdirectory( exrmultiview )
add_subdirectory( exrmultipart )
add_subdirectory( exrcheck )
add_subdirectory( exrmanifest )
+14
View File
@@ -0,0 +1,14 @@
# SPDX-License-Identifier: BSD-3-Clause
# Copyright (c) Contributors to the OpenEXR Project.
add_executable(exrmanifest main.cpp)
target_link_libraries(exrmanifest OpenEXR::OpenEXR)
set_target_properties(exrmanifest PROPERTIES
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin"
)
if(OPENEXR_INSTALL_TOOLS)
install(TARGETS exrmanifest DESTINATION ${CMAKE_INSTALL_BINDIR})
endif()
if(WIN32 AND BUILD_SHARED_LIBS)
target_compile_definitions(exrmanifest PRIVATE OPENEXR_DLL)
endif()
+210
View File
@@ -0,0 +1,210 @@
//
// SPDX-License-Identifier: BSD-3-Clause
// Copyright (c) Contributors to the OpenEXR Project.
//
//
// output all the idmanifest information found in the file as plain text
//
#include <ImfMisc.h>
#include <ImfMultiPartInputFile.h>
#include <ImfIDManifestAttribute.h>
#include <ImfStandardAttributes.h>
#include <iostream>
#include <vector>
#include <string>
using namespace OPENEXR_IMF_NAMESPACE;
using std::cerr;
using std::cout;
using std::endl;
using std::exception;
using std::max;
using std::ostream;
using std::set;
using std::string;
using std::to_string;
using std::vector;
size_t
dumpManifest (const IDManifest& mfst)
{
size_t uncompressedSize = 0;
for (size_t i = 0; i < mfst.size (); ++i)
{
const IDManifest::ChannelGroupManifest& m = mfst[i];
bool first = true;
if (i > 0) { cout << "\n\n"; }
cout << " channels : ";
for (set<string>::const_iterator s = m.getChannels ().begin ();
s != m.getChannels ().end ();
++s)
{
if (!first) { cout << ','; }
else { first = false; }
cout << *s;
uncompressedSize += s->size () + 1;
}
cout << "\n hashScheme: " << m.getHashScheme () << endl;
cout << " encoding : " << m.getEncodingScheme () << endl;
switch (m.getLifetime ())
{
case IDManifest::LIFETIME_FRAME:
cout << " lifetime : frame\n";
break;
case IDManifest::LIFETIME_SHOT:
cout << " lifetime : shot\n";
break;
case IDManifest::LIFETIME_STABLE:
cout << " lifetime : stable\n";
break;
}
//
// compute max field sizes
//
size_t maxNumLen = 0;
vector<size_t> componentLength (m.getComponents ().size ());
for (size_t c = 0; c < m.getComponents ().size (); ++c)
{
size_t componentSize = m.getComponents ()[c].size ();
uncompressedSize += componentSize + 1;
componentLength[c] = max (componentLength[c], componentSize);
}
for (IDManifest::ChannelGroupManifest::ConstIterator q = m.begin ();
q != m.end ();
++q)
{
size_t stringLen = to_string (q.id ()).size ();
uncompressedSize += stringLen;
maxNumLen = max (maxNumLen, stringLen);
for (size_t i = 0; i < q.text ().size (); i++)
{
uncompressedSize += q.text ()[i].size () + 1;
componentLength[i] =
max (componentLength[i], q.text ()[i].size ());
}
}
cout << " " << string (maxNumLen + 1, ' ');
for (size_t c = 0; c < m.getComponents ().size (); ++c)
{
string s = m.getComponents ()[c];
cout << s << string (componentLength[c] + 1 - s.size (), ' ');
}
cout << endl;
for (IDManifest::ChannelGroupManifest::ConstIterator q = m.begin ();
q != m.end ();
++q)
{
string id = to_string (q.id ());
cout << " " << id << string (maxNumLen + 1 - id.size (), ' ');
for (size_t i = 0; i < q.text ().size (); i++)
{
string s = q.text ()[i];
cout << s << string (componentLength[i] + 1 - s.size (), ' ');
}
cout << '\n';
}
}
return uncompressedSize;
}
void
printManifest (const char fileName[])
{
MultiPartInputFile in (fileName);
//
// extract objectID attribute
//
for (int part = 0; part < in.parts (); part++)
{
if (in.parts () > 1) { cout << fileName << " part " << part << ":\n"; }
if (hasIDManifest (in.header (part)))
{
const Imf::CompressedIDManifest& mfst =
idManifest (in.header (part));
size_t size = dumpManifest (mfst);
cout << "raw text size : " << size << endl;
cout << "uncompressed size: " << mfst._uncompressedDataSize << endl;
cout << "compressed size : " << mfst._compressedDataSize << endl;
}
else { cout << "no manifest found\n"; }
}
}
void
usageMessage (ostream& stream, const char* program_name, bool verbose = false)
{
stream << "Usage: " << program_name << " imagefile [imagefile ...]\n";
if (verbose)
stream
<< "\n"
"Read exr files and print the contents of the embedded manifest.\n"
"\n"
"Options:\n"
" -h, --help print this message\n"
" --version print version information\n"
"\n"
"Report bugs via https://github.com/AcademySoftwareFoundation/openexr/issues or email security@openexr.com\n"
"";
}
int
main (int argc, char* argv[])
{
if (argc < 2)
{
usageMessage (cerr, argv[0], false);
return -1;
}
for (int i = 1; i < argc; ++i)
{
if (!strcmp (argv[i], "-h") || !strcmp (argv[1], "--help"))
{
usageMessage (cout, "exrmanifest", true);
return 0;
}
else if (!strcmp (argv[i], "--version"))
{
const char* libraryVersion = getLibraryVersion ();
cout << "exrmanifest (OpenEXR) " << OPENEXR_VERSION_STRING;
if (strcmp (libraryVersion, OPENEXR_VERSION_STRING))
cout << "(OpenEXR version " << libraryVersion << ")";
cout << " https://openexr.com" << endl;
cout << "Copyright (c) Contributors to the OpenEXR Project" << endl;
cout << "License BSD-3-Clause" << endl;
return 0;
}
}
try
{
for (int i = 1; i < argc; ++i)
printManifest (argv[i]);
}
catch (const exception& e)
{
cerr << argv[0] << ": " << e.what () << endl;
return 1;
}
for (int i = 1; i < argc; ++i) {}
}
+4 -1
View File
@@ -485,7 +485,7 @@ getChromaticities (
if (i > argc - 9)
throw invalid_argument("Expected 8 chromaticity values");
ChromaticitiesAttribute* a = new ChromaticitiesAttribute;
ChromaticitiesAttribute* a = new ChromaticitiesAttribute(Chromaticities());
attrs.push_back (SetAttr (attrName, part, a));
a->value ().red.x = static_cast<float> (strtod (argv[i + 1], 0));
@@ -848,6 +848,9 @@ main (int argc, char** argv)
outPart.copyPixels (inPart);
}
}
for (size_t i=0; i<attrs.size(); i++)
delete attrs[i].attr;
}
catch (const exception& e)
{
+12
View File
@@ -6,6 +6,8 @@
# standalone program linking against an already-installed OpenEXR
# library.
message(STATUS "Building OpenEXR examples")
if("${CMAKE_PROJECT_NAME}" STREQUAL "")
cmake_minimum_required(VERSION 3.12)
project(OpenEXRExamples)
@@ -41,6 +43,16 @@ target_link_libraries(OpenEXRExamples OpenEXR::OpenEXR)
set_target_properties(OpenEXRExamples PROPERTIES
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin"
)
add_executable(deepidexample deepidexample.cpp)
target_link_libraries(deepidexample OpenEXR::OpenEXR)
add_executable(deepidselect deepidselect.cpp)
target_link_libraries(deepidselect OpenEXR::OpenEXR)
if(WIN32 AND BUILD_SHARED_LIBS)
target_compile_definitions(OpenEXRExamples PRIVATE OPENEXR_DLL)
endif()
+689
View File
@@ -0,0 +1,689 @@
//
// SPDX-License-Identifier: BSD-3-Clause
// Copyright (c) Contributors to the OpenEXR Project.
//
//
// example of writing a file with DeepIDs.
// Creates a deep file with a series of circles and 'blobs' (2D Guassian-like objects)
// of different colors and sizes, each with two or three ID channels, and an idmanifest
// to store the respective names.
//
//
#include <ImfChannelList.h>
#include <ImfDeepScanLineOutputFile.h>
#include <ImfDeepFrameBuffer.h>
#include <ImfIDManifest.h>
#include <ImfStandardAttributes.h>
#include <string.h>
#include <algorithm>
#include <iostream>
#include <random>
#include <set>
#include <vector>
using namespace OPENEXR_IMF_NAMESPACE;
using std::cerr;
using std::set;
using std::sort;
using std::vector;
void
printHelp ()
{
cerr
<< "syntax: deepidexample options output.deep.exr\n\n";
cerr << "--multivariate : combine 'material' and 'model' name into a single ID channel, rather than separate channels\n";
cerr << "--64 : use 64 bit hashes in two channels, rather than a single channel 32 bit hash\n";
cerr << "--frame number : specify animation frame number. Animation cycles every 200 frames\n";
cerr << "--objectid : store object ids in a simple stringvector format rather than the idmanifest attribute\n";
cerr << "--size width height : specify image dimensions for output (default 256 256)\n";
cerr << "--count number : number of objects to write (default 100)\n";
}
//
// a 'sample' as written to the deep file, with RGBA, depth, and up to five ID channels
//
struct Rgbaz
{
half r, g, b, a, z;
uint32_t id0, id1, id2, id3, id4;
//
// depth sort (use ID to resolve ties)
//
bool operator<(const Rgbaz& other) const
{
if (z < other.z) { return true; }
if (z > other.z) { return false; }
return id4 < other.id4;
}
};
//
// object model names
//
static const char* shapeNames[] = {"blob", "circle"};
static const char* sizeNames[] = {"small", "medium", "big"};
//
// seven colors, names and RGB values of each
//
static const char* colorNames[] = {
"white", "red", "green", "blue", "cyan", "magenta", "yellow"};
struct Rgbaz colors[] = {
{0.9, 0.9, 0.9},
{0.9, 0.1, 0.1},
{0.1, 0.9, 0.1},
{0.1, 0.1, 0.9},
{0.1, 0.9, 0.9},
{0.9, 0.1, 0.9},
{0.9, 0.9, 0.1}};
int random (std::default_random_engine& generator, int max);
void drawBlob (
std::vector<std::vector<Rgbaz>>& image,
int width,
int height,
float x,
float y,
float z,
int size,
int color,
const uint32_t* ids);
void drawCircle (
std::vector<std::vector<Rgbaz>>& image,
int width,
int height,
float x,
float y,
float z,
int size,
int color,
const uint32_t* ids);
int
main (int argc, char* argv[])
{
char* outputFile = nullptr;
bool hash64 = false;
bool multivariate = false;
bool objectID = false;
int width = 256;
int height = 256;
int count = 100;
int frame = 0;
// parse options
for (int i = 1; i < argc; ++i)
{
if (strncmp (argv[i], "--64", 3) == 0) { hash64 = true; }
else if (strncmp (argv[i], "--multivariate", 3) == 0)
{
multivariate = true;
}
else if (strncmp (argv[i], "--help", 3) == 0)
{
printHelp ();
return 0;
}
else if (strncmp (argv[i], "--size", 3) == 0)
{
if (argc < i + 2)
{
printHelp ();
return 1;
}
width = atoi (argv[i + 1]);
height = atoi (argv[i + 2]);
i += 2;
}
else if (strncmp (argv[i], "--count", 3) == 0)
{
if (argc < i + 1)
{
printHelp ();
return 1;
}
count = atoi (argv[i + 1]);
i += 1;
}
else if (strncmp (argv[i], "--frame", 3) == 0)
{
if (argc < i + 1)
{
printHelp ();
return 1;
}
frame = atoi (argv[i + 1]);
i += 1;
}
else if (strncmp (argv[i], "--objectid", 3) == 0) { objectID = true; }
else if (outputFile == nullptr) { outputFile = argv[i]; }
else
{
printHelp ();
return 1;
}
}
if (outputFile == nullptr)
{
cerr << "error: need to specify output filename\n";
printHelp ();
return 1;
}
if (objectID)
{
if (!multivariate || hash64)
{
cerr
<< "error: --objectid mode only works with --multivariate on and --64 off\n";
return 1;
}
}
//
// initialize manifest object
//
IDManifest::ChannelGroupManifest modelOrMultiManifest;
IDManifest::ChannelGroupManifest materialManifest;
std::vector<std::string> oldObjectID;
if (multivariate)
{
if (hash64)
{
set<string> ids;
ids.insert ("id0");
ids.insert ("id1");
modelOrMultiManifest.setChannels (ids);
modelOrMultiManifest.setEncodingScheme (IDManifest::ID2_SCHEME);
modelOrMultiManifest.setHashScheme (IDManifest::MURMURHASH3_64);
}
else
{
modelOrMultiManifest.setChannel ("id");
modelOrMultiManifest.setEncodingScheme (IDManifest::ID_SCHEME);
modelOrMultiManifest.setHashScheme (IDManifest::MURMURHASH3_32);
}
vector<string> components (2);
components[0] = "model";
components[1] = "material";
modelOrMultiManifest.setComponents (components);
}
else
{
if (hash64)
{
set<string> model;
model.insert ("model.id0");
model.insert ("model.id1");
modelOrMultiManifest.setChannels (model);
modelOrMultiManifest.setEncodingScheme (IDManifest::ID2_SCHEME);
modelOrMultiManifest.setHashScheme (IDManifest::MURMURHASH3_64);
set<string> material;
material.insert ("material.id0");
material.insert ("material.id1");
materialManifest.setChannels (material);
materialManifest.setEncodingScheme (IDManifest::ID2_SCHEME);
materialManifest.setHashScheme (IDManifest::MURMURHASH3_64);
}
else
{
modelOrMultiManifest.setChannel ("modelid");
materialManifest.setChannel ("materialid");
modelOrMultiManifest.setEncodingScheme (IDManifest::ID_SCHEME);
modelOrMultiManifest.setHashScheme (IDManifest::MURMURHASH3_32);
materialManifest.setEncodingScheme (IDManifest::ID_SCHEME);
materialManifest.setHashScheme (IDManifest::MURMURHASH3_32);
}
modelOrMultiManifest.setComponent ("model");
materialManifest.setComponent ("material");
}
modelOrMultiManifest.setLifetime (IDManifest::LIFETIME_STABLE);
materialManifest.setLifetime (IDManifest::LIFETIME_STABLE);
//
// draw image
//
vector<vector<Rgbaz>> deepImage (width * height);
std::default_random_engine generator;
generator.seed (2);
uint32_t ids[5];
//
// animation oscillates between two random positions
// over 100 frames
//
float blend = 0.5 - cos (double (frame) * M_PI / 100.) / 2;
for (int object = 0; object < count; ++object)
{
int shape = random (generator, 1);
int size = random (generator, 2);
int color = random (generator, 6);
//
// generate ID
//
if (multivariate)
{
vector<string> s (2);
s[0] = string (shapeNames[shape]) + "/" + string (sizeNames[size]);
s[1] = colorNames[color];
uint64_t hash = modelOrMultiManifest.insert (s);
ids[0] = hash & 0xFFFFFFFF;
// only needed for 64 bit hash scheme: store most significant 32 bits in ids[1]
ids[1] = hash >> 32;
if (objectID)
{
oldObjectID.push_back (
s[0] + "," + s[1] + "," + std::to_string (ids[0]));
}
}
else
{
uint64_t hash = modelOrMultiManifest.insert (
string (shapeNames[shape]) + "/" + string (sizeNames[size]));
ids[0] = hash & 0xFFFFFFFF;
ids[1] = hash >> 32;
hash = materialManifest.insert (colorNames[color]);
ids[2] = hash & 0xFFFFFFFF;
ids[3] = hash >> 32;
}
ids[4] = object; // particle ID
//
// randomized position, velocity, depth
//
int x1 = random (generator, width);
int y1 = random (generator, height);
int x2 = random (generator, width);
int y2 = random (generator, height);
float z = random (generator, 4096) / 2.f;
float x = blend * x2 + (1.0 - blend) * x1;
float y = blend * y2 + (1.0 - blend) * y1;
if (shape == 0)
{
drawBlob (deepImage, width, height, x, y, z, size, color, ids);
}
else
{
drawCircle (deepImage, width, height, x, y, z, size, color, ids);
}
}
//
// initialize pointers required by OpenEXR API to indicate address of first sample of each channel of each pixel
//
vector<int> sampleCounts (width * height);
vector<char*> ptrR (width * height);
vector<char*> ptrG (width * height);
vector<char*> ptrB (width * height);
vector<char*> ptrA (width * height);
vector<char*> ptrZ (width * height);
vector<char*> ptrID0 (width * height);
vector<char*> ptrID1 (width * height);
vector<char*> ptrID2 (width * height);
vector<char*> ptrID3 (width * height);
vector<char*> ptrID4 (width * height);
for (int i = 0; i < width * height; ++i)
{
sampleCounts[i] = int (deepImage[i].size ());
if (sampleCounts[i] > 0)
{
//
// store samples depth sorted
//
sort (deepImage[i].begin (), deepImage[i].end ());
ptrR[i] = (char*) &deepImage[i][0].r;
ptrG[i] = (char*) &deepImage[i][0].g;
ptrB[i] = (char*) &deepImage[i][0].b;
ptrA[i] = (char*) &deepImage[i][0].a;
ptrZ[i] = (char*) &deepImage[i][0].z;
ptrID0[i] = (char*) &deepImage[i][0].id0;
ptrID1[i] = (char*) &deepImage[i][0].id1;
ptrID2[i] = (char*) &deepImage[i][0].id2;
ptrID3[i] = (char*) &deepImage[i][0].id3;
ptrID4[i] = (char*) &deepImage[i][0].id4;
}
}
// save file
Header h (width, height);
h.compression () = ZIPS_COMPRESSION;
DeepFrameBuffer buf;
buf.insertSampleCountSlice (Slice (
UINT,
(char*) sampleCounts.data (),
sizeof (int),
sizeof (int) * width));
h.channels ().insert ("R", Channel (HALF));
buf.insert (
"R",
DeepSlice (
HALF,
(char*) ptrR.data (),
sizeof (char*),
sizeof (char*) * width,
sizeof (Rgbaz)));
h.channels ().insert ("G", Channel (HALF));
buf.insert (
"G",
DeepSlice (
HALF,
(char*) ptrG.data (),
sizeof (char*),
sizeof (char*) * width,
sizeof (Rgbaz)));
h.channels ().insert ("B", Channel (HALF));
buf.insert (
"B",
DeepSlice (
HALF,
(char*) ptrB.data (),
sizeof (char*),
sizeof (char*) * width,
sizeof (Rgbaz)));
h.channels ().insert ("A", Channel (HALF));
buf.insert (
"A",
DeepSlice (
HALF,
(char*) ptrA.data (),
sizeof (char*),
sizeof (char*) * width,
sizeof (Rgbaz)));
h.channels ().insert ("Z", Channel (HALF));
buf.insert (
"Z",
DeepSlice (
HALF,
(char*) ptrZ.data (),
sizeof (char*),
sizeof (char*) * width,
sizeof (Rgbaz)));
if (multivariate)
{
if (hash64)
{
h.channels ().insert ("id.id0", UINT);
buf.insert (
"id.id0",
DeepSlice (
UINT,
(char*) ptrID0.data (),
sizeof (char*),
sizeof (char*) * width,
sizeof (Rgbaz)));
h.channels ().insert ("id.id1", UINT);
buf.insert (
"id.id1",
DeepSlice (
UINT,
(char*) ptrID1.data (),
sizeof (char*),
sizeof (char*) * width,
sizeof (Rgbaz)));
}
else
{
h.channels ().insert ("id", UINT);
buf.insert (
"id",
DeepSlice (
UINT,
(char*) ptrID0.data (),
sizeof (char*),
sizeof (char*) * width,
sizeof (Rgbaz)));
}
}
else
{
if (hash64)
{
h.channels ().insert ("model.id0", UINT);
buf.insert (
"model.id0",
DeepSlice (
UINT,
(char*) ptrID0.data (),
sizeof (char*),
sizeof (char*) * width,
sizeof (Rgbaz)));
h.channels ().insert ("model.id1", UINT);
buf.insert (
"model.id1",
DeepSlice (
UINT,
(char*) ptrID1.data (),
sizeof (char*),
sizeof (char*) * width,
sizeof (Rgbaz)));
h.channels ().insert ("material.id0", UINT);
buf.insert (
"material.id0",
DeepSlice (
UINT,
(char*) ptrID2.data (),
sizeof (char*),
sizeof (char*) * width,
sizeof (Rgbaz)));
h.channels ().insert ("material.id1", UINT);
buf.insert (
"material.id1",
DeepSlice (
UINT,
(char*) ptrID3.data (),
sizeof (char*),
sizeof (char*) * width,
sizeof (Rgbaz)));
}
else
{
h.channels ().insert ("modelid", UINT);
buf.insert (
"modelid",
DeepSlice (
UINT,
(char*) ptrID0.data (),
sizeof (char*),
sizeof (char*) * width,
sizeof (Rgbaz)));
h.channels ().insert ("materialid", UINT);
buf.insert (
"materialid",
DeepSlice (
UINT,
(char*) ptrID2.data (),
sizeof (char*),
sizeof (char*) * width,
sizeof (Rgbaz)));
}
}
h.channels ().insert ("particleid", UINT);
buf.insert (
"particleid",
DeepSlice (
UINT,
(char*) ptrID4.data (),
sizeof (char*),
sizeof (char*) * width,
sizeof (Rgbaz)));
if (objectID)
{
h.insert ("objectID", StringVectorAttribute (oldObjectID));
}
else
{
IDManifest manifest;
manifest.add (modelOrMultiManifest);
if (!multivariate) { manifest.add (materialManifest); }
IDManifest::ChannelGroupManifest particleManifest;
particleManifest.setChannel ("particleid");
particleManifest.setEncodingScheme (IDManifest::ID_SCHEME);
particleManifest.setHashScheme (IDManifest::NOTHASHED);
particleManifest.setLifetime (IDManifest::LIFETIME_SHOT);
manifest.add (particleManifest);
addIDManifest (h, manifest);
}
//
// samples are depth sorted, and do not overlap
//
addDeepImageState (h, DIS_TIDY);
DeepScanLineOutputFile file (outputFile, h);
file.setFrameBuffer (buf);
file.writePixels (height);
}
int
random (std::default_random_engine& generator, int max)
{
std::uniform_int_distribution<int> dist (0, max);
return dist (generator);
}
void
drawBlob (
std::vector<std::vector<Rgbaz>>& image,
int width,
int height,
float x,
float y,
float z,
int size,
int color,
const uint32_t* ids)
{
//
// draw windowed gaussian centered at (x,y)
//
float sigma = 5 + 40 * size;
int ptr = 0;
Rgbaz point;
point.z = z;
point.id0 = ids[0];
point.id1 = ids[1];
point.id2 = ids[2];
point.id3 = ids[3];
point.id4 = ids[4];
for (int j = 0; j < height; ++j)
{
for (int i = 0; i < width; ++i)
{
float dist_sq = (x - i) * (x - i) + (j - y) * (j - y);
float scale =
exp (-dist_sq / sigma) * (1.f - sqrt (dist_sq) / sigma);
if (scale > 0.001)
{
point.r = colors[color].r * scale;
point.g = colors[color].g * scale;
point.b = colors[color].b * scale;
point.a = scale;
image[ptr].push_back (point);
}
ptr++;
}
}
}
float
getAlpha (float xmin, float ymin, float xmax, float ymax, float size)
{
bool in1 = xmin * xmin + ymin * ymin < size * size;
bool in2 = xmax * xmax + ymin * ymin < size * size;
bool in3 = xmin * xmin + ymax * ymax < size * size;
bool in4 = xmin * xmin + ymax * ymax < size * size;
if (in1 && in2 && in3 && in4) { return (xmax - xmin) * (ymax - ymin); }
if (!in1 && !in2 && !in3 && !in4) { return 0.f; }
if (xmax - xmin < 0.001 && ymax - ymin < 0.001) { return 0.f; }
return getAlpha (xmin, ymin, (xmin + xmax) / 2, (ymin + ymax) / 2, size) +
getAlpha ((xmin + xmax) / 2, ymin, xmax, (ymin + ymax) / 2, size) +
getAlpha (xmin, (ymin + ymax) / 2, (xmin + xmax) / 2, ymax, size) +
getAlpha ((xmin + xmax) / 2, (ymin + ymax) / 2, xmax, ymax, size);
}
void
drawCircle (
std::vector<std::vector<Rgbaz>>& image,
int width,
int height,
float x,
float y,
float z,
int size,
int color,
const uint32_t* ids)
{
int ptr = 0;
float radius = 3 + 8 * size;
Rgbaz point;
point.z = z;
point.id0 = ids[0];
point.id1 = ids[1];
point.id2 = ids[2];
point.id3 = ids[3];
point.id4 = ids[4];
for (int j = 0; j < height; ++j)
{
for (int i = 0; i < width; ++i)
{
float alpha = getAlpha (
i - x - 0.5f, j - y - 0.5f, i - x + 0.5f, j - y + 0.5f, radius);
if (alpha > 0)
{
point.r = colors[color].r * alpha;
point.g = colors[color].g * alpha;
point.b = colors[color].b * alpha;
point.a = alpha;
image[ptr].push_back (point);
}
ptr++;
}
}
}
+540
View File
@@ -0,0 +1,540 @@
//
// SPDX-License-Identifier: BSD-3-Clause
// Copyright (c) Contributors to the OpenEXR Project.
//
//
// example of using an IDManifest to locate given objects in a deep image with IDs
// demonstrates how to use multivariate IDs, and 64 bit IDs spread across two channels
// deepidexample will create images that can be used as input
// (though this tool is intended to support images from other sources)
//
#include <ImfChannelList.h>
#include <ImfMultiPartInputFile.h>
#include <ImfDeepScanLineInputPart.h>
#include <ImfMultiPartOutputFile.h>
#include <ImfDeepScanLineOutputPart.h>
#include <ImfOutputPart.h>
#include <ImfDeepFrameBuffer.h>
#include <ImfIDManifest.h>
#include <ImfStandardAttributes.h>
#include <ImfPartType.h>
#include <iomanip>
#include <list>
#include <map>
#include <vector>
using namespace OPENEXR_IMF_NAMESPACE;
using namespace IMATH_NAMESPACE;
using std::cerr;
using std::dec;
using std::endl;
using std::hex;
using std::list;
using std::map;
using std::set;
using std::stoi;
using std::vector;
struct match
{
int channel1; // index of first channel to look up ID in
uint32_t id1; // first ID
int channel2; // index of second channel, or -1 if only one channel
uint32_t id2; // second ID, ignored channel2==-1
};
//
// setIds parses the matches arguments, and populates lists of matching IDs. If there are '--and' statements in the matches
// ids are entered in a new list
//
void setIds (
const IDManifest& mfst,
list<list<match>>& ids,
const char* matches[],
int numMatches,
const map<string, int>& channelToPos);
int
main (int argc, const char* argv[])
{
if (argc < 4)
{
cerr
<< "syntax: [--mask] input.exr match [match...] [--and match [match...]] output.exr\n"
<< " if --mask specified, writes a shallow EXR with a mask of the selected object(s) in the 'A' channel\n"
<< " otherwise, writes a deep EXR only containing the selected object(s)\n"
<< '\n'
<< " matches can be:\n"
<< " searchstring - match any component of any channel\n"
<< " componentname:searchstring - only match given component\n"
<< " channelname:number - match specified numeric ID in given channel\n"
<< '\n'
<< "\"A B --and C D\" means \"(must match either A or B) and also (must match either C or D)\"\n"
<< " e.g:\n"
<< " input.deep.exr blue output.deep.exr\n"
<< " input.deep.exr material:blue --and model:blob output.deep.exr\n"
<< " input.deep.exr material:blue material:red --and model:blob output.deep.exr\n"
<< " input.deep.exr particleid:3 output.deep.exr\n";
return 1;
}
bool mask = false;
int numMatchArguments = argc - 2;
const char** matchArguments = argv + 2;
const char* inputFile = argv[1];
if (strcmp (argv[1], "--mask") == 0)
{
mask = true;
numMatchArguments--;
matchArguments++;
inputFile = argv[2];
}
MultiPartInputFile input (inputFile);
if (!hasIDManifest (input.header (0)))
{
cerr << "deepidselect requires an ID manifest in the EXR header\n";
return 1;
}
for (int i = 0; i < input.parts (); ++i)
{
if (input.header (i).type () != DEEPSCANLINE)
{
cerr
<< "deepidselect currently only supports files which are entirely deep scanline files\n";
return 1;
}
}
//
// build output headers. For deep output, this is easy: just copy them over
// for masks, build scanline images of the same dimensions, each with a single alpha channel
//
vector<Header> hdrs (input.parts ());
if (mask)
{
for (int h = 0; h < input.parts (); ++h)
{
const Header& inHdr = input.header (h);
hdrs[h].dataWindow () = inHdr.dataWindow ();
hdrs[h].setType (SCANLINEIMAGE);
hdrs[h].displayWindow () = inHdr.displayWindow ();
if (inHdr.hasView ()) { hdrs[h].setView (inHdr.view ()); }
if (inHdr.hasName ()) { hdrs[h].setName (inHdr.name ()); }
hdrs[h].channels ().insert ("A", Channel (HALF));
}
}
else
{
for (int h = 0; h < input.parts (); ++h)
{
hdrs[h] = input.header (h);
}
}
MultiPartOutputFile output (argv[argc - 1], hdrs.data (), input.parts ());
// process each part individually
for (int pt = 0; pt < input.parts (); ++pt)
{
const Header& inputHeader = input.header (pt);
const ChannelList& inputChans = inputHeader.channels ();
Box2i dataWindow = inputHeader.dataWindow ();
int width = dataWindow.max.x + 1 - dataWindow.min.x;
map<string, int>
channelToPos; // index of each channel as stored in scanLine object
int alphaChannel = -1;
PixelType alphaChannelType = HALF;
int channels = 0;
for (ChannelList::ConstIterator i = inputChans.begin ();
i != inputChans.end ();
++i)
{
channelToPos[i.name ()] = channels;
if (strcmp (i.name (), "A") == 0)
{
alphaChannel = channels;
alphaChannelType = i.channel ().type;
}
channels++;
}
// if the part has a manifest, use this part's manifest
// otherwise use part 0's manifest.
// (but reparse the manifest for every part in case the channel list has changed)
//
int manifestPart = hasIDManifest (inputHeader) ? pt : 0;
list<list<match>> ids;
IDManifest mfst = idManifest (input.header (manifestPart));
setIds (mfst, ids, matchArguments, numMatchArguments, channelToPos);
// store for an individual deep scanline. Accessed using scanLine[channelIndex][pixelIndex][sampleIndex]
// where pixelIndex is 0 for the leftmost pixel (even if the dataWindow doesn't start at 0)
vector<vector<vector<uint32_t>>> scanLine (channels);
// pointers to the data in each channel for FrameBuffer
vector<vector<uint32_t*>> scanPointers (channels);
for (int i = 0; i < channels; ++i)
{
scanLine[i].resize (width);
scanPointers[i].resize (width);
}
vector<int> pixelCounts (width);
vector<half> outputAlpha (
width); // only required for --mask mode: stores output
DeepFrameBuffer buf;
buf.insertSampleCountSlice (Slice (
UINT,
(char*) pixelCounts.data () - dataWindow.min.x,
sizeof (int),
0));
int c = 0;
//
// read all channels as their native type, so they round trip when writing deep
//
for (ChannelList::ConstIterator i = inputChans.begin ();
i != inputChans.end ();
++i, ++c)
{
buf.insert (
i.name (),
DeepSlice (
i.channel ().type,
(char*) scanPointers[c].data () - dataWindow.min.x,
sizeof (char*),
0,
sizeof (int32_t)));
}
DeepScanLineInputPart inPart (input, pt);
inPart.setFrameBuffer (buf);
//
// for mask, create an alpha channel and initialize a FrameBuffer with that data
// otherwise, can use the deep frame buffer for both input and output, since the data is processed in-place
//
if (mask)
{
FrameBuffer outBuf;
outBuf.insert (
"A",
Slice (
HALF,
(char*) outputAlpha.data () - dataWindow.min.x,
sizeof (half),
0));
OutputPart outPart (output, pt);
outPart.setFrameBuffer (outBuf);
}
else
{
DeepScanLineOutputPart outPart (output, pt);
outPart.setFrameBuffer (buf);
}
for (int y = dataWindow.min.y; y <= dataWindow.max.y; ++y)
{
inPart.readPixelSampleCounts (y);
for (int c = 0; c < channels; ++c)
{
for (int x = 0; x < width; ++x)
{
scanLine[c][x].resize (pixelCounts[x]);
scanPointers[c][x] = scanLine[c][x].data ();
}
}
inPart.readPixels (y);
for (int x = 0; x < width; ++x)
{
int outputSample = 0;
float totalAlpha = 0.f;
float maskAlpha = 0.f;
for (int s = 0; s < pixelCounts[x]; ++s)
{
//
// should sample s be retained?
// look for an entry in each 'and group' where all the required channels match their
// corresponding values
//
bool good = true;
for (list<list<match>>::const_iterator idGroup =
ids.begin ();
idGroup != ids.end () && good;
++idGroup)
{
good = false;
for (list<match>::const_iterator i = idGroup->begin ();
i != idGroup->end () && !good;
++i)
{
if (scanLine[i->channel1][x][s] == i->id1 &&
(i->channel2 == -1 ||
scanLine[i->channel2][x][s] == i->id2))
{
good = true;
}
}
}
//
// deep output mode:
// delete unwanted samples
// mask output node:
// composite together wanted sample's alpha
//
if (mask)
{
//cast alpha to float, and composite
float alpha = 0.f;
switch (alphaChannelType)
{
case FLOAT:
alpha = *(float*) (&scanLine[alphaChannel][x]
.data ()[s]);
break;
case HALF:
alpha = *(half*) (&scanLine[alphaChannel][x]
.data ()[s]);
break;
case UINT:
alpha = scanLine[alphaChannel][x][s];
break; //wat! this is a weird thing to do,but whatever...
case NUM_PIXELTYPES: break;
}
if (good) { maskAlpha += (1.0 - totalAlpha) * alpha; }
totalAlpha += (1.0 - totalAlpha) * alpha;
}
else if (good)
{
// keep Sample: copy from original position into output position
// (so overwrite any samples that are to be deleted)
for (int c = 0; c < channels; ++c)
{
scanLine[c][x][outputSample] = scanLine[c][x][s];
}
outputSample++;
}
}
if (mask)
{
if (totalAlpha > 0.f) { maskAlpha /= totalAlpha; }
outputAlpha[x] = maskAlpha;
}
else
{
// update total count of samples
pixelCounts[x] = outputSample;
}
}
//
// write data out
//
if (mask)
{
OutputPart outPart (output, pt);
outPart.writePixels (1);
}
else
{
DeepScanLineOutputPart outPart (output, pt);
outPart.writePixels (1);
}
}
}
}
void
setIds (
const IDManifest& mfst,
list<list<match>>& ids,
const char* matches[],
int numMatches,
const map<string, int>& channelToPos)
{
//
// initially one single list
//
ids.clear ();
ids.push_back (list<match> ());
//
// check each manifest, each entry, against each matching expression
//
for (int c = 0; c < numMatches; ++c)
{
//
// an 'and' argument means proceed to the next group of ids
// must find a match in every such group
//
if (strcmp (matches[c], "--and") == 0)
{
ids.push_back (list<match> ());
continue;
}
string matchString (matches[c]);
//
// handle strings of the form component:searchstring
// and channel:idnumber
//
string componentName;
string::size_type pos = matchString.find (':');
if (pos != string::npos)
{
componentName = matchString.substr (0, pos);
matchString = matchString.substr (pos + 1);
}
if (matchString.find_first_not_of ("0123456789") == string::npos)
{
map<string, int>::const_iterator chan =
channelToPos.find (componentName);
if (chan != channelToPos.end ())
{
match m;
m.channel1 = chan->second;
m.id1 = stoi (matchString);
m.channel2 = -1;
ids.back ().push_back (m);
}
continue; // skip parsing the manifests for this string
}
// check the manifest for each group of channels
for (size_t i = 0; i < mfst.size (); ++i)
{
for (IDManifest::ChannelGroupManifest::ConstIterator it =
mfst[i].begin ();
it != mfst[i].end ();
++it)
{
for (size_t stringIndex = 0; stringIndex < it.text ().size ();
++stringIndex)
{
if (componentName == "" ||
mfst[i].getComponents ()[stringIndex] == componentName)
{
// simple substring matching only: could do wildcards or regexes here instead
if (it.text ()[stringIndex].find (matchString) !=
string::npos)
{
// a match is found - add it to the corresponding channels
if (mfst[i].getEncodingScheme () ==
IDManifest::ID_SCHEME)
{
// simple scheme: the ID channel has to match
for (const string& s: mfst[i].getChannels ())
{
map<string, int>::const_iterator chan =
channelToPos.find (s);
if (chan != channelToPos.end ())
{
//could support matching the ID against a specific channel
//that check would happen here
match m;
m.channel1 = chan->second;
m.id1 = uint32_t (it.id ());
m.channel2 = -1;
ids.back ().push_back (m);
cerr << "adding match " << hex
<< it.id () << dec
<< " for string "
<< it.text ()[stringIndex]
<< " in channel " << chan->second
<< '(' << chan->first << ")\n";
}
}
}
else if (
mfst[i].getEncodingScheme () ==
IDManifest::ID2_SCHEME)
{
// 64 bit IDs are spread across two channels, with the least significant bits
// in the first channel (alphabetically) and the most significant bits in the second
// so process the channel set in pairs
set<string>::const_iterator chanLow =
mfst[i].getChannels ().begin ();
set<string>::const_iterator end =
mfst[i].getChannels ().end ();
while (chanLow != end)
{
set<string>::const_iterator chanHigh =
chanLow;
++chanHigh;
if (chanHigh != end)
{
map<string, int>::const_iterator
chanIdxLow =
channelToPos.find (*chanLow);
map<string, int>::const_iterator
chanIdxHigh =
channelToPos.find (*chanHigh);
if (chanIdxLow != channelToPos.end () &&
chanIdxHigh != channelToPos.end ())
{
// to match against specific channels, check at least one channel matches here
match m;
m.channel1 = chanIdxLow->second;
m.id1 = it.id () & 0xFFFFFFFF;
m.channel2 = chanIdxHigh->second;
m.id2 = it.id () >> 32;
ids.back ().push_back (m);
cerr << "adding match " << hex
<< it.id () << dec
<< " for string "
<< it.text ()[stringIndex]
<< ": " << hex << m.id1
<< " in channel " << m.channel1
<< '(' << chanIdxLow->first
<< "), " << hex << m.id2
<< " in channel " << m.channel2
<< '(' << chanIdxHigh->first
<< ")\n";
}
++chanLow;
if (chanLow != end) { ++chanLow; }
}
}
}
}
}
}
}
}
}
}
+22 -6
View File
@@ -96,7 +96,7 @@ void combineFiles ()
const char * filenames[] = {"gz1.exr", "tiledgz1.exr", "test.deep.exr", "testTiled.deep.exr"};
for (int i = 0; i < sizeof (filenames) / sizeof (filenames[0]); i++)
for (size_t i = 0; i < sizeof (filenames) / sizeof (filenames[0]); i++)
{
MultiPartInputFile * in_file = new MultiPartInputFile (filenames[i]);
Header header = in_file->header (0);
@@ -112,7 +112,7 @@ void combineFiles ()
MultiPartOutputFile outputFile ("multipart.exr", headers.data (), (int)headers.size ());
for (int i = 0; i < sizeof (filenames) / sizeof (filenames[0]); i++)
for (size_t i = 0; i < sizeof (filenames) / sizeof (filenames[0]); i++)
{
Header & header = headers[i];
const string& type = header.type ();
@@ -314,6 +314,18 @@ void resizeDeepBuffers (
}
}
template <typename T>
void freeDeepBuffers (list<Array2D<T *>> & channels)
{
for (auto i = channels.begin (); i != channels.end (); i++)
{
Array2D<T*> & channel = *i;
for (int y = 0; y < channel.height (); y++)
for (int x = 0; x < channel.width (); x++)
delete[] channel[y][x];
}
}
template <typename T>
void modifyChannels(list<Array2D<T>> & channels, T delta)
{
@@ -351,8 +363,8 @@ void modifyDeepChannels(Array2D<uint32_t> & sampleCount, list<Array2D<T *>> & ch
for (int x = 0; x < channel.width (); x++)
{
uint32_t count = sampleCount[y][x];
for (int j = 0; j < count; j++)
channel[y][x][j] += delta;
for (uint32_t j = 0; j < count; j++)
channel[y][x][j] += delta;
}
}
}
@@ -414,7 +426,7 @@ void modifyMultipart ()
Box2i dataWindow = header.dataWindow ();
OutputPart outputPart (outputFile, i);
outputPart.setFrameBuffer (frameBuffer);
outputPart.writePixels (header.dataWindow ().max.y - header.dataWindow ().min.y + 1);
outputPart.writePixels (dataWindow.max.y - dataWindow.min.y + 1);
}
else
{
@@ -466,7 +478,7 @@ void modifyMultipart ()
Box2i dataWindow = header.dataWindow ();
DeepScanLineOutputPart outputPart (outputFile, i);
outputPart.setFrameBuffer (frameBuffer);
outputPart.writePixels (header.dataWindow ().max.y - header.dataWindow ().min.y + 1);
outputPart.writePixels (dataWindow.max.y - dataWindow.min.y + 1);
}
else
{
@@ -474,6 +486,10 @@ void modifyMultipart ()
outputPart.setFrameBuffer (frameBuffer);
outputPart.writeTiles (0, outputPart.numXTiles () - 1, 0, outputPart.numYTiles () - 1);
}
freeDeepBuffers(intChannels);
freeDeepBuffers(halfChannels);
freeDeepBuffers(floatChannels);
}
}
}
+2
View File
@@ -1,6 +1,8 @@
# SPDX-License-Identifier: BSD-3-Clause
# Copyright (c) Contributors to the OpenEXR Project.
message(STATUS "Building OpenEXR libraries")
add_subdirectory( Iex )
add_subdirectory( IlmThread )
+1 -1
View File
@@ -24,7 +24,7 @@ class OptimizationMode
public:
bool _optimizable;
int _ySampling;
OptimizationMode () : _optimizable (false) {}
OptimizationMode () : _optimizable (false), _ySampling (0) {}
};
# ifdef IMF_HAVE_SSE2
+44 -15
View File
@@ -454,10 +454,12 @@ reconstruct_chunk_table (
uint64_t* chunktable)
{
exr_result_t rv = EXR_ERR_SUCCESS;
exr_result_t firstfailrv = EXR_ERR_SUCCESS;
uint64_t offset_start, chunk_start, max_offset;
uint64_t* curctable;
const struct _internal_exr_part* curpart = NULL;
int found_ci, computed_ci, partnum = 0;
size_t chunkbytes;
curpart = ctxt->parts[ctxt->num_parts - 1];
offset_start = curpart->chunk_table_offset;
@@ -492,6 +494,11 @@ reconstruct_chunk_table (
if (rv != EXR_ERR_SUCCESS) return rv;
}
chunkbytes = (size_t)part->chunk_count * sizeof(uint64_t);
curctable = (uint64_t*) ctxt->alloc_fn (chunkbytes);
if (!curctable) return EXR_ERR_OUT_OF_MEMORY;
memset (curctable, 0, chunkbytes);
for (int ci = 0; ci < part->chunk_count; ++ci)
{
if (chunktable[ci] >= offset_start && chunktable[ci] < max_offset)
@@ -503,21 +510,37 @@ reconstruct_chunk_table (
if (part->lineorder == EXR_LINEORDER_DECREASING_Y)
computed_ci = part->chunk_count - (ci + 1);
found_ci = computed_ci;
rv = read_and_validate_chunk_leader (
ctxt, part, partnum, chunk_start, &found_ci, &offset_start);
if (rv != EXR_ERR_SUCCESS) return rv;
if (rv != EXR_ERR_SUCCESS)
{
chunk_start = 0;
if (firstfailrv == EXR_ERR_SUCCESS) firstfailrv = rv;
}
// scanlines can be more strict about the ordering
if (part->storage_mode == EXR_STORAGE_SCANLINE ||
part->storage_mode == EXR_STORAGE_DEEP_SCANLINE)
{
if (computed_ci != found_ci) return EXR_ERR_BAD_CHUNK_LEADER;
if (computed_ci != found_ci)
{
chunk_start = 0;
if (firstfailrv == EXR_ERR_SUCCESS)
firstfailrv = EXR_ERR_BAD_CHUNK_LEADER;
}
}
chunktable[found_ci] = chunk_start;
if (found_ci >= 0 && found_ci < part->chunk_count)
{
if (curctable[found_ci] == 0)
curctable[found_ci] = chunk_start;
}
}
memcpy (chunktable, curctable, chunkbytes);
ctxt->free_fn (curctable);
return rv;
return firstfailrv;
}
static exr_result_t
@@ -566,10 +589,9 @@ extract_chunk_table (
if (rv != EXR_ERR_SUCCESS)
{
ctxt->free_fn (ctable);
return rv;
ctable = (uint64_t *) UINTPTR_MAX;
}
if (!ctxt->disable_chunk_reconstruct)
else if (!ctxt->disable_chunk_reconstruct)
{
// could convert table all at once, but need to check if the
// file is incomplete (i.e. crashed during write and didn't
@@ -590,13 +612,19 @@ extract_chunk_table (
// something similar, except when in strict mode, we
// will fail with a corrupt chunk immediately.
rv = reconstruct_chunk_table (ctxt, part, ctable);
if (rv != EXR_ERR_SUCCESS && ctxt->strict_header)
if (rv != EXR_ERR_SUCCESS)
{
ctxt->free_fn (ctable);
return ctxt->report_error (
ctxt,
EXR_ERR_BAD_CHUNK_LEADER,
"Incomplete / corrupt chunk table, unable to reconstruct");
if (ctxt->strict_header)
{
ctxt->free_fn (ctable);
ctable = (uint64_t *) UINTPTR_MAX;
rv = ctxt->report_error (
ctxt,
EXR_ERR_BAD_CHUNK_LEADER,
"Incomplete / corrupt chunk table, unable to reconstruct");
}
else
rv = EXR_ERR_SUCCESS;
}
}
}
@@ -609,7 +637,8 @@ extract_chunk_table (
&eptr,
nptr))
{
ctxt->free_fn (ctable);
if (nptr != UINTPTR_MAX)
ctxt->free_fn (ctable);
ctable = (uint64_t*) eptr;
if (ctable == NULL)
return ctxt->standard_error (ctxt, EXR_ERR_OUT_OF_MEMORY);
@@ -617,7 +646,7 @@ extract_chunk_table (
}
*chunktable = ctable;
return EXR_ERR_SUCCESS;
return ((uintptr_t)ctable) == UINTPTR_MAX ? EXR_ERR_BAD_CHUNK_LEADER : EXR_ERR_SUCCESS;
}
/**************************************/
+67 -28
View File
@@ -288,6 +288,7 @@ default_decompress_chunk (exr_decode_pipeline_t* decode)
uint64_t sampsize =
(((uint64_t) decode->chunk.width) *
((uint64_t) decode->chunk.height));
sampsize *= sizeof (int32_t);
rv = decompress_data (
@@ -340,7 +341,7 @@ unpack_sample_table (
exr_result_t rv = EXR_ERR_SUCCESS;
int32_t w = decode->chunk.width;
int32_t h = decode->chunk.height;
int32_t totsamp = 0;
uint64_t totsamp = 0;
int32_t* samptable = decode->sample_count_table;
size_t combSampSize = 0;
@@ -351,42 +352,52 @@ unpack_sample_table (
{
for (int32_t y = 0; y < h; ++y)
{
int32_t *cursampline = samptable + y * w;
int32_t prevsamp = 0;
for (int32_t x = 0; x < w; ++x)
{
int32_t nsamps =
(int32_t) one_to_native32 ((uint32_t) samptable[y * w + x]);
if (nsamps < 0) return EXR_ERR_INVALID_SAMPLE_DATA;
samptable[y * w + x] = nsamps - prevsamp;
prevsamp = nsamps;
(int32_t) one_to_native32 ((uint32_t) cursampline[x]);
// not monotonic, violation
if (nsamps < prevsamp) return EXR_ERR_INVALID_SAMPLE_DATA;
cursampline[x] = nsamps - prevsamp;
prevsamp = nsamps;
}
totsamp += prevsamp;
totsamp += (uint64_t)prevsamp;
}
samptable[w * h] = totsamp;
if (totsamp >= (uint64_t)INT32_MAX)
return EXR_ERR_INVALID_SAMPLE_DATA;
samptable[w * h] = (int32_t)totsamp;
}
else
{
for (int32_t y = 0; y < h; ++y)
{
int32_t *cursampline = samptable + y * w;
int32_t prevsamp = 0;
for (int32_t x = 0; x < w; ++x)
{
int32_t nsamps =
(int32_t) one_to_native32 ((uint32_t) samptable[y * w + x]);
if (nsamps < 0) return EXR_ERR_INVALID_SAMPLE_DATA;
samptable[y * w + x] = nsamps;
prevsamp = nsamps;
(int32_t) one_to_native32 ((uint32_t) cursampline[x]);
// not monotonic, violation
if (nsamps < prevsamp) return EXR_ERR_INVALID_SAMPLE_DATA;
cursampline[x] = nsamps;
prevsamp = nsamps;
}
totsamp += prevsamp;
totsamp += (uint64_t)prevsamp;
}
if (totsamp >= (uint64_t)INT32_MAX)
return EXR_ERR_INVALID_SAMPLE_DATA;
}
if (totsamp < 0 ||
(((uint64_t) totsamp) * combSampSize) > decode->chunk.unpacked_size)
if ((totsamp * combSampSize) > decode->chunk.unpacked_size)
{
rv = pctxt->report_error (
pctxt, EXR_ERR_INVALID_SAMPLE_DATA, "Corrupt sample count table");
}
return rv;
}
@@ -657,14 +668,34 @@ exr_decoding_run (
(part->storage_mode == EXR_STORAGE_DEEP_SCANLINE ||
part->storage_mode == EXR_STORAGE_DEEP_TILED))
{
if (part->comp_type == EXR_COMPRESSION_NONE &&
decode->sample_count_table != decode->packed_sample_count_table)
{
/* happens when we're requested to pack to 'individual' mode */
if (decode->sample_count_alloc_size < decode->chunk.sample_count_table_size)
return EXR_ERR_OUT_OF_MEMORY;
if (decode->chunk.sample_count_table_size > 0)
{
memcpy (decode->sample_count_table,
decode->packed_sample_count_table,
decode->chunk.sample_count_table_size);
}
else
{
memset (decode->sample_count_table, 0, decode->sample_count_alloc_size);
}
}
rv = unpack_sample_table (pctxt, decode);
if ((decode->decode_flags & EXR_DECODE_SAMPLE_DATA_ONLY)) return rv;
}
if (rv != EXR_ERR_SUCCESS)
return pctxt->report_error (
pctxt, rv, "Decode pipeline unable to unpack deep sample table");
if (rv != EXR_ERR_SUCCESS)
return pctxt->report_error (
pctxt,
rv,
"Decode pipeline unable to unpack deep sample table");
}
if (rv == EXR_ERR_SUCCESS && decode->realloc_nonimage_data_fn)
rv = decode->realloc_nonimage_data_fn (decode);
@@ -674,11 +705,14 @@ exr_decoding_run (
rv,
"Decode pipeline unable to realloc deep sample table info");
if (rv == EXR_ERR_SUCCESS && decode->unpack_and_convert_fn)
rv = decode->unpack_and_convert_fn (decode);
if (rv != EXR_ERR_SUCCESS)
return pctxt->report_error (
pctxt, rv, "Decode pipeline unable to unpack and convert data");
if (decode->chunk.unpacked_size > 0)
{
if (rv == EXR_ERR_SUCCESS && decode->unpack_and_convert_fn)
rv = decode->unpack_and_convert_fn (decode);
if (rv != EXR_ERR_SUCCESS)
return pctxt->report_error (
pctxt, rv, "Decode pipeline unable to unpack and convert data");
}
return rv;
}
@@ -699,6 +733,10 @@ exr_decoding_destroy (exr_const_context_t ctxt, exr_decode_pipeline_t* decode)
decode->unpacked_alloc_size == 0)
decode->unpacked_buffer = NULL;
if (decode->sample_count_table == decode->packed_sample_count_table &&
decode->sample_count_alloc_size == 0)
decode->sample_count_table = NULL;
internal_decode_free_buffer (
decode,
EXR_TRANSCODE_BUFFER_PACKED,
@@ -719,16 +757,17 @@ exr_decoding_destroy (exr_const_context_t ctxt, exr_decode_pipeline_t* decode)
EXR_TRANSCODE_BUFFER_SCRATCH2,
&(decode->scratch_buffer_2),
&(decode->scratch_alloc_size_2));
internal_decode_free_buffer (
decode,
EXR_TRANSCODE_BUFFER_PACKED_SAMPLES,
&(decode->packed_sample_count_table),
&(decode->packed_sample_count_alloc_size));
internal_decode_free_buffer (
decode,
EXR_TRANSCODE_BUFFER_SAMPLES,
(void**) &(decode->sample_count_table),
&(decode->sample_count_alloc_size));
internal_decode_free_buffer (
decode,
EXR_TRANSCODE_BUFFER_PACKED_SAMPLES,
&(decode->packed_sample_count_table),
&(decode->packed_sample_count_alloc_size));
*decode = nil;
}
return EXR_ERR_SUCCESS;
+1 -1
View File
@@ -31,7 +31,7 @@ default_compress_chunk (exr_encode_pipeline_t* encode)
return pctxt->print_error (
pctxt,
rv,
"error allocating buffer %lu",
"error allocating buffer %zu",
exr_compress_max_buffer_size (encode->packed_bytes));
//return rv;
+2 -2
View File
@@ -15,7 +15,7 @@
#endif
#if OPENEXR_ENABLE_X86_SIMD_CHECK
# if defined(_MSC_VER) && defined(_WIN32)
# if defined(_WIN32)
# include <intrin.h>
# else
# include <cpuid.h>
@@ -51,7 +51,7 @@ check_for_x86_simd (int* f16c, int* avx, int* sse2)
#elif OPENEXR_ENABLE_X86_SIMD_CHECK
# if defined(_MSC_VER) && defined(_WIN32)
# if defined(_WIN32)
int regs[4]={0}, osxsave;
__cpuid (regs, 0);
+21 -18
View File
@@ -251,20 +251,11 @@ DwaCompressor_compress (DwaCompressor* me)
uint8_t* outDataPtr;
uint8_t* inDataPtr;
// Starting with 2, we write the channel
// Starting with DWA v2, we write the channel
// classification rules into the file
if (fileVersion < 2)
{
me->_channelRules = sLegacyChannelRules;
me->_channelRuleCount =
sizeof (sLegacyChannelRules) / sizeof (Classifier);
}
else
{
me->_channelRules = sDefaultChannelRules;
me->_channelRuleCount =
sizeof (sDefaultChannelRules) / sizeof (Classifier);
}
me->_channelRules = sDefaultChannelRules;
me->_channelRuleCount =
sizeof (sDefaultChannelRules) / sizeof (Classifier);
rv = DwaCompressor_initializeBuffers (me, &outBufferSize);
@@ -1047,6 +1038,7 @@ DwaCompressor_uncompress (
me->alloc_fn, me->free_fn, &(cd->_dctData), outBufferEnd);
if (rv != EXR_ERR_SUCCESS) return rv;
cd->_dctData._type = chan->data_type;
outBufferEnd += chan->width * chan->bytes_per_element;
}
}
@@ -1612,7 +1604,7 @@ DwaCompressor_classifyChannels (DwaCompressor* me)
prefixMap,
me->_numChannels,
curc->channel_name,
(size_t) (curc->channel_name - suffix));
(size_t) (suffix - curc->channel_name));
for (size_t i = 0; i < me->_channelRuleCount; ++i)
{
@@ -1707,12 +1699,23 @@ DwaCompressor_setupChannelData (DwaCompressor* me)
cd->planarUncRle[0] = cd->planarUncBuffer;
cd->planarUncRleEnd[0] = cd->planarUncRle[0];
for (int byte = 1; byte < curc->bytes_per_element; ++byte)
if (!cd->planarUncBuffer)
{
cd->planarUncRle[byte] =
cd->planarUncRle[byte - 1] + curc->width * curc->height;
for (int byte = 1; byte < curc->bytes_per_element; ++byte)
{
cd->planarUncRle[byte] = 0;
cd->planarUncRleEnd[byte] = 0;
}
}
else
{
for (int byte = 1; byte < curc->bytes_per_element; ++byte)
{
cd->planarUncRle[byte] =
cd->planarUncRle[byte - 1] + curc->width * curc->height;
cd->planarUncRleEnd[byte] = cd->planarUncRle[byte];
cd->planarUncRleEnd[byte] = cd->planarUncRle[byte];
}
}
cd->planarUncType = (exr_pixel_type_t) curc->data_type;
+13 -3
View File
@@ -1007,13 +1007,23 @@ readUInt (const uint8_t* b)
#ifdef __APPLE__
# include <libkern/OSByteOrder.h>
# define READ64(c) OSSwapInt64 (*(const uint64_t*) (c))
# define SWAP64(c) OSSwapInt64 (c)
#elif defined(linux)
# include <byteswap.h>
# define READ64(c) bswap_64 (*(const uint64_t*) (c))
# define SWAP64(c) bswap_64 (c)
#elif defined(_MSC_VER)
# include <stdlib.h>
# define READ64(c) _byteswap_uint64 (*(const uint64_t*) (c))
# define SWAP64(c) _byteswap_uint64 (c)
#endif
#ifdef SWAP64
static inline uint64_t READ64(const uint8_t *src)
{
uint64_t v;
// unaligned reads are UB
memcpy (&v, src, sizeof(uint64_t));
return SWAP64 (v);
}
#else
# define READ64(c) \
((uint64_t) (c)[0] << 56) | ((uint64_t) (c)[1] << 48) | \
+1 -1
View File
@@ -174,7 +174,7 @@ internal_exr_destroy_part (
ctable = (uint64_t*) atomic_load (&(cur->chunk_table));
atomic_store (&(cur->chunk_table), (uintptr_t) (0));
#endif
if (ctable) dofree (ctable);
if (ctable && ((uintptr_t)ctable) != UINTPTR_MAX) dofree (ctable);
}
/**************************************/
+1 -1
View File
@@ -10,6 +10,6 @@
# define OPENEXR_VERSION_MAJOR 3
# define OPENEXR_VERSION_MINOR 2
# define OPENEXR_VERSION_PATCH 0
# define OPENEXR_VERSION_PATCH 4
#endif
+5
View File
@@ -195,6 +195,9 @@ exr_attr_string_set_with_length (
#ifdef _MSC_VER
# pragma warning(push)
# pragma warning(disable : 4996)
#elif __MSVCRT__ && __GNUC__
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wstringop-truncation"
#endif
if (d)
strncpy (sstr, d, (size_t) len);
@@ -202,6 +205,8 @@ exr_attr_string_set_with_length (
memset (sstr, 0, (size_t) len);
#ifdef _MSC_VER
# pragma warning(pop)
#elif __MSVCRT__ && __GNUC__
#pragma GCC diagnostic pop
#endif
}
sstr[len] = '\0';
+6 -3
View File
@@ -1196,9 +1196,10 @@ generic_unpack_deep_pointers (exr_decode_pipeline_t* decode)
if (outpix)
{
uint8_t* cdata = outpix;
UNPACK_SAMPLES (samps)
}
srcbuffer += bpc * samps;
srcbuffer += ((size_t) bpc) * ((size_t) samps);
}
}
sampbuffer += w;
@@ -1242,12 +1243,14 @@ generic_unpack_deep (exr_decode_pipeline_t* decode)
}
else
prevsamps = sampbuffer[w - 1];
srcbuffer += ((size_t) bpc) * ((size_t) prevsamps);
if (incr_tot) totsamps += (size_t) prevsamps;
continue;
}
cdata += totsamps * ((size_t) ubpc);
for (int x = 0; x < w; ++x)
@@ -1263,7 +1266,7 @@ generic_unpack_deep (exr_decode_pipeline_t* decode)
UNPACK_SAMPLES (samps)
srcbuffer += bpc * samps;
srcbuffer += ((size_t) bpc) * ((size_t) samps);
if (incr_tot) totsamps += (size_t) samps;
}
}
@@ -1301,7 +1304,7 @@ internal_exr_match_decode (
if (isdeep)
{
if ((decode->decode_flags & EXR_DECODE_SAMPLE_COUNTS_AS_INDIVIDUAL))
if ((decode->decode_flags & EXR_DECODE_NON_IMAGE_DATA_AS_POINTERS))
return &generic_unpack_deep_pointers;
return &generic_unpack_deep;
}
+175 -32
View File
@@ -1200,13 +1200,92 @@ runChecks (T& source, bool reduceMemory, bool reduceTime)
return threw;
}
// This is not entirely needed in that the chunk info has the
// total unpacked_size field which can be used for allocation
// but this adds an additional point to use when debugging issues.
static exr_result_t
realloc_deepdata(exr_decode_pipeline_t* decode)
{
int32_t w = decode->chunk.width;
int32_t h = decode->chunk.height;
uint64_t totsamps = 0, bytes = 0;
const int32_t *sampbuffer = decode->sample_count_table;
std::vector<uint8_t>* ud = static_cast<std::vector<uint8_t>*>(
decode->decoding_user_data);
if ( ! ud )
{
for (int c = 0; c < decode->channel_count; c++)
{
exr_coding_channel_info_t& outc = decode->channels[c];
outc.decode_to_ptr = NULL;
outc.user_pixel_stride = outc.user_bytes_per_element;
outc.user_line_stride = 0;
}
return EXR_ERR_SUCCESS;
}
if ((decode->decode_flags &
EXR_DECODE_SAMPLE_COUNTS_AS_INDIVIDUAL))
{
for (int32_t y = 0; y < h; ++y)
{
for (int x = 0; x < w; ++x)
totsamps += sampbuffer[x];
sampbuffer += w;
}
}
else
{
for (int32_t y = 0; y < h; ++y)
totsamps += sampbuffer[y*w + w - 1];
}
for (int c = 0; c < decode->channel_count; c++)
{
exr_coding_channel_info_t& outc = decode->channels[c];
bytes += totsamps * outc.user_bytes_per_element;
}
if (bytes == 0 || bytes >= gMaxBytesPerDeepScanline)
{
for (int c = 0; c < decode->channel_count; c++)
{
exr_coding_channel_info_t& outc = decode->channels[c];
outc.decode_to_ptr = NULL;
outc.user_pixel_stride = outc.user_bytes_per_element;
outc.user_line_stride = 0;
}
return EXR_ERR_SUCCESS;
}
if (ud->size () < bytes)
{
ud->resize (bytes);
if (ud->capacity() < bytes)
return EXR_ERR_OUT_OF_MEMORY;
}
uint8_t* dptr = &((*ud)[0]);
for (int c = 0; c < decode->channel_count; c++)
{
exr_coding_channel_info_t& outc = decode->channels[c];
outc.decode_to_ptr = dptr;
outc.user_pixel_stride = outc.user_bytes_per_element;
outc.user_line_stride = 0;
dptr += totsamps * (uint64_t) outc.user_bytes_per_element;
}
return EXR_ERR_SUCCESS;
}
////////////////////////////////////////
bool
readCoreScanlinePart (
exr_context_t f, int part, bool reduceMemory, bool reduceTime)
{
exr_result_t rv;
exr_result_t rv, frv;
exr_attr_box2i_t datawin;
rv = exr_get_data_window (f, part, &datawin);
if (rv != EXR_ERR_SUCCESS) return true;
@@ -1224,6 +1303,8 @@ readCoreScanlinePart (
rv = exr_get_scanlines_per_chunk (f, part, &lines_per_chunk);
if (rv != EXR_ERR_SUCCESS) return true;
frv = rv;
for (uint64_t chunk = 0; chunk < height; chunk += lines_per_chunk)
{
exr_chunk_info_t cinfo = {0};
@@ -1232,6 +1313,7 @@ readCoreScanlinePart (
rv = exr_read_scanline_chunk_info (f, part, y, &cinfo);
if (rv != EXR_ERR_SUCCESS)
{
frv = rv;
if (reduceTime) break;
continue;
}
@@ -1253,19 +1335,32 @@ readCoreScanlinePart (
(uint64_t) lines_per_chunk;
}
// TODO: check we are supposed to multiple by lines per chunk above
doread = true;
if (reduceMemory && bytes >= gMaxBytesPerScanline) doread = false;
if (reduceMemory && bytes >= gMaxBytesPerScanline)
doread = false;
if (doread) imgdata.resize (bytes);
if (cinfo.type == EXR_STORAGE_DEEP_SCANLINE)
{
decoder.decoding_user_data = &imgdata;
decoder.realloc_nonimage_data_fn = &realloc_deepdata;
}
else
{
if (doread) imgdata.resize (bytes);
}
rv = exr_decoding_choose_default_routines (f, part, &decoder);
if (rv != EXR_ERR_SUCCESS) break;
if (rv != EXR_ERR_SUCCESS)
{
frv = rv;
break;
}
}
else
{
rv = exr_decoding_update (f, part, &cinfo, &decoder);
if (rv != EXR_ERR_SUCCESS)
{
frv = rv;
if (reduceTime) break;
continue;
}
@@ -1273,20 +1368,25 @@ readCoreScanlinePart (
if (doread)
{
uint8_t* dptr = &(imgdata[0]);
for (int c = 0; c < decoder.channel_count; c++)
if (cinfo.type != EXR_STORAGE_DEEP_SCANLINE)
{
exr_coding_channel_info_t& outc = decoder.channels[c];
outc.decode_to_ptr = dptr;
outc.user_pixel_stride = outc.user_bytes_per_element;
outc.user_line_stride = outc.user_pixel_stride * width;
dptr += width * (uint64_t) outc.user_bytes_per_element *
uint8_t* dptr = &(imgdata[0]);
for (int c = 0; c < decoder.channel_count; c++)
{
exr_coding_channel_info_t& outc = decoder.channels[c];
outc.decode_to_ptr = dptr;
outc.user_pixel_stride = outc.user_bytes_per_element;
outc.user_line_stride = outc.user_pixel_stride * width;
dptr += width * (uint64_t) outc.user_bytes_per_element *
(uint64_t) lines_per_chunk;
}
}
rv = exr_decoding_run (f, part, &decoder);
if (rv != EXR_ERR_SUCCESS)
{
frv = rv;
if (reduceTime) break;
}
}
@@ -1294,7 +1394,7 @@ readCoreScanlinePart (
exr_decoding_destroy (f, &decoder);
return (rv != EXR_ERR_SUCCESS);
return (frv != EXR_ERR_SUCCESS);
}
////////////////////////////////////////
@@ -1303,7 +1403,7 @@ bool
readCoreTiledPart (
exr_context_t f, int part, bool reduceMemory, bool reduceTime)
{
exr_result_t rv;
exr_result_t rv, frv;
exr_attr_box2i_t datawin;
rv = exr_get_data_window (f, part, &datawin);
@@ -1321,6 +1421,7 @@ readCoreTiledPart (
rv = exr_get_tile_levels (f, part, &levelsx, &levelsy);
if (rv != EXR_ERR_SUCCESS) return true;
frv = rv;
bool keepgoing = true;
for (int32_t ylevel = 0; keepgoing && ylevel < levelsy; ++ylevel)
{
@@ -1330,6 +1431,7 @@ readCoreTiledPart (
rv = exr_get_level_sizes (f, part, xlevel, ylevel, &levw, &levh);
if (rv != EXR_ERR_SUCCESS)
{
frv = rv;
if (reduceTime)
{
keepgoing = false;
@@ -1342,6 +1444,7 @@ readCoreTiledPart (
rv = exr_get_tile_sizes (f, part, xlevel, ylevel, &curtw, &curth);
if (rv != EXR_ERR_SUCCESS)
{
frv = rv;
if (reduceTime)
{
keepgoing = false;
@@ -1371,6 +1474,7 @@ readCoreTiledPart (
f, part, tx, ty, xlevel, ylevel, &cinfo);
if (rv != EXR_ERR_SUCCESS)
{
frv = rv;
if (reduceTime)
{
keepgoing = false;
@@ -1385,6 +1489,7 @@ readCoreTiledPart (
exr_decoding_initialize (f, part, &cinfo, &decoder);
if (rv != EXR_ERR_SUCCESS)
{
frv = rv;
keepgoing = false;
break;
}
@@ -1409,11 +1514,20 @@ readCoreTiledPart (
if (reduceMemory && bytes >= gMaxTileBytes)
doread = false;
if (doread) tiledata.resize (bytes);
if (cinfo.type == EXR_STORAGE_DEEP_TILED)
{
decoder.decoding_user_data = &tiledata;
decoder.realloc_nonimage_data_fn = &realloc_deepdata;
}
else
{
if (doread) tiledata.resize (bytes);
}
rv = exr_decoding_choose_default_routines (
f, part, &decoder);
if (rv != EXR_ERR_SUCCESS)
{
frv = rv;
keepgoing = false;
break;
}
@@ -1423,6 +1537,7 @@ readCoreTiledPart (
rv = exr_decoding_update (f, part, &cinfo, &decoder);
if (rv != EXR_ERR_SUCCESS)
{
frv = rv;
if (reduceTime)
{
keepgoing = false;
@@ -1434,24 +1549,28 @@ readCoreTiledPart (
if (doread)
{
uint8_t* dptr = &(tiledata[0]);
for (int c = 0; c < decoder.channel_count; c++)
if (cinfo.type != EXR_STORAGE_DEEP_TILED)
{
exr_coding_channel_info_t& outc =
decoder.channels[c];
outc.decode_to_ptr = dptr;
outc.user_pixel_stride =
outc.user_bytes_per_element;
outc.user_line_stride =
outc.user_pixel_stride * curtw;
dptr += (uint64_t) curtw *
uint8_t* dptr = &(tiledata[0]);
for (int c = 0; c < decoder.channel_count; c++)
{
exr_coding_channel_info_t& outc =
decoder.channels[c];
outc.decode_to_ptr = dptr;
outc.user_pixel_stride =
outc.user_bytes_per_element;
outc.user_line_stride =
outc.user_pixel_stride * curtw;
dptr += (uint64_t) curtw *
(uint64_t) outc.user_bytes_per_element *
(uint64_t) curth;
}
}
rv = exr_decoding_run (f, part, &decoder);
if (rv != EXR_ERR_SUCCESS)
{
frv = rv;
if (reduceTime)
{
keepgoing = false;
@@ -1486,17 +1605,14 @@ checkCoreFile (exr_context_t f, bool reduceMemory, bool reduceTime)
rv = exr_get_storage (f, p, &store);
if (rv != EXR_ERR_SUCCESS) return true;
// TODO: Need to fill this in
if (store == EXR_STORAGE_DEEP_SCANLINE ||
store == EXR_STORAGE_DEEP_TILED)
continue;
if (store == EXR_STORAGE_SCANLINE)
if (store == EXR_STORAGE_SCANLINE ||
store == EXR_STORAGE_DEEP_SCANLINE)
{
if (readCoreScanlinePart (f, p, reduceMemory, reduceTime))
return true;
}
else if (store == EXR_STORAGE_TILED)
else if (store == EXR_STORAGE_TILED ||
store == EXR_STORAGE_DEEP_TILED)
{
if (readCoreTiledPart (f, p, reduceMemory, reduceTime)) return true;
}
@@ -1535,6 +1651,20 @@ runCoreChecks (const char* filename, bool reduceMemory, bool reduceTime)
cinit.error_handler_fn = &core_error_handler_cb;
if (reduceMemory || reduceTime)
{
/* could use set_default functions for this, but those just
* initialize the context, doing it in the initializer is mt
* safe...
* exr_set_default_maximum_image_size (2048, 2048);
* exr_set_default_maximum_tile_size (512, 512);
*/
cinit.max_image_width = 2048;
cinit.max_image_height = 2048;
cinit.max_tile_width = 512;
cinit.max_tile_height = 512;
}
rv = exr_start_read (&f, filename, &cinit);
if (rv != EXR_ERR_SUCCESS) return true;
@@ -1604,6 +1734,19 @@ runCoreChecks (
cinit.read_fn = &memstream_read;
cinit.size_fn = &memstream_size;
cinit.error_handler_fn = &core_error_handler_cb;
if (reduceMemory || reduceTime)
{
/* could use set_default functions for this, but those just
* initialize the context, doing it in the initializer is mt
* safe...
* exr_set_default_maximum_image_size (2048, 2048);
* exr_set_default_maximum_tile_size (512, 512);
*/
cinit.max_image_width = 2048;
cinit.max_image_height = 2048;
cinit.max_tile_width = 512;
cinit.max_tile_height = 512;
}
rv = exr_start_read (&f, "<memstream>", &cinit);
if (rv != EXR_ERR_SUCCESS) return true;
+11 -6
View File
@@ -4,10 +4,15 @@
# We require this to get object library link library support and
# combined python 2 + 3 support
add_subdirectory(IexTest)
add_subdirectory(OpenEXRCoreTest)
add_subdirectory(OpenEXRTest)
add_subdirectory(OpenEXRUtilTest)
add_subdirectory(OpenEXRFuzzTest)
add_subdirectory(bin)
if (OPENEXR_TEST_LIBRARIES)
add_subdirectory(IexTest)
add_subdirectory(OpenEXRCoreTest)
add_subdirectory(OpenEXRTest)
add_subdirectory(OpenEXRUtilTest)
add_subdirectory(OpenEXRFuzzTest)
endif()
if (OPENEXR_BUILD_TOOLS AND OPENEXR_TEST_TOOLS)
add_subdirectory(bin)
endif()
+14 -1
View File
@@ -20,6 +20,7 @@
#include <iostream>
#include <ImfSystemSpecific.h>
#include <ImfNamespace.h>
#include "../../lib/OpenEXRCore/internal_cpuid.h"
#include "../../lib/OpenEXRCore/internal_coding.h"
@@ -52,6 +53,18 @@ testBase (const std::string& tempdir)
exr_get_library_version (&major, NULL, &patch, &extra);
exr_get_library_version (&major, &minor, NULL, &extra);
exr_get_library_version (&major, &minor, &patch, NULL);
major = (OPENEXR_VERSION_HEX >> 24) & 0xff;
minor = (OPENEXR_VERSION_HEX >> 16) & 0xff;
patch = (OPENEXR_VERSION_HEX >> 8) & 0xff;
EXRCORE_TEST (major == COMP_MAJ);
EXRCORE_TEST (minor == COMP_MIN);
EXRCORE_TEST (patch == COMP_PATCH);
#if OPENEXR_VERSION_HEX > 0
// confirm the macro compiles in an #if
#endif
}
void
@@ -352,7 +365,7 @@ testBaseDebug (const std::string& tempdir)
void testCPUIdent (const std::string& tempdir)
{
int hf16c, havx, hsse2;
Imf::CpuId id;
OPENEXR_IMF_NAMESPACE::CpuId id;
check_for_x86_simd (&hf16c, &havx, &hsse2);
if (hf16c != (int)id.f16c)
+3
View File
@@ -57,6 +57,8 @@ add_executable(OpenEXRTest
testExistingStreams.h
testFutureProofing.cpp
testFutureProofing.h
testHeader.cpp
testHeader.h
testHuf.cpp
testHuf.h
testIDManifest.cpp
@@ -169,6 +171,7 @@ define_openexr_tests(
testDwaLookups
testExistingStreams
testFutureProofing
testHeader
testHuf
testInputPart
testIsComplete
+1 -1
View File
@@ -19,8 +19,8 @@
# include <sys/types.h>
# define bswap_32(x) swap32 (x)
#elif defined(__NetBSD__)
# include <sys/bswap.h>
# include <sys/types.h>
# include <machine/bswap.h>
# define bswap_32(x) bswap32 (x)
#else
# include <byteswap.h>
+2
View File
@@ -32,6 +32,7 @@
#include "testDwaLookups.h"
#include "testExistingStreams.h"
#include "testFutureProofing.h"
#include "testHeader.h"
#include "testHuf.h"
#include "testIDManifest.h"
#include "testInputPart.h"
@@ -232,6 +233,7 @@ main (int argc, char* argv[])
TEST (testDwaLookups, "core");
TEST (testIDManifest, "core");
TEST (testCpuId, "core");
TEST (testHeader, "basic");
// NB: If you add a test here, make sure to enumerate it in the
// CMakeLists.txt so it runs as part of the test suite
+1 -1
View File
@@ -28,7 +28,7 @@ testCpuId (const string&)
std::cout << "IMF_HAVE_AVX: " << false << "\n";
#endif
Imf::CpuId cpuId;
OPENEXR_IMF_NAMESPACE::CpuId cpuId;
std::cout << "cpuId.sse2: " << cpuId.sse2 << "\n";
std::cout << "cpuId.sse3: " << cpuId.sse3 << "\n";
std::cout << "cpuId.ssse3: " << cpuId.ssse3 << "\n";
+109
View File
@@ -0,0 +1,109 @@
//
// SPDX-License-Identifier: BSD-3-Clause
// Copyright (c) Contributors to the OpenEXR Project.
//
#ifdef NDEBUG
# undef NDEBUG
#endif
#include <ImfBoxAttribute.h>
#include <ImfHeader.h>
#include <exception>
#include <iostream>
#include <string>
#include <assert.h>
using namespace IEX_NAMESPACE;
using namespace IMATH_NAMESPACE;
using namespace OPENEXR_IMF_NAMESPACE;
using namespace std;
template<typename Header>
struct Test
{
void testFind(const string& name)
{
Header header;
auto iterator = header.find(name);
assert(iterator != header.end());
}
void testSubscript(const string& name)
{
Header header;
auto& comparand = header.find("displayWindow").attribute();
auto& attribute = header[name];
assert(&attribute == &comparand);
}
void testIterators(const string& name)
{
Header header;
auto& comparand = header.find("displayWindow").attribute();
for (auto iterator = header.begin(); iterator != header.end(); ++iterator)
{
if (iterator.name() == name)
{
assert(&iterator.attribute() == &comparand);
return;
}
}
assert (false);
}
};
void testEraseAttribute(const string& name)
{
Header header;
assert(header.find(name) != header.end());
header.erase(name);
assert(header.find(name) == header.end());
}
void testEraseAttributeThrowsWithEmptyString()
{
Header header;
try
{
header.erase("");
assert (false);
}
catch (const ArgExc&)
{
assert (true);
}
}
void testHeader (const string& tempDir)
{
try
{
{
Test<Header> headerTest;
headerTest.testFind("displayWindow");
headerTest.testSubscript("displayWindow");
headerTest.testIterators("displayWindow");
}
{
Test<const Header> headerTest;
headerTest.testFind("displayWindow");
headerTest.testSubscript("displayWindow");
headerTest.testIterators("displayWindow");
}
testEraseAttribute("displayWindow");
testEraseAttributeThrowsWithEmptyString();
cout << "ok\n" << endl;
}
catch (const exception& e)
{
cerr << "ERROR -- caught exception: " << e.what () << endl;
assert (false);
}
}
+8
View File
@@ -0,0 +1,8 @@
//
// SPDX-License-Identifier: BSD-3-Clause
// Copyright (c) Contributors to the OpenEXR Project.
//
#include <string>
void testHeader (const std::string& tempDir);
+6 -2
View File
@@ -16,8 +16,12 @@ if(BUILD_TESTING)
set(OPENEXR_IMAGES_TAG "v1.0" CACHE STRING "Bin test image repo tag")
set(images_url ${OPENEXR_IMAGES_REPO}/${OPENEXR_IMAGES_TAG})
if ("${OPENEXR_IMAGES_TAG}" STREQUAL "")
set(images_url ${OPENEXR_IMAGES_REPO})
else()
set(images_url ${OPENEXR_IMAGES_REPO}/${OPENEXR_IMAGES_TAG})
endif()
set(images
TestImages/GrayRampsHorizontal.exr
LuminanceChroma/Garden.exr
+13 -13
View File
@@ -16,31 +16,31 @@ version = sys.argv[4]
# no args = usage message, error
result = run ([exr2aces], stdout=PIPE, stderr=PIPE, universal_newlines=True)
print(" ".join(result.args))
assert(result.returncode != 0)
assert(result.stderr.startswith ("Usage: "))
assert(result.returncode != 0), "\n"+result.stderr
assert(result.stderr.startswith ("Usage: ")), "\n"+result.stderr
# -h = usage message
result = run ([exr2aces, "-h"], stdout=PIPE, stderr=PIPE, universal_newlines=True)
print(" ".join(result.args))
assert(result.returncode == 0)
assert(result.stdout.startswith ("Usage: "))
assert(result.returncode == 0), "\n"+result.stderr
assert(result.stdout.startswith ("Usage: ")), "\n"+result.stdout
result = run ([exr2aces, "--help"], stdout=PIPE, stderr=PIPE, universal_newlines=True)
print(" ".join(result.args))
assert(result.returncode == 0)
assert(result.stdout.startswith ("Usage: "))
assert(result.returncode == 0), "\n"+result.stderr
assert(result.stdout.startswith ("Usage: ")), "\n"+result.stdout
# --version
result = run ([exr2aces, "--version"], stdout=PIPE, stderr=PIPE, universal_newlines=True)
print(" ".join(result.args))
assert(result.returncode == 0)
assert(result.stdout.startswith ("exr2aces"))
assert(version in result.stdout)
assert(result.returncode == 0), "\n"+result.stderr
assert(result.stdout.startswith ("exr2aces")), "\n"+result.stdout
assert(version in result.stdout), "\n"+result.stdout
# invalid arguments
result = run ([exr2aces, "foo.exr", "bar.exr"], stdout=PIPE, stderr=PIPE, universal_newlines=True)
print(" ".join(result.args))
assert(result.returncode != 0)
assert(result.returncode != 0), "\n"+result.stderr
def find_line(keyword, lines):
for line in lines:
@@ -58,14 +58,14 @@ atexit.register(cleanup)
image = f"{image_dir}/TestImages/GrayRampsHorizontal.exr"
result = run ([exr2aces, "-v", image, outimage], stdout=PIPE, stderr=PIPE, universal_newlines=True)
print(" ".join(result.args))
assert(result.returncode == 0)
assert(result.returncode == 0), "\n"+result.stderr
result = run ([exrinfo, "-v", outimage], stdout=PIPE, stderr=PIPE, universal_newlines=True)
print(" ".join(result.args))
assert(result.returncode == 0)
assert(result.returncode == 0), "\n"+result.stderr
# confirm the output has the proper chromaticities
assert("chromaticities: chromaticities r[0.7347, 0.2653] g[0, 1] b[0.0001, -0.077] w[0.32168, 0.33767]" in result.stdout)
assert("chromaticities: chromaticities r[0.7347, 0.2653] g[0, 1] b[0.0001, -0.077] w[0.32168, 0.33767]" in result.stdout), "\n"+result.stdout
print("success")
+6 -11
View File
@@ -7,7 +7,7 @@ import sys, os
from subprocess import PIPE, run
print(f"testing exrcheck: {' '.join(sys.argv)}")
exrcheck = sys.argv[1]
image_dir = sys.argv[2]
@@ -17,28 +17,23 @@ for exr_file in sys.argv[3:]:
result = run ([exrcheck, exr_path], stdout=PIPE, stderr=PIPE, universal_newlines=True)
print(" ".join(result.args))
print(result.stderr)
assert(result.returncode == 0)
assert(result.returncode == 0), "\n"+result.stderr
result = run ([exrcheck, "-m", exr_path], stdout=PIPE, stderr=PIPE, universal_newlines=True)
print(" ".join(result.args))
print(result.stderr)
assert(result.returncode == 0)
assert(result.returncode == 0), "\n"+result.stderr
result = run ([exrcheck, "-t", exr_path], stdout=PIPE, stderr=PIPE, universal_newlines=True)
print(" ".join(result.args))
print(result.stderr)
assert(result.returncode == 0)
assert(result.returncode == 0), "\n"+result.stderr
result = run ([exrcheck, "-s", exr_path], stdout=PIPE, stderr=PIPE, universal_newlines=True)
print(" ".join(result.args))
print(result.stderr)
assert(result.returncode == 0)
assert(result.returncode == 0), "\n"+result.stderr
result = run ([exrcheck, "-c", exr_path], stdout=PIPE, stderr=PIPE, universal_newlines=True)
print(" ".join(result.args))
print(result.stderr)
assert(result.returncode == 0)
assert(result.returncode == 0), "\n"+result.stderr
print("success.")
+88 -88
View File
@@ -16,10 +16,10 @@ version = sys.argv[4]
latlong_image = f"{image_dir}/MultiResolution/WavyLinesLatLong.exr"
cube_image = f"{image_dir}/MultiResolution/WavyLinesCube.exr"
assert(os.path.isfile(exrenvmap))
assert(os.path.isfile(exrinfo))
assert(os.path.isdir(image_dir))
assert(os.path.isfile(latlong_image))
assert(os.path.isfile(exrenvmap)), "\nMissing " + exrenvmap
assert(os.path.isfile(exrinfo)), "\nMissing " + exrinfo
assert(os.path.isdir(image_dir)), "\nMissing " + image_dir
assert(os.path.isfile(latlong_image)), "\nMissing " + latlong_image
fd, outimage = tempfile.mkstemp(".exr")
os.close(fd)
@@ -31,221 +31,221 @@ atexit.register(cleanup)
# no args = usage message
result = run ([exrenvmap], stdout=PIPE, stderr=PIPE, universal_newlines=True)
print(" ".join(result.args))
assert(result.returncode != 0)
assert(result.stderr.startswith ("Usage: "))
assert(result.returncode != 0), "\n"+result.stderr
assert(result.stderr.startswith ("Usage: ")), "\n"+result.stderr
# -h = usage message
result = run ([exrenvmap, "-h"], stdout=PIPE, stderr=PIPE, universal_newlines=True)
print(" ".join(result.args))
assert(result.returncode == 0)
assert(result.stdout.startswith ("Usage: "))
assert(result.returncode == 0), "\n"+result.stderr
assert(result.stdout.startswith ("Usage: ")), "\n"+result.stdout
# --help = usage message
result = run ([exrenvmap, "--help"], stdout=PIPE, stderr=PIPE, universal_newlines=True)
print(" ".join(result.args))
assert(result.returncode == 0)
assert(result.stdout.startswith ("Usage: "))
assert(result.returncode == 0), "\n"+result.stderr
assert(result.stdout.startswith ("Usage: ")), "\n"+result.stdout
# --version
result = run ([exrenvmap, "--version"], stdout=PIPE, stderr=PIPE, universal_newlines=True)
print(" ".join(result.args))
assert(result.returncode == 0)
assert(result.stdout.startswith ("exrenvmap"))
assert(version in result.stdout)
assert(result.returncode == 0), "\n"+result.stderr
assert(result.stdout.startswith ("exrenvmap")), "\n"+result.stdout
assert(version in result.stdout), "\n"+result.stdout
# default
result = run ([exrenvmap, latlong_image, outimage], stdout=PIPE, stderr=PIPE, universal_newlines=True)
print(" ".join(result.args))
assert(result.returncode == 0)
assert(os.path.isfile(outimage))
assert(result.returncode == 0), "\n"+result.stderr
assert(os.path.isfile(outimage)), "\nMissing " + outimage
default_file_size = os.path.getsize(outimage)
result = run ([exrenvmap, "-li", latlong_image, outimage], stdout=PIPE, stderr=PIPE, universal_newlines=True)
print(" ".join(result.args))
assert(result.returncode == 0)
assert(os.path.isfile(outimage))
assert(result.returncode == 0), "\n"+result.stderr
assert(os.path.isfile(outimage)), "\nMissing " + outimage
result = run ([exrenvmap, "-ci", cube_image, outimage], stdout=PIPE, stderr=PIPE, universal_newlines=True)
print(" ".join(result.args))
assert(result.returncode == 0)
assert(os.path.isfile(outimage))
assert(result.returncode == 0), "\n"+result.stderr
assert(os.path.isfile(outimage)), "\nMissing " + outimage
# -o
result = run ([exrenvmap, "-o", latlong_image, outimage], stdout=PIPE, stderr=PIPE, universal_newlines=True)
print(" ".join(result.args))
assert(result.returncode == 0)
assert(os.path.isfile(outimage))
assert(result.returncode == 0), "\n"+result.stderr
assert(os.path.isfile(outimage)), "\nMissing " + outimage
result = run ([exrinfo, "-v", outimage], stdout=PIPE, stderr=PIPE, universal_newlines=True)
print(" ".join(result.args))
assert(result.returncode == 0)
assert('tiles: tiledesc size 64 x 64 level 0 (single image) round 0 (down)' in result.stdout)
assert(result.returncode == 0), "\n"+result.stderr
assert('tiles: tiledesc size 64 x 64 level 0 (single image) round 0 (down)' in result.stdout), "\n"+result.stdout
os.unlink(outimage)
# -m
result = run ([exrenvmap, "-m", latlong_image, outimage], stdout=PIPE, stderr=PIPE, universal_newlines=True)
print(" ".join(result.args))
assert(result.returncode == 0)
assert(os.path.isfile(outimage))
assert(os.path.isfile(outimage)), "\nMissing " + outimage
result = run ([exrinfo, "-v", outimage], stdout=PIPE, stderr=PIPE, universal_newlines=True)
print(" ".join(result.args))
assert(result.returncode == 0)
assert('tiles: tiledesc size 64 x 64 level 1 (mipmap) round 0 (down)' in result.stdout)
assert(result.returncode == 0), "\n"+result.stderr
assert('tiles: tiledesc size 64 x 64 level 1 (mipmap) round 0 (down)' in result.stdout), "\n"+result.stdout
os.unlink(outimage)
# -c
result = run ([exrenvmap, "-c", latlong_image, outimage], stdout=PIPE, stderr=PIPE, universal_newlines=True)
print(" ".join(result.args))
assert(result.returncode == 0)
assert(os.path.isfile(outimage))
assert(result.returncode == 0), "\n"+result.stderr
assert(os.path.isfile(outimage)), "\nMissing " + outimage
result = run ([exrinfo, "-v", outimage], stdout=PIPE, stderr=PIPE, universal_newlines=True)
print(" ".join(result.args))
assert(result.returncode == 0)
assert('envmap: envmap cube' in result.stdout)
assert(result.returncode == 0), "\n"+result.stderr
assert('envmap: envmap cube' in result.stdout), "\n"+result.stdout
os.unlink(outimage)
# -l
result = run ([exrenvmap, "-l", latlong_image, outimage], stdout=PIPE, stderr=PIPE, universal_newlines=True)
print(" ".join(result.args))
assert(result.returncode == 0)
assert(os.path.isfile(outimage))
assert(result.returncode == 0), "\n"+result.stderr
assert(os.path.isfile(outimage)), "\nMissing " + outimage
result = run ([exrinfo, "-v", outimage], stdout=PIPE, stderr=PIPE, universal_newlines=True)
print(" ".join(result.args))
assert(result.returncode == 0)
assert('envmap: envmap latlong' in result.stdout)
assert(result.returncode == 0), "\n"+result.stderr
assert('envmap: envmap latlong' in result.stdout), "\n"+result.stdout
os.unlink(outimage)
# -w
result = run ([exrenvmap, "-w", latlong_image, outimage], stdout=PIPE, stderr=PIPE, universal_newlines=True)
print(" ".join(result.args))
assert(result.returncode != 0)
assert(not os.path.isfile(outimage))
assert(result.returncode != 0), "\n"+result.stderr
assert(not os.path.isfile(outimage)), "\nMissing " + outimage
result = run ([exrenvmap, "-w", "-64", latlong_image, outimage], stdout=PIPE, stderr=PIPE, universal_newlines=True)
print(" ".join(result.args))
assert(result.returncode != 0)
assert(not os.path.isfile(outimage))
assert(result.returncode != 0), "\n"+result.stderr
assert(not os.path.isfile(outimage)), "\nMissing " + outimage
result = run ([exrenvmap, "-w", "64", latlong_image, outimage], stdout=PIPE, stderr=PIPE, universal_newlines=True)
print(" ".join(result.args))
assert(result.returncode == 0)
assert(os.path.isfile(outimage))
assert(result.returncode == 0), "\n"+result.stderr
assert(os.path.isfile(outimage)), "\nMissing " + outimage
result = run ([exrinfo, "-v", outimage], stdout=PIPE, stderr=PIPE, universal_newlines=True)
print(" ".join(result.args))
assert(result.returncode == 0)
assert('x tile count: 1 (sz 64)' in result.stdout)
assert('y tile count: 6 (sz 384)' in result.stdout)
assert(result.returncode == 0), "\n"+result.stderr
assert('x tile count: 1 (sz 64)' in result.stdout), "\n"+result.stdout
assert('y tile count: 6 (sz 384)' in result.stdout), "\n"+result.stdout
os.unlink(outimage)
# -f (filter)
result = run ([exrenvmap, "-f", latlong_image, outimage], stdout=PIPE, stderr=PIPE, universal_newlines=True)
print(" ".join(result.args))
assert(result.returncode != 0)
assert(not os.path.isfile(outimage))
assert(result.returncode != 0), "\n"+result.stderr
assert(not os.path.isfile(outimage)), "\nMissing " + outimage
result = run ([exrenvmap, "-f", "1.1", latlong_image, outimage], stdout=PIPE, stderr=PIPE, universal_newlines=True)
print(" ".join(result.args))
assert(result.returncode != 0)
assert(not os.path.isfile(outimage))
assert(result.returncode != 0), "\n"+result.stderr
assert(not os.path.isfile(outimage)), "\nMissing " + outimage
result = run ([exrenvmap, "-f", "-1.1", "6", latlong_image, outimage], stdout=PIPE, stderr=PIPE, universal_newlines=True)
print(" ".join(result.args))
assert(result.returncode != 0)
assert(not os.path.isfile(outimage))
assert(result.returncode != 0), "\n"+result.stderr
assert(not os.path.isfile(outimage)), "\nMissing " + outimage
result = run ([exrenvmap, "-f", "1.1", "-6", latlong_image, outimage], stdout=PIPE, stderr=PIPE, universal_newlines=True)
print(" ".join(result.args))
assert(result.returncode != 0)
assert(not os.path.isfile(outimage))
assert(result.returncode != 0), "\n"+result.stderr
assert(not os.path.isfile(outimage)), "\nMissing " + outimage
result = run ([exrenvmap, "-f", "1.1", "6", latlong_image, outimage], stdout=PIPE, stderr=PIPE, universal_newlines=True)
print(" ".join(result.args))
assert(result.returncode == 0)
assert(os.path.isfile(outimage))
assert(result.returncode == 0), "\n"+result.stderr
assert(os.path.isfile(outimage)), "\nMissing " + outimage
file_size = os.path.getsize(outimage)
assert(file_size != default_file_size)
assert(file_size != default_file_size), "\n{} is the wrong size".format(outimage)
os.unlink(outimage)
# -b (blur)
result = run ([exrenvmap, "-b", latlong_image, outimage], stdout=PIPE, stderr=PIPE, universal_newlines=True)
print(" ".join(result.args))
assert(result.returncode == 0)
assert(os.path.isfile(outimage))
assert(result.returncode == 0), "\n"+result.stderr
assert(os.path.isfile(outimage)), "\nMissing " + outimage
file_size = os.path.getsize(outimage)
assert(file_size != default_file_size)
assert(file_size != default_file_size), "\n{} is the wrong size".format(outimage)
os.unlink(outimage)
# -t
result = run ([exrenvmap, "-t", latlong_image, outimage], stdout=PIPE, stderr=PIPE, universal_newlines=True)
print(" ".join(result.args))
assert(result.returncode != 0)
assert(not os.path.isfile(outimage))
assert(result.returncode != 0), "\n"+result.stderr
assert(not os.path.isfile(outimage)), "\n{} still exists".format(outimage)
result = run ([exrenvmap, "-t", "32", latlong_image, outimage], stdout=PIPE, stderr=PIPE, universal_newlines=True)
print(" ".join(result.args))
assert(result.returncode != 0)
assert(not os.path.isfile(outimage))
assert(result.returncode != 0), "\n"+result.stderr
assert(not os.path.isfile(outimage)), "\n{} still exists".format(outimage)
result = run ([exrenvmap, "-t", "-32", "48", latlong_image, outimage], stdout=PIPE, stderr=PIPE, universal_newlines=True)
print(" ".join(result.args))
assert(result.returncode != 0)
assert(not os.path.isfile(outimage))
assert(result.returncode != 0), "\n"+result.stderr
assert(not os.path.isfile(outimage)), "\n{} still exists".format(outimage)
result = run ([exrenvmap, "-t", "32", "-48", latlong_image, outimage], stdout=PIPE, stderr=PIPE, universal_newlines=True)
print(" ".join(result.args))
assert(result.returncode != 0)
assert(not os.path.isfile(outimage))
assert(result.returncode != 0), "\n"+result.stderr
assert(not os.path.isfile(outimage)), "\n{} still exists".format(outimage)
result = run ([exrenvmap, "-t", "32", "48", latlong_image, outimage], stdout=PIPE, stderr=PIPE, universal_newlines=True)
print(" ".join(result.args))
assert(result.returncode == 0)
assert(os.path.isfile(outimage))
assert(result.returncode == 0), "\n"+result.stderr
assert(os.path.isfile(outimage)), "\nMissing " + outimage
result = run ([exrinfo, "-v", outimage], stdout=PIPE, stderr=PIPE, universal_newlines=True)
print(" ".join(result.args))
assert(result.returncode == 0)
assert('x tile count: 8 (sz 256)' in result.stdout)
assert('y tile count: 32 (sz 1536)' in result.stdout)
assert(result.returncode == 0), "\n"+result.stderr
assert('x tile count: 8 (sz 256)' in result.stdout), "\n"+result.stdout
assert('y tile count: 32 (sz 1536)' in result.stdout), "\n"+result.stdout
os.unlink(outimage)
# -u
result = run ([exrenvmap, "-u", latlong_image, outimage], stdout=PIPE, stderr=PIPE, universal_newlines=True)
print(" ".join(result.args))
assert(result.returncode == 0)
assert(os.path.isfile(outimage))
assert(result.returncode == 0), "\n"+result.stderr
assert(os.path.isfile(outimage)), "\nMissing " + outimage
result = run ([exrinfo, "-v", outimage], stdout=PIPE, stderr=PIPE, universal_newlines=True)
print(" ".join(result.args))
assert(result.returncode == 0)
assert('tiles: tiledesc size 64 x 64 level 0 (single image) round 1 (up)' in result.stdout)
assert(result.returncode == 0), "\n"+result.stderr
assert('tiles: tiledesc size 64 x 64 level 0 (single image) round 1 (up)' in result.stdout), "\n"+result.stdout
os.unlink(outimage)
# -z
result = run ([exrenvmap, "-z", latlong_image, outimage], stdout=PIPE, stderr=PIPE, universal_newlines=True)
print(" ".join(result.args))
assert(result.returncode != 0)
assert(not os.path.isfile(outimage))
assert(result.returncode != 0), "\n"+result.stderr
assert(not os.path.isfile(outimage)), "\n{} still exists".format(outimage)
result = run ([exrenvmap, "-z", "xxx", latlong_image, outimage], stdout=PIPE, stderr=PIPE, universal_newlines=True)
print(" ".join(result.args))
assert(result.returncode != 0)
assert(not os.path.isfile(outimage))
assert(result.returncode != 0), "\n"+result.stderr
assert(not os.path.isfile(outimage)), "\n{} still exists".format(outimage)
for z in ["none", "rle", "zip", "piz", "pxr24", "b44", "b44a", "dwaa", "dwab"]:
result = run ([exrenvmap, "-z", z, latlong_image, outimage], stdout=PIPE, stderr=PIPE, universal_newlines=True)
print(" ".join(result.args))
assert(result.returncode == 0)
assert(os.path.isfile(outimage))
assert(result.returncode == 0), "\n"+result.stderr
assert(os.path.isfile(outimage)), "\nMissing " + outimage
result = run ([exrinfo, "-v", outimage], stdout=PIPE, stderr=PIPE, universal_newlines=True)
print(" ".join(result.args))
assert(result.returncode == 0)
assert(f'compression: compression \'{z}\'' in result.stdout)
assert(result.returncode == 0), "\n"+result.stderr
assert(f'compression: compression \'{z}\'' in result.stdout), "\n"+result.stdout
os.unlink(outimage)
with tempfile.TemporaryDirectory() as tempdir:
@@ -253,7 +253,7 @@ with tempfile.TemporaryDirectory() as tempdir:
cube_face_image_t = f"{tempdir}/out.%.exr"
result = run ([exrenvmap, latlong_image, cube_face_image_t], stdout=PIPE, stderr=PIPE, universal_newlines=True)
print(" ".join(result.args))
assert(result.returncode == 0)
assert(result.returncode == 0), "\n"+result.stderr
for o in ["+X", "-X", "+Y", "-Y", "+Z", "-Z"]:
@@ -261,16 +261,16 @@ with tempfile.TemporaryDirectory() as tempdir:
result = run ([exrinfo, "-v", cube_face_image], stdout=PIPE, stderr=PIPE, universal_newlines=True)
print(" ".join(result.args))
assert(result.returncode == 0)
assert('x tile count: 4 (sz 256)' in result.stdout)
assert('y tile count: 4 (sz 256)' in result.stdout)
assert('x tile count: 4 (sz 256)' in result.stdout), "\n"+result.stdout
assert('y tile count: 4 (sz 256)' in result.stdout), "\n"+result.stdout
result = run ([exrenvmap, cube_face_image_t, outimage], stdout=PIPE, stderr=PIPE, universal_newlines=True)
print(" ".join(result.args))
assert(result.returncode == 0)
assert(result.returncode == 0), "\n"+result.stderr
result = run ([exrinfo, "-v", outimage], stdout=PIPE, stderr=PIPE, universal_newlines=True)
print(" ".join(result.args))
assert(result.returncode == 0)
assert('tiles: tiledesc size 64 x 64 level 0 (single image) round 0 (down)' in result.stdout)
assert(result.returncode == 0), "\n"+result.stderr
assert('tiles: tiledesc size 64 x 64 level 0 (single image) round 0 (down)' in result.stdout), "\n"+result.stdout
print("success")
+24 -22
View File
@@ -15,33 +15,32 @@ version = sys.argv[3]
# no args = usage message
result = run ([exrheader], stdout=PIPE, stderr=PIPE, universal_newlines=True)
print(" ".join(result.args))
assert(result.returncode != 0)
assert(result.stderr.startswith ("Usage: "))
assert(result.returncode != 0), "\n"+result.stderr
assert(result.stderr.startswith ("Usage: ")), "\n"+result.stderr
# -h = usage message
result = run ([exrheader, "-h"], stdout=PIPE, stderr=PIPE, universal_newlines=True)
print(" ".join(result.args))
assert(result.returncode == 0)
print(result.stdout)
assert(result.stdout.startswith ("Usage: "))
assert(result.returncode == 0), "\n"+result.stderr
assert(result.stdout.startswith ("Usage: ")), "\n"+result.stdout
# --help = usage message
result = run ([exrheader, "--help"], stdout=PIPE, stderr=PIPE, universal_newlines=True)
print(" ".join(result.args))
assert(result.returncode == 0)
assert(result.stdout.startswith ("Usage: "))
assert(result.returncode == 0), "\n"+result.stderr
assert(result.stdout.startswith ("Usage: ")), "\n"+result.stdout
# --version
result = run ([exrheader, "--version"], stdout=PIPE, stderr=PIPE, universal_newlines=True)
print(" ".join(result.args))
assert(result.returncode == 0)
assert(result.stdout.startswith ("exrheader"))
assert(version in result.stdout)
assert(result.returncode == 0), "\n"+result.stderr
assert(result.stdout.startswith ("exrheader")), "\n"+result.stdout
assert(version in result.stdout), "\n"+result.stdout
# nonexistent.exr, error
result = run ([exrheader, "nonexistent.exr"], stdout=PIPE, stderr=PIPE, universal_newlines=True)
print(" ".join(result.args))
assert(result.returncode != 0)
assert(result.returncode != 0), "\n"+result.stderr
def find_line(keyword, lines):
for line in lines:
@@ -53,19 +52,22 @@ def find_line(keyword, lines):
image = f"{image_dir}/TestImages/GrayRampsHorizontal.exr"
result = run ([exrheader, image], stdout=PIPE, stderr=PIPE, universal_newlines=True)
print(" ".join(result.args))
assert(result.returncode == 0)
assert(result.returncode == 0), "\n"+result.stderr
output = result.stdout.split('\n')
assert ("2, flags 0x0" in find_line("file format version:", output))
assert ("pxr24" in find_line ("compression", output))
assert ("(0 0) - (799 799)" in find_line ("dataWindow", output))
assert ("(0 0) - (799 799)" in find_line ("displayWindow", output))
assert ("increasing y" in find_line ("lineOrder", output))
assert ("1" in find_line ("pixelAspectRatio", output))
assert ("(0 0)" in find_line ("screenWindowCenter", output))
assert ("1" in find_line ("screenWindowWidth", output))
assert ("scanlineimage" in find_line ("type (type string)", output))
try:
assert ("2, flags 0x0" in find_line("file format version:", output))
assert ("pxr24" in find_line ("compression", output))
assert ("(0 0) - (799 799)" in find_line ("dataWindow", output))
assert ("(0 0) - (799 799)" in find_line ("displayWindow", output))
assert ("increasing y" in find_line ("lineOrder", output))
assert ("1" in find_line ("pixelAspectRatio", output))
assert ("(0 0)" in find_line ("screenWindowCenter", output))
assert ("1" in find_line ("screenWindowWidth", output))
assert ("scanlineimage" in find_line ("type (type string)", output))
except AssertionError:
print(result.stdout)
raise
print("success")
+25 -17
View File
@@ -14,43 +14,51 @@ version = sys.argv[3]
result = run ([exrinfo, "-h"], stdout=PIPE, stderr=PIPE, universal_newlines=True)
print(" ".join(result.args))
assert(result.returncode == 0)
assert(result.stdout.startswith ("Usage: "))
assert(result.returncode == 0), "\n"+result.stderr
assert(result.stdout.startswith ("Usage: ")), "\n"+result.stdout
result = run ([exrinfo, "--help"], stdout=PIPE, stderr=PIPE, universal_newlines=True)
print(" ".join(result.args))
assert(result.returncode == 0)
assert(result.stdout.startswith ("Usage: "))
assert(result.returncode == 0), "\n"+result.stderr
assert(result.stdout.startswith ("Usage: ")), "\n"+result.stdout
# --version
result = run ([exrinfo, "--version"], stdout=PIPE, stderr=PIPE, universal_newlines=True)
print(" ".join(result.args))
print(result.stdout)
assert(result.returncode == 0)
assert(result.stdout.startswith ("exrinfo"))
assert(version in result.stdout)
assert(result.returncode == 0), "\n"+result.stderr
assert(result.stdout.startswith ("exrinfo")), "\n"+result.stdout
assert(version in result.stdout), "\n"+result.stdout
image = f"{image_dir}/TestImages/GrayRampsHorizontal.exr"
result = run ([exrinfo, image, "-a", "-v"], stdout=PIPE, stderr=PIPE, universal_newlines=True)
print(" ".join(result.args))
assert(result.returncode == 0)
assert(result.returncode == 0), "\n"+result.stderr
output = result.stdout.split('\n')
assert ('pxr24' in output[1])
assert ('800 x 800' in output[2])
assert ('800 x 800' in output[3])
assert ('1 channels' in output[4])
try:
assert ('pxr24' in output[1])
assert ('800 x 800' in output[2])
assert ('800 x 800' in output[3])
assert ('1 channels' in output[4])
except AssertionError:
print(result.stdout)
raise
# test image as stdio
with open(image, 'rb') as f:
data = f.read()
result = run ([exrinfo, '-', "-a", "-v"], input=data, stdout=PIPE, stderr=PIPE)
print(" ".join(result.args))
assert(result.returncode == 0)
assert(result.returncode == 0), "\n"+result.stderr
output = result.stdout.decode().split('\n')
assert ('pxr24' in output[1])
assert ('800 x 800' in output[2])
assert ('800 x 800' in output[3])
assert ('1 channels' in output[4])
try:
assert ('pxr24' in output[1])
assert ('800 x 800' in output[2])
assert ('800 x 800' in output[3])
assert ('1 channels' in output[4])
except AssertionError:
print(result.stdout)
raise
print("success")
+14 -14
View File
@@ -13,33 +13,33 @@ exrinfo = sys.argv[2]
image_dir = sys.argv[3]
version = sys.argv[4]
assert(os.path.isfile(exrmakepreview))
assert(os.path.isfile(exrinfo))
assert(os.path.isdir(image_dir))
assert(os.path.isfile(exrmakepreview)), "\nMissing " + exrmakepreview
assert(os.path.isfile(exrinfo)), "\nMissing " + exrinfo
assert(os.path.isdir(image_dir)), "\nMissing " + image_dir
# no args = usage message
result = run ([exrmakepreview], stdout=PIPE, stderr=PIPE, universal_newlines=True)
print(" ".join(result.args))
assert(result.returncode != 0)
assert(result.stderr.startswith ("Usage: "))
assert(result.returncode != 0), "\n"+result.stderr
assert(result.stderr.startswith ("Usage: ")), "\n"+result.stderr
# -h = usage message
result = run ([exrmakepreview, "-h"], stdout=PIPE, stderr=PIPE, universal_newlines=True)
print(" ".join(result.args))
assert(result.returncode == 0)
assert(result.returncode == 0), "\n"+result.stderr
assert(result.stdout.startswith ("Usage: "))
result = run ([exrmakepreview, "--help"], stdout=PIPE, stderr=PIPE, universal_newlines=True)
print(" ".join(result.args))
assert(result.returncode == 0)
assert(result.stdout.startswith ("Usage: "))
assert(result.returncode == 0), "\n"+result.stderr
assert(result.stdout.startswith ("Usage: ")), "\n"+result.stdout
# --version
result = run ([exrmakepreview, "--version"], stdout=PIPE, stderr=PIPE, universal_newlines=True)
print(" ".join(result.args))
assert(result.returncode == 0)
assert(result.stdout.startswith ("exrmakepreview"))
assert(version in result.stdout)
assert(result.returncode == 0), "\n"+result.stderr
assert(result.stdout.startswith ("exrmakepreview")), "\n"+result.stdout
assert(version in result.stdout), "\n"+result.stdout
def find_line(keyword, lines):
@@ -58,13 +58,13 @@ atexit.register(cleanup)
image = f"{image_dir}/TestImages/GrayRampsHorizontal.exr"
result = run ([exrmakepreview, "-w", "50", "-e", "1", "-v", image, outimage], stdout=PIPE, stderr=PIPE, universal_newlines=True)
print(" ".join(result.args))
assert(result.returncode == 0)
assert(result.returncode == 0), "\n"+result.stderr
result = run ([exrinfo, "-v", outimage], stdout=PIPE, stderr=PIPE, universal_newlines=True)
print(" ".join(result.args))
assert(result.returncode == 0)
assert(result.returncode == 0), "\n"+result.stderr
output = result.stdout.split('\n')
assert("preview 50 x 50" in find_line(" preview", output))
assert("preview 50 x 50" in find_line(" preview", output)), "\n"+result.stdout
print("success")
+17 -18
View File
@@ -15,10 +15,10 @@ version = sys.argv[4]
image = f"{image_dir}/TestImages/GammaChart.exr"
assert(os.path.isfile(exrmaketiled))
assert(os.path.isfile(exrinfo))
assert(os.path.isdir(image_dir))
assert(os.path.isfile(image))
assert(os.path.isfile(exrmaketiled)), "\nMissing " + exrmaketiled
assert(os.path.isfile(exrinfo)), "\nMissing " + exrinfo
assert(os.path.isdir(image_dir)), "\nMissing " + image_dir
assert(os.path.isfile(image)), "\nMissing " + image
fd, outimage = tempfile.mkstemp(".exr")
os.close(fd)
@@ -30,36 +30,35 @@ atexit.register(cleanup)
# no args = usage message
result = run ([exrmaketiled], stdout=PIPE, stderr=PIPE, universal_newlines=True)
print(" ".join(result.args))
assert(result.returncode != 0)
assert(result.stderr.startswith ("Usage: "))
assert(result.returncode != 0), "\n"+result.stderr
assert(result.stderr.startswith ("Usage: ")), "\n"+result.stderr
# -h = usage message
result = run ([exrmaketiled, "-h"], stdout=PIPE, stderr=PIPE, universal_newlines=True)
print(" ".join(result.args))
assert(result.returncode == 0)
assert(result.stdout.startswith ("Usage: "))
assert(result.returncode == 0), "\n"+result.stderr
assert(result.stdout.startswith ("Usage: ")), "\n"+result.stdout
result = run ([exrmaketiled, "--help"], stdout=PIPE, stderr=PIPE, universal_newlines=True)
print(" ".join(result.args))
assert(result.returncode == 0)
assert(result.stdout.startswith ("Usage: "))
assert(result.returncode == 0), "\n"+result.stderr
assert(result.stdout.startswith ("Usage: ")), "\n"+result.stdout
# --version
result = run ([exrmaketiled, "--version"], stdout=PIPE, stderr=PIPE, universal_newlines=True)
print(" ".join(result.args))
print(result.stdout)
assert(result.returncode == 0)
assert(result.stdout.startswith ("exrmaketiled"))
assert(version in result.stdout)
assert(result.returncode == 0), "\n"+result.stderr
assert(result.stdout.startswith ("exrmaketiled")), "\n"+result.stdout
assert(version in result.stdout), "\n"+result.stdout
result = run ([exrmaketiled, image, outimage], stdout=PIPE, stderr=PIPE, universal_newlines=True)
print(" ".join(result.args))
assert(result.returncode == 0)
assert(os.path.isfile(outimage))
assert(result.returncode == 0), "\n"+result.stderr
assert(os.path.isfile(outimage)), "\nMissing " + outimage
result = run ([exrinfo, "-v", outimage], stdout=PIPE, stderr=PIPE, universal_newlines=True)
print(" ".join(result.args))
assert(result.returncode == 0)
assert('tiled image has levels: x 1 y 1' in result.stdout)
assert(result.returncode == 0), "\n"+result.stderr
assert('tiled image has levels: x 1 y 1' in result.stdout), "\n"+result.stdout
print("success")
+19 -20
View File
@@ -15,26 +15,26 @@ version = sys.argv[4]
result = run ([exrmultipart], stdout=PIPE, stderr=PIPE, universal_newlines=True)
print(" ".join(result.args))
assert(result.returncode != 0)
assert("Usage:" in result.stderr)
assert(result.returncode != 0), "\n"+result.stderr
assert("Usage:" in result.stderr), "\n"+result.stderr
# -h = usage message
result = run ([exrmultipart, "-h"], stdout=PIPE, stderr=PIPE, universal_newlines=True)
print(" ".join(result.args))
assert(result.returncode == 0)
assert(result.stdout.startswith ("Usage: "))
assert(result.returncode == 0), "\n"+result.stderr
assert(result.stdout.startswith ("Usage: ")), "\n"+result.stdout
result = run ([exrmultipart, "--help"], stdout=PIPE, stderr=PIPE, universal_newlines=True)
print(" ".join(result.args))
assert(result.returncode == 0)
assert(result.stdout.startswith ("Usage: "))
assert(result.returncode == 0), "\n"+result.stderr
assert(result.stdout.startswith ("Usage: ")), "\n"+result.stdout
# --version
result = run ([exrmultipart, "--version"], stdout=PIPE, stderr=PIPE, universal_newlines=True)
print(" ".join(result.args))
assert(result.returncode == 0)
assert(result.stdout.startswith ("exrmultipart"))
assert(version in result.stdout)
assert(result.returncode == 0), "\n"+result.stderr
assert(result.stdout.startswith ("exrmultipart")), "\n"+result.stdout
assert(version in result.stdout), "\n"+result.stdout
image = f"{image_dir}/Beachball/multipart.0001.exr"
@@ -50,11 +50,11 @@ atexit.register(cleanup)
command = [exrmultipart, "-combine", "-i", f"{image}:0", f"{image}:1", "-o", outimage]
result = run (command, stdout=PIPE, stderr=PIPE, universal_newlines=True)
print(" ".join(result.args))
assert(result.returncode == 0)
assert(result.returncode == 0), "\n"+result.stderr
result = run ([exrinfo, outimage], stdout=PIPE, stderr=PIPE, universal_newlines=True)
print(" ".join(result.args))
assert(result.returncode == 0)
assert(result.returncode == 0), "\n"+result.stderr
# error: can't convert multipart images
command = [exrmultipart, "-convert", "-i", image, "-o", outimage]
@@ -66,19 +66,18 @@ assert (result.returncode != 0)
singlepart_image = f"{image_dir}/Beachball/singlepart.0001.exr"
command = [exrmultipart, "-convert", "-i", singlepart_image, "-o", outimage]
result = run (command, stdout=PIPE, stderr=PIPE, universal_newlines=True)
print(" ".join(result.args))
assert(result.returncode == 0)
assert(result.returncode == 0), "\n"+result.stderr
result = run ([exrinfo, outimage], stdout=PIPE, stderr=PIPE, universal_newlines=True)
print(" ".join(result.args))
assert(result.returncode == 0)
assert(result.returncode == 0), "\n"+result.stderr
# separate
# get part names from the multipart image
result = run ([exrinfo, image], stdout=PIPE, stderr=PIPE, universal_newlines=True)
print(" ".join(result.args))
assert(result.returncode == 0)
assert(result.returncode == 0), "\n"+result.stderr
part_names = {}
for p in result.stdout.split('\n part ')[1:]:
output = p.split('\n')
@@ -90,19 +89,19 @@ with tempfile.TemporaryDirectory() as tempdir:
command = [exrmultipart, "-separate", "-i", image, "-o", f"{tempdir}/separate"]
result = run (command, stdout=PIPE, stderr=PIPE, universal_newlines=True)
print(" ".join(result.args))
assert(result.returncode == 0)
assert(result.returncode == 0), "\n"+result.stderr
for i in range(1, 10):
s = f"{tempdir}/separate.{i}.exr"
result = run ([exrinfo, "-v", s], stdout=PIPE, stderr=PIPE, universal_newlines=True)
print(" ".join(result.args))
assert(result.returncode == 0)
assert(result.returncode == 0), "\n"+result.stderr
output = result.stdout.split('\n')
assert(output[1].startswith(' parts: 1'))
assert(output[1].startswith(' parts: 1')), "\n"+result.stdout
output[2].startswith(' part 1:')
part_name = output[2][9:]
part_number = str(i)
assert(part_names[part_number] == part_name)
assert(part_names[part_number] == part_name), "\n"+result.stdout
print("success")
+18 -14
View File
@@ -15,26 +15,26 @@ version = sys.argv[4]
result = run ([exrmultiview], stdout=PIPE, stderr=PIPE, universal_newlines=True)
print(" ".join(result.args))
assert(result.returncode != 0)
assert("Usage:" in result.stderr)
assert(result.returncode != 0), "\n"+result.stderr
assert("Usage:" in result.stderr), "\n"+result.stderr
# -h = usage message
result = run ([exrmultiview, "-h"], stdout=PIPE, stderr=PIPE, universal_newlines=True)
print(" ".join(result.args))
assert(result.returncode == 0)
assert(result.stdout.startswith ("Usage: "))
assert(result.returncode == 0), "\n"+result.stderr
assert(result.stdout.startswith ("Usage: ")), "\n"+result.stdout
result = run ([exrmultiview, "--help"], stdout=PIPE, stderr=PIPE, universal_newlines=True)
print(" ".join(result.args))
assert(result.returncode == 0)
assert(result.stdout.startswith ("Usage: "))
assert(result.returncode == 0), "\n"+result.stderr
assert(result.stdout.startswith ("Usage: ")), "\n"+result.stdout
# --version
result = run ([exrmultiview, "--version"], stdout=PIPE, stderr=PIPE, universal_newlines=True)
print(" ".join(result.args))
assert(result.returncode == 0)
assert(result.stdout.startswith ("exrmultiview"))
assert(version in result.stdout)
assert(result.returncode == 0), "\n"+result.stderr
assert(result.stdout.startswith ("exrmultiview")), "\n"+result.stdout
assert(version in result.stdout), "\n"+result.stdout
left_image = f"{image_dir}/TestImages/GammaChart.exr"
right_image = f"{image_dir}/TestImages/GrayRampsHorizontal.exr"
@@ -54,11 +54,15 @@ assert(result.returncode == 0)
result = run ([exrinfo, outimage], stdout=PIPE, stderr=PIPE, universal_newlines=True)
print(" ".join(result.args))
assert(result.returncode == 0)
assert('\'B\': half samp 1 1' in result.stdout)
assert('\'G\': half samp 1 1' in result.stdout)
assert('\'R\': half samp 1 1' in result.stdout)
assert('\'right.Y\': half samp 1 1' in result.stdout)
assert(result.returncode == 0), "\n"+result.stderr
try:
assert('\'B\': half samp 1 1' in result.stdout)
assert('\'G\': half samp 1 1' in result.stdout)
assert('\'R\': half samp 1 1' in result.stdout)
assert('\'right.Y\': half samp 1 1' in result.stdout)
except AssertionError:
print(result.stdout)
raise
print("success")
+53 -49
View File
@@ -13,9 +13,9 @@ exrinfo = sys.argv[2]
image_dir = sys.argv[3]
version = sys.argv[4]
assert(os.path.isfile(exrstdattr))
assert(os.path.isfile(exrinfo))
assert(os.path.isdir(image_dir))
assert(os.path.isfile(exrstdattr)), "\nMissing " + exrstdattr
assert(os.path.isfile(exrinfo)), "\nMissing " + exrinfo
assert(os.path.isdir(image_dir)), "\nMissing " + image_dir
fd, outimage = tempfile.mkstemp(".exr")
os.close(fd)
@@ -27,26 +27,26 @@ atexit.register(cleanup)
# no args = usage message
result = run ([exrstdattr], stdout=PIPE, stderr=PIPE, universal_newlines=True)
print(" ".join(result.args))
assert(result.returncode != 0)
assert(result.stderr.startswith ("Usage: "))
assert(result.returncode != 0), "\n"+result.stderr
assert(result.stderr.startswith ("Usage: ")), "\n"+result.stderr
# -h = usage message
result = run ([exrstdattr, "-h"], stdout=PIPE, stderr=PIPE, universal_newlines=True)
print(" ".join(result.args))
assert(result.returncode == 0)
assert(result.stdout.startswith ("Usage: "))
assert(result.returncode == 0), "\n"+result.stderr
assert(result.stdout.startswith ("Usage: ")), "\n"+result.stdout
result = run ([exrstdattr, "--help"], stdout=PIPE, stderr=PIPE, universal_newlines=True)
print(" ".join(result.args))
assert(result.returncode == 0)
assert(result.stdout.startswith ("Usage: "))
assert(result.returncode == 0), "\n"+result.stderr
assert(result.stdout.startswith ("Usage: ")), "\n"+result.stdout
# --version
result = run ([exrstdattr, "--version"], stdout=PIPE, stderr=PIPE, universal_newlines=True)
print(" ".join(result.args))
assert(result.returncode == 0)
assert(result.stdout.startswith ("exrstdattr"))
assert(version in result.stdout)
assert(result.returncode == 0), "\n"+result.stderr
assert(result.stdout.startswith ("exrstdattr")), "\n"+result.stdout
assert(version in result.stdout), "\n"+result.stdout
attrs = [
["-part", "0"],
@@ -84,7 +84,7 @@ for a in attrs:
result = run ([exrstdattr, a[0]], stdout=PIPE, stderr=PIPE, universal_newlines=True)
print(" ".join(result.args))
print(result.stderr)
assert(result.returncode != 0)
assert(result.returncode != 0), "\n"+result.stderr
command = [exrstdattr]
for a in attrs:
@@ -94,44 +94,48 @@ command += [image, outimage]
result = run (command, stdout=PIPE, stderr=PIPE, universal_newlines=True)
print(" ".join(result.args))
assert(result.returncode == 0)
assert(os.path.isfile(outimage))
assert(result.returncode == 0), "\n"+result.stderr
assert(os.path.isfile(outimage)), "\nMissing " + outimage
result = run ([exrinfo, "-v", outimage], stdout=PIPE, stderr=PIPE, universal_newlines=True)
print(" ".join(result.args))
assert(result.returncode == 0)
assert('adoptedNeutral: v2f [ 1.1, 2.2 ]' in result.stdout)
assert('altitude: float 6.5' in result.stdout)
assert('aperture: float 3.2' in result.stdout)
assert('capDate: string \'1999:12:31 23:59:59\'' in result.stdout)
assert('channels: chlist 1 channels' in result.stdout)
assert('\'Y\': half samp 1 1' in result.stdout)
assert('chromaticities: chromaticities r[1, 2] g[3, 4] b[5, 6] w[7, 8]' in result.stdout)
assert('comments: string \'blah blah blah\'' in result.stdout)
assert('compression: compression \'pxr24\' (0x05)' in result.stdout)
assert('dataWindow: box2i [ 0, 0 - 799 799 ] 800 x 800' in result.stdout)
assert('displayWindow: box2i [ 0, 0 - 799 799 ] 800 x 800' in result.stdout)
assert('envmap: envmap latlong' in result.stdout)
assert('expTime: float 4.3' in result.stdout)
assert('focus: float 5.4' in result.stdout)
assert('framesPerSecond: rational 48 / 1 (48)' in result.stdout)
assert('isoSpeed: float 2.1' in result.stdout)
assert('keyCode: keycode mfgc 1 film 2 prefix 3 count 4 perf_off 5 ppf 6 ppc 20' in result.stdout)
assert('latitude: float 7.6' in result.stdout)
assert('lineOrder: lineOrder 0 (increasing)' in result.stdout)
assert('longitude: float 8.7' in result.stdout)
assert('owner: string \'florian\'' in result.stdout)
assert('pixelAspectRatio: float 1.7' in result.stdout)
assert('screenWindowCenter: v2f [ 42, 43 ]' in result.stdout)
assert('screenWindowWidth: float 4.4' in result.stdout)
assert('test_float: float 4.2' in result.stdout)
assert('test_int: int 42' in result.stdout)
assert('test_string: string \'forty two\'' in result.stdout)
assert('timeCode: timecode time 305419896 user 878082192' in result.stdout)
assert('type: string \'scanlineimage\'' in result.stdout)
assert('utcOffset: float 9' in result.stdout)
assert('whiteLuminance: float 17.1' in result.stdout)
assert('wrapmodes: string \'clamp\'' in result.stdout)
assert('xDensity: float 10' in result.stdout)
assert(result.returncode == 0), "\n"+result.stderr
try:
assert('adoptedNeutral: v2f [ 1.1, 2.2 ]' in result.stdout)
assert('altitude: float 6.5' in result.stdout)
assert('aperture: float 3.2' in result.stdout)
assert('capDate: string \'1999:12:31 23:59:59\'' in result.stdout)
assert('channels: chlist 1 channels' in result.stdout)
assert('\'Y\': half samp 1 1' in result.stdout)
assert('chromaticities: chromaticities r[1, 2] g[3, 4] b[5, 6] w[7, 8]' in result.stdout)
assert('comments: string \'blah blah blah\'' in result.stdout)
assert('compression: compression \'pxr24\' (0x05)' in result.stdout)
assert('dataWindow: box2i [ 0, 0 - 799 799 ] 800 x 800' in result.stdout)
assert('displayWindow: box2i [ 0, 0 - 799 799 ] 800 x 800' in result.stdout)
assert('envmap: envmap latlong' in result.stdout)
assert('expTime: float 4.3' in result.stdout)
assert('focus: float 5.4' in result.stdout)
assert('framesPerSecond: rational 48 / 1 (48)' in result.stdout)
assert('isoSpeed: float 2.1' in result.stdout)
assert('keyCode: keycode mfgc 1 film 2 prefix 3 count 4 perf_off 5 ppf 6 ppc 20' in result.stdout)
assert('latitude: float 7.6' in result.stdout)
assert('lineOrder: lineOrder 0 (increasing)' in result.stdout)
assert('longitude: float 8.7' in result.stdout)
assert('owner: string \'florian\'' in result.stdout)
assert('pixelAspectRatio: float 1.7' in result.stdout)
assert('screenWindowCenter: v2f [ 42, 43 ]' in result.stdout)
assert('screenWindowWidth: float 4.4' in result.stdout)
assert('test_float: float 4.2' in result.stdout)
assert('test_int: int 42' in result.stdout)
assert('test_string: string \'forty two\'' in result.stdout)
assert('timeCode: timecode time 305419896 user 878082192' in result.stdout)
assert('type: string \'scanlineimage\'' in result.stdout)
assert('utcOffset: float 9' in result.stdout)
assert('whiteLuminance: float 17.1' in result.stdout)
assert('wrapmodes: string \'clamp\'' in result.stdout)
assert('xDensity: float 10' in result.stdout)
except AssertionError:
print(result.stdout)
raise
print("success")
+26 -13
View File
@@ -1,29 +1,42 @@
# SPDX-License-Identifier: BSD-3-Clause
# Copyright Contributors to the OpenEXR Project.
message(STATUS "Building OpenEXR python bindings")
if(NOT "${CMAKE_PROJECT_NAME}" STREQUAL "OpenEXR")
cmake_minimum_required(VERSION 3.12)
project(PyOpenEXR)
find_package(OpenEXR)
endif()
get_cmake_property(_variableNames VARIABLES)
list (SORT _variableNames)
foreach (_variableName ${_variableNames})
message(STATUS "${_variableName}=${${_variableName}}")
endforeach()
find_package(Python COMPONENTS Interpreter Development.Module REQUIRED)
add_library (PyOpenEXR SHARED OpenEXR.cpp)
python_add_library (PyOpenEXR MODULE OpenEXR.cpp)
set (Python_ADDITIONAL_VERSIONS 3)
find_package (PythonLibs REQUIRED)
find_package (PythonInterp REQUIRED)
target_link_libraries (PyOpenEXR PRIVATE "${Python_LIBRARIES}" OpenEXR::OpenEXR)
include_directories ("${PYTHON_INCLUDE_DIRS}")
# The python module should be called "OpenEXR.so", not "PyOpenEXR.so",
# but "OpenEXR" is taken as a library name by the main lib, so specify
# the name explicitly here.
set_target_properties (PyOpenEXR PROPERTIES PREFIX "")
set_target_properties (PyOpenEXR PROPERTIES OUTPUT_NAME "OpenEXR")
set_target_properties (PyOpenEXR PROPERTIES SUFFIX ".so")
target_link_libraries (PyOpenEXR "${PYTHON_LIBRARIES}" OpenEXR::OpenEXR)
configure_file(Imath.py ${CMAKE_CURRENT_BINARY_DIR}/Imath.py COPYONLY)
set(PYTHON_INSTALL_DIR "python/OpenEXR")
if(SKBUILD)
set(PYTHON_INSTALL_DIR ${SKBUILD_PLATLIB_DIR})
endif()
install(TARGETS PyOpenEXR DESTINATION ${PYTHON_INSTALL_DIR} COMPONENT python)
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/Imath.py DESTINATION ${PYTHON_INSTALL_DIR} COMPONENT python)
if(BUILD_TESTING AND OPENEXR_TEST_PYTHON)
add_test(OpenEXR.PyOpenEXR pytest ${CMAKE_CURRENT_SOURCE_DIR}/tests)
set_tests_properties(OpenEXR.PyOpenEXR PROPERTIES
ENVIRONMENT "PYTHONPATH=${CMAKE_CURRENT_BINARY_DIR}"
)
endif()
+8 -8
View File
@@ -92,8 +92,8 @@ typedef int Py_ssize_t;
#endif
using namespace std;
using namespace Imf;
using namespace Imath;
using namespace OPENEXR_IMF_NAMESPACE;
using namespace IMATH_NAMESPACE;
static PyObject *OpenEXR_error = NULL;
static PyObject *pModuleImath;
@@ -155,7 +155,7 @@ C_IStream::read (char c[], int n)
memcpy(c, PyString_AsString(data), PyString_Size(data));
Py_DECREF(data);
} else {
throw Iex::InputExc("file read failed");
throw IEX_NAMESPACE::InputExc("file read failed");
}
return 0;
}
@@ -177,7 +177,7 @@ C_IStream::tellg ()
Py_DECREF(rv);
return (Int64)t;
} else {
throw Iex::InputExc("tell failed");
throw IEX_NAMESPACE::InputExc("tell failed");
}
}
@@ -188,7 +188,7 @@ C_IStream::seekg (Int64 pos)
if (data != NULL) {
Py_DECREF(data);
} else {
throw Iex::InputExc("seek failed");
throw IEX_NAMESPACE::InputExc("seek failed");
}
}
@@ -220,7 +220,7 @@ C_OStream::write (const char*c, int n)
if (data != NULL) {
Py_DECREF(data);
} else {
throw Iex::InputExc("file write failed");
throw IEX_NAMESPACE::InputExc("file write failed");
}
}
@@ -241,7 +241,7 @@ C_OStream::tellp ()
Py_DECREF(rv);
return (Int64)t;
} else {
throw Iex::InputExc("tell failed");
throw IEX_NAMESPACE::InputExc("tell failed");
}
}
@@ -252,7 +252,7 @@ C_OStream::seekp (Int64 pos)
if (data != NULL) {
Py_DECREF(data);
} else {
throw Iex::InputExc("seek failed");
throw IEX_NAMESPACE::InputExc("seek failed");
}
}
+142
View File
@@ -0,0 +1,142 @@
<!-- SPDX-License-Identifier: BSD-3-Clause -->
<!-- Copyright (c) Contributors to the OpenEXR Project -->
[![License](https://img.shields.io/github/license/AcademySoftwareFoundation/openexr)](https://github.com/AcademySoftwareFoundation/openexr/blob/main/LICENSE.md)
[![CII Best Practices](https://bestpractices.coreinfrastructure.org/projects/2799/badge)](https://bestpractices.coreinfrastructure.org/projects/2799)
[![OpenSSF Scorecard](https://api.securityscorecards.dev/projects/github.com/AcademySoftwareFoundation/openexr/badge)](https://securityscorecards.dev/viewer/?uri=github.com/AcademySoftwareFoundation/openexr)
[![Build Status](https://github.com/AcademySoftwareFoundation/openexr/workflows/CI/badge.svg)](https://github.com/AcademySoftwareFoundation/openexr/actions?query=workflow%3ACI)
[![Analysis Status](https://github.com/AcademySoftwareFoundation/openexr/workflows/Analysis/badge.svg)](https://github.com/AcademySoftwareFoundation/openexr/actions?query=workflow%3AAnalysis)
[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=AcademySoftwareFoundation_openexr&metric=alert_status)](https://sonarcloud.io/dashboard?id=AcademySoftwareFoundation_openexr)
# OpenEXR
OpenEXR provides the specification and reference implementation of the
EXR file format, the professional-grade image storage format of the
motion picture industry.
The purpose of EXR format is to accurately and efficiently represent
high-dynamic-range scene-linear image data and associated metadata,
with strong support for multi-part, multi-channel use cases.
OpenEXR is widely used in host application software where accuracy is
critical, such as photorealistic rendering, texture access, image
compositing, deep compositing, and DI.
## OpenEXR Project Mission
The goal of the OpenEXR project is to keep the EXR format reliable and
modern and to maintain its place as the preferred image format for
entertainment content creation.
Major revisions are infrequent, and new features will be carefully
weighed against increased complexity. The principal priorities of the
project are:
* Robustness, reliability, security
* Backwards compatibility, data longevity
* Performance - read/write/compression/decompression time
* Simplicity, ease of use, maintainability
* Wide adoption, multi-platform support - Linux, Windows, macOS, and others
OpenEXR is intended solely for 2D data. It is not appropriate for
storage of volumetric data, cached or lit 3D scenes, or more complex
3D data such as light fields.
The goals of the Imath project are simplicity, ease of use,
correctness and verifiability, and breadth of adoption. Imath is not
intended to be a comprehensive linear algebra or numerical analysis
package.
## Python Module
The OpenEXR python module provides rudimentary support for reading and
writing basic scanline image data. Many features of the file format
are not yet supported, including:
- Writing of tiled images
- Multiresoltion images
- Deep image data
- Some attribute types
- Nonunity channel sampling frequencies
- No support for interleaved channel data
## Project Governance
OpenEXR is a project of the [Academy Software
Foundation](https://www.aswf.io). See the project's [governance
policies](https://github.com/AcademySoftwareFoundation/openexr/blob/main/GOVERNANCE.md), [contribution guidelines](https://github.com/AcademySoftwareFoundation/openexr/blob/main/CONTRIBUTING.md), and [code of conduct](https://github.com/AcademySoftwareFoundation/openexr/blob/main/CODE_OF_CONDUCT.md)
for more information.
# Quick Start
The "hello, world" image writer:
import OpenEXR
width = 10
height = 10
size = width * height
h = OpenEXR.Header(width,height)
h['channels'] = {'R' : Imath.Channel(FLOAT),
'G' : Imath.Channel(FLOAT),
'B' : Imath.Channel(FLOAT),
'A' : Imath.Channel(FLOAT)}
o = OpenEXR.OutputFile("hello.exr", h)
r = array('f', [n for n in range(size*0,size*1)]).tobytes()
g = array('f', [n for n in range(size*1,size*2)]).tobytes()
b = array('f', [n for n in range(size*2,size*3)]).tobytes()
a = array('f', [n for n in range(size*3,size*4)]).tobytes()
channels = {'R' : r, 'G' : g, 'B' : b, 'A' : a}
o.writePixels(channels)
o.close()
# Community
* **Ask a question:**
- Email: openexr-dev@lists.aswf.io
- Slack: [academysoftwarefdn#openexr](https://academysoftwarefdn.slack.com/archives/CMLRW4N73)
* **Attend a meeting:**
- Technical Steering Committee meetings are open to the
public, fortnightly on Thursdays, 1:30pm Pacific Time.
- Calendar: https://lists.aswf.io/g/openexr-dev/calendar
- Meeting notes: https://wiki.aswf.io/display/OEXR/TSC+Meetings
* **Report a bug:**
- Submit an Issue: https://github.com/AcademySoftwareFoundation/openexr/issues
* **Report a security vulnerability:**
- Email to security@openexr.com
* **Contribute a Fix, Feature, or Improvement:**
- Read the [Contribution Guidelines](https://github.com/AcademySoftwareFoundation/openexr/blob/main/CONTRIBUTING.md) and [Code of Conduct](https://github.com/AcademySoftwareFoundation/openexr/blob/main/CODE_OF_CONDUCT.md)
- Sign the [Contributor License
Agreement](https://contributor.easycla.lfx.linuxfoundation.org/#/cla/project/2e8710cb-e379-4116-a9ba-964f83618cc5/user/564e571e-12d7-4857-abd4-898939accdd7)
- Submit a Pull Request: https://github.com/AcademySoftwareFoundation/openexr/pulls
# Resources
- Website: http://www.openexr.com
- Technical documentation: https://openexr.readthedocs.io
- Porting help: [OpenEXR/Imath Version 2.x to 3.x Porting Guide](https://openexr.readthedocs.io/en/latest/PortingGuide.html)
- Reference images: https://github.com/AcademySoftwareFoundation/openexr-images
- Security policy: [SECURITY.md](https://github.com/AcademySoftwareFoundation/openexr/blob/main/SECURITY.md)
- Release notes: [CHANGES.md](https://github.com/AcademySoftwareFoundation/openexr/blob/main/CHANGES.md)
- Contributors: [CONTRIBUTORS.md](https://github.com/AcademySoftwareFoundation/openexr/blob/main/CONTRIBUTORS.md)
# License
OpenEXR is licensed under the [BSD-3-Clause license](https://github.com/AcademySoftwareFoundation/openexr/blob/main/LICENSE.md).
-12
View File
@@ -1,12 +0,0 @@
--- CMakeLists.txt 2023-07-14 08:51:51.375958419 +0200
+++ CMakeLists.txt 2023-07-14 08:47:47.854104430 +0200
@@ -183,7 +183,8 @@
endif()
set_target_properties(libdeflate_static PROPERTIES
OUTPUT_NAME ${STATIC_LIB_NAME}
- PUBLIC_HEADER libdeflate.h)
+ PUBLIC_HEADER libdeflate.h
+ POSITION_INDEPENDENT_CODE ON)
target_include_directories(libdeflate_static PUBLIC ${LIB_INCLUDE_DIRS})
target_compile_definitions(libdeflate_static PRIVATE ${LIB_COMPILE_DEFINITIONS})
target_compile_options(libdeflate_static PRIVATE ${LIB_COMPILE_OPTIONS})
@@ -0,0 +1,106 @@
# Copyright Contributors to the MaterialX Project
# SPDX-License-Identifier: Apache-2.0
# copied from: https://github.com/AcademySoftwareFoundation/MaterialX/blob/main/python/mtx_skbuild_plugin.py
# Modifications Copyright (c) Contributors to the OpenEXR Project.
"""
This is a custom scikit-build-core plugin that will
fetch the OpenEXR version from the CMake project.
"""
import os
import tempfile
import subprocess
from pathlib import Path
from typing import FrozenSet, Dict, Optional, Union, List
from scikit_build_core.file_api.query import stateless_query
from scikit_build_core.file_api.reply import load_reply_dir
def dynamic_metadata(
fields: FrozenSet[str],
settings: Optional[Dict[str, object]] = None,
) -> Dict[str, Union[str, Dict[str, Optional[str]]]]:
print("openexr_skbuild_plugin: Computing OpenEXR version from CMake...")
if fields != "version":
msg = f"Only the 'version' field is supported: fields={fields}"
raise ValueError(msg)
if settings:
msg = "No inline configuration is supported"
raise ValueError(msg)
if "OPENEXR_RELEASE_CANDIDATE_TAG" in os.environ:
# e.g. "v3.1.2-rc4"
#
# If OPENEXR_RELEASE_CANDIDATE_TAG is set,
# the build is for a publish to test.pypi.org. Multiple test
# publishes may happen in the course of preparing for a
# release, but published packages require unique
# names/versions, so use the release candidate tag as the
# version (minus the leading 'v'),
rct = os.environ["OPENEXR_RELEASE_CANDIDATE_TAG"]
version = rct[1:]
else:
current_dir = os.path.dirname(__file__)
with tempfile.TemporaryDirectory() as tmpdir:
# We will use CMake's file API to get the version
# instead of parsing the CMakeLists files.
# First generate the query folder so that CMake can generate replies.
reply_dir = stateless_query(Path(tmpdir))
# Run cmake (configure). CMake will generate a reply automatically.
try:
subprocess.run(
[
"cmake",
"-S",
current_dir + "../../../..",
"-B",
tmpdir,
"-DOPENEXR_BUILD_LIBS=OFF",
],
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
check=True,
text=True,
)
except subprocess.CalledProcessError as exc:
print(exc.stdout)
raise RuntimeError(
"Failed to configure project to get the version"
) from exc
# Get the generated replies.
index = load_reply_dir(reply_dir)
# Get the version from the CMAKE_PROJECT_VERSION variable.
entries = [
entry
for entry in index.reply.cache_v2.entries
if entry.name == "CMAKE_PROJECT_VERSION"
]
if not entries:
raise ValueError("Could not find OpenEXR version from CMake project")
if len(entries) > 1:
raise ValueError("More than one entry for CMAKE_PROJECT_VERSION found...")
version = entries[0].value
print("openexr_skbuild_plugin: Computed version: {0}".format(version))
return version
def get_requires_for_dynamic_metadata(
_settings: Optional[Dict[str, object]] = None,
) -> List[str]:
return ["cmake"]
-81
View File
@@ -1,81 +0,0 @@
from setuptools import setup, Extension
import os
import platform
import re
DESC = """Python bindings for the OpenEXR image file format.
This is a script to autobuild the wheels using github actions. Please, do not
use it manually
If you detect any problem, please feel free to report the issue on the GitHub
page:
https://github.com/AcademySoftwareFoundation/openexr/issues
"""
version = []
with open('src/lib/OpenEXRCore/openexr_version.h', 'r') as f:
txt = f.read()
for name in ('MAJOR', 'MINOR', 'PATCH'):
version.append(re.search(
f'VERSION_{name} ([0-9]*)', txt).group(0).split(' ')[-1])
version_major, version_minor, version_patch = version
version = f"{version_major}.{version_minor}.{version_patch}"
libs=[]
libs_static=[f'OpenEXR-{version_major}_{version_minor}',
f'IlmThread-{version_major}_{version_minor}',
f'Iex-{version_major}_{version_minor}',
f'Imath-{version_major}_{version_minor}',
f'OpenEXRCore-{version_major}_{version_minor}'
]
definitions = [('PYOPENEXR_VERSION_MAJOR', f'{version_major}'),
('PYOPENEXR_VERSION_MINOR', f'{version_minor}'),
('PYOPENEXR_VERSION_PATCH', f'{version_patch}'),]
if platform.system() == "Windows":
definitions = [('PYOPENEXR_VERSION', f'\\"{version}\\"')]
extra_compile_args = []
if platform.system() == 'Darwin':
extra_compile_args += ['-std=c++11',
'-Wc++11-extensions',
'-Wc++11-long-long']
libs_dir = "./openexr.install/lib/"
if not os.path.isdir(libs_dir):
libs_dir = "./openexr.install/lib64/"
if platform.system() == "Windows":
extra_link_args = [libs_dir + lib + ".lib"
for lib in libs_static]
extra_link_args = extra_link_args + [
"ws2_32.lib", "dbghelp.lib", "psapi.lib", "kernel32.lib", "user32.lib",
"gdi32.lib", "winspool.lib", "shell32.lib", "ole32.lib",
"oleaut32.lib", "uuid.lib", "comdlg32.lib", "advapi32.lib"]
else:
extra_link_args = [libs_dir + "lib" + lib + ".a"
for lib in libs_static]
setup(name='OpenEXR',
author = 'Contributors to the OpenEXR Project',
author_email = 'info@openexr.com',
url = 'https://github.com/AcademySoftwareFoundation/openexr',
description = "Python bindings for the OpenEXR image file format",
long_description = DESC,
version=version,
ext_modules=[
Extension('OpenEXR',
['OpenEXR.cpp'],
language='c++',
define_macros=definitions,
include_dirs=['./openexr.install/include/OpenEXR',
'./openexr.install/include/Imath',],
libraries=libs,
extra_compile_args=extra_compile_args,
extra_link_args=extra_link_args,
)
],
py_modules=['Imath'],
)
+5 -1
View File
@@ -1,5 +1,9 @@
import pytest
#!/usr/bin/env python3
# SPDX-License-Identifier: BSD-3-Clause
# Copyright (c) Contributors to the OpenEXR Project.
import pytest
def test_import():
import OpenEXR
+1 -3
View File
@@ -11,10 +11,8 @@ import os
import random
from array import array
import Imath
import OpenEXR
test_dir = os.path.dirname(os.path.abspath(__file__))
import Imath
FLOAT = Imath.PixelType(Imath.PixelType.FLOAT)
UINT = Imath.PixelType(Imath.PixelType.UINT)
+31
View File
@@ -7,6 +7,37 @@
The OpenEXR API
###############
There are two separate, independent APIs for reading and writing EXR
image files: the traditional C++ API, and the newer C API, referred to
as ``OpenEXRCore``. The C++ API is the original, widely-used interface
first released in 2003. It consists of the ``OpenEXR``, ``Iex``, and
``IlmThread`` libraries, with functions and classes in the ``Imf::``
namespace (or "image format", the original the name of the library at
ILM prior to its public release).
The ``OpenEXRCore`` library, written primarily by Kimball Thurston at
Weta Digital, was introduced in July, 2021. This C-language
implementation of the file format is the result of a significant
re-thinking of image file I/O and access to image data. The
``OpenEXRCore`` library provides thread-safe, non-blocking access to
files, which was not possible with the older C++ API, where the
framebuffer management is separate from read requests. This new
low-level API allows applications to do custom unpacking of EXR data,
such as on the GPU, while still benefiting from efficient I/O, file
validation, and other semantics. It provides efficient direct access
to EXR files in texturing applications. This C library also introduces
an easier path to implementing OpenEXR bindings in other languages,
such as Rust.
Currently, the two libraries and APIs sit alongside each other,
although in future OpenEXR releases, the C++ API will migrate to use
the new core in stages. It is not the intention to entirely deprecate
the C++ API, nor must all applications re-implement EXR I/O in terms
of the C library. The C API does not, and will not, provide the rich
set of utility classes that exist in the C++ layer. The
``OpenEXRCore`` library simply offers new functionality for specialty
applications seeking the highest possible performance.
.. toctree::
:caption: API
:maxdepth: 2
+407
View File
@@ -0,0 +1,407 @@
..
SPDX-License-Identifier: BSD-3-Clause
Copyright Contributors to the OpenEXR Project.
OpenEXR Deep IDs Specification
##############################
Introduction
============
Deep IDs are primarily used in compositing applications to select
objects in images:
- The 3D renderer stores multiple ids per
pixel in the final OpenEXR file as well a scene manifest providing a mapping to a
human-readable identifier, such as an object or material name.
- The compositing application reads in the deep IDs and implements a
UI for the artist to create a selection based on the manifest data.
- The selected IDs will then be used to create a deep (per-sample) or
shallow (per-pixel) mask for later use.
Typical uses include:
- Grading selected objects.
- Removing a certain percentage of particles from a snow or dust motes
render.
- Adding consistent variations to different individuals in a crowd or to different
trees in a forest by applying an ID-based color correction.
Deep IDs can also be used for debugging:
- To identify an object/material which renders incorrectly.
- To collect all visible IDs over a sequence so as to prune scene
objects that are never on screen.
Pros
----
- Each pixel can contain an arbitrary number of IDs (0 included),
allowing for perfect mattes.
- Deep IDs are combined with transparency data to support anti-aliasing,
motion blur and depth of field.
- A deep image stores the color of the individual objects as well as their
individual alpha, so an object extracted by ID will have the correct color
and alpha values along the edges.
Cons
----
- IDs cannot be directly inspected with a simple image viewer.
- Hash collisions can make multiple objects map to the same ID.
- DeepIDs require software to support deepscanline and/or deeptile OpenEXR image types.
Cryptomatte comparison
----------------------
Cryptomatte [1]_ is widely adopted ID scheme similar to that described here, with a few differences:
- Cryptomatte uses a similar concept of a manifest, which is stored as JSON in a string attribute.
- Cryptomatte allocates a fixed size array for IDs, imposing a maximum number of IDs in one pixel.
Setting this too large requires a lot of memory; setting it too small can cause
noise if an object is selected which has been discarded from some pixels.
- Cryptomatte uses the ``scanlineimage`` or ``tiledimage`` EXR format, rather than ``deepscanline`` or ``deeptile``
to simplify adoption by applications already supporting these EXR types.
Similarly, IDs are stored in the ``R`` and ``B`` channels as FLOAT rather than UINT types,
though are intended to be interpreted as UINT. Although these
channels are named as color channels, care must be taken not to color manage or otherwise modify them,
and tools which re-write Cryptomatte files should ensure they are written as FLOAT rather than HALF.
- Cryptomatte allows slightly less than 2\ :sup:`32` different IDs; the Deep ID scheme allows for 2\ :sup:`32` or 2\ :sup:`64` different IDs.
This reduces the chance of a hash collision.
- Cryptomatte only stores *coverage* of each object. This can be used to generate a mask for an object to apply
a selective grade. Because the depth ordering, transparency and color of each object is not stored, objects isolated
using Cryptomatte will have contaminated edge colors and incorrect alpha values.
Comparisons of typical images suggest Cryptomatte and deep images with IDs are similar size on disk,
even though deep images carry more information,
but generally deep images are processed faster since they take less space when decompressed.
It is possible to convert a Deep ID image into a Cryptomatte image, which may be a path to adoption for
tools which read Cryptomatte but not Deep IDs. Conversion in the opposite direction is also possible,
but the resultant deep ID image can only be used for coverage computation.
As such, the Deep ID approach can be considered an evolution of Cryptomatte, supporting and extending all the features of Cryptomatte,
but with a more formal specification and flexible storage.
Deep ID Basics
==============
The deep OpenEXR files need to contain the following elements:
- Deep IDs stored using one or two ``Imf::UINT`` (``uint32_t``)
channels:
- A single channel stores 32 bit hashes.
- A pair of channels store 64 bit hashes.
- Manifest data stored in the file’s metadata as an attribute, or in a side-car file.
- **NOTE**: OpenEXR 3.0+ provides a data structure
and attribute for efficient storage. For more details see :ref:`idmanifest-label`
Sample storage
==============
Terminology
-----------
- **Channel**: Every deep image contains many named channels (``R``,
``G``, ``B``, ``A``, ``Z``, ``id``, etc)
- **Pixel**: A discrete image element containing 0 or more deep
``samples``.
- **Sample**: A container storing the value of each channel at a
specific depth.
- **Id**: A unique numerical identifier mapping to an ``item``.
- **Kind**: a named ``id`` category (model name, shader name, asset management URI, etc).
- **Manifest**: a data structure mapping each id to one ``item``.
- **Item**: an entity (object, material, etc) represented by:
- Artist friendly strings in the manifest, e.g. “Chair16” (one string of each ``kind``)
- A collection of ``samples`` matching a particular ``id`` in the image.
Principles
----------
1. Deep IDs have a single ``id`` of each ``kind`` per ``sample``, and
every ``item`` in the image has a consistent ``id`` for all its
``samples``.
2. If two different ``items`` overlap the same space, they will be
stored in separate ``samples`` (which themselves may or may not
overlap).
3. Some ``id`` may not have an associated string, like ``particleid``,
and still be useful (for example through image-based picking).
4. In complex cases like an instanced particle system, each ``sample``
may have an ``instanceid``, ``particleid``, and ``id``/``objectid``.
Standard ID Channel names
-------------------------
============== ======================================
Name Contents
============== ======================================
**id** by default, an object identifier [2]_
**objectid** identifier of an object
**materialid** identifier of an material
**particleid** identifier of a particle
**instanceid** identifier of an instanced object
============== ======================================
To limit the risk of hash collision, a ``uint64_t`` bits can be encoded
with two ``uint32_t`` channels. The convention is then to suffix the
channel name with ``0`` or ``1`` to indicate the channels storing the
least and the most significant 32 bits respectively,
e.g. ``particleid0`` and ``particleid1`` or ``particle.id0`` and ``particle.id1``
When sorted alphanumerically, the channel storing the most significant bits should appear immediately
after the channel storing the least significant bits. See appendix for details.
ID generation
-------------
Any non-uint32 identifier can be hashed to generate an id.
- ``uint32_t`` hashes can be generated with ``MurmurHash3_x86_32``.
- ``uint64_t`` hashes can be generated with the top 8 bytes of
``MurmurHash3_x64_128``.
OpenEXR 3.x offers two convenience functions to hash strings [3]_:
``IDManifest::MurmurHash32`` and ``IDManifest::MurmurHash64``.
The Deep ID scheme does not require these hash schemes to be used. For example if an asset management
system already generates hashes, those can be used instead. It is also not required to use a hash scheme
to map between names and numeric identifiers. For example the IDs could be generated in the order that they
are stored in the source geometry file. Such an approach would avoid hash collisions,
but would not generate IDs consistently across different shots or scenes.
Multivariate IDs
----------------
Hashing more than one ``kind`` (i.e. object + material) limits storage
requirements without impairing the artist’s ability to generate
arbitrary mattes.
For examples hashing the object and material names together is common
practice. In that case, a single ``id`` will map to two ``kinds`` in the
manifest, providing more flexibility at the cost of a slightly increased
risk of hash collision.
Manifest data
-------------
The manifest contains the human-readable data corresponding to a given
hash. It is a big table of strings that may require more storage than
the actual image data. It can be stored using the following mechanisms:
.. _idmanifest-label:
OpenEXR idmanifest container
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Since OpenEXR 3.0, there is a new standard ``idmanifest`` attribute
using a ``CompressedIDManifest`` metadata type, specially designed to
transport manifest data efficiently [4]_. It is optimized to reduce the storage space required,
and is the most standard approach.
The utility ``exrmanifest`` outputs the manifest of EXR images as plain text.
Note that use of the ``idmanifest`` attribute is not limited to the Deep ID scheme.
Other schemes - such as Cryptomatte - could also use the attribute to encode manifest
and other related data, to save storage space.
(Indeed, the idmanifest was in part derived from Cryptomatte’s metadata scheme for this purpose)
OpenEXR string container
^^^^^^^^^^^^^^^^^^^^^^^^
The manifest can be stored in ``string`` or ``stringvector`` attributes,
but this is not very efficient and may significantly increase file size.
Side-car files
^^^^^^^^^^^^^^
Alternatively, the manifest may be stored in a separate file, with an OpenEXR attributes,
a database or a file naming convention used to associate one or more OpenEXR files
with the corresponding sidecar file. Sidecar files can be advantageous because
they can be shared between different images, and also updated as more content is being rendered.
Such schemes are not supported by the OpenEXR library, nor are they defined here,
since that is outside the scope of the OpenEXR file format specification.
Although sidecar files may be appropriate for temporary usage, it is strongly recommended
that the embedded manifest is used in OpenEXR images which are to be shared between different companies
or for archival usage.
Deep Compositing and Deep IDs
=============================
Deep Compositing workflows allow objects to be rendered in separate passes and then merged together as a post process.
For example, it is possible to render a volumetric element such as a smoke, and merge other objects into it.
Deep images containing such volume renders may need to become very large in order to capture all the detail required for the merge to work correctly.
Deep IDs are a powerful addition to deep compositing workflows, as they allow for greater control of individual elements within a single render.
For this to work effectively, the ID passes must be included in the same deep image as that used for deep compositing.
It is tempting to generate two separate deep images for each rendered object, one containing color and one containing the IDs,
particularly since this is a common approach with Cryptomatte workflow.
However, creating separate files complicates deep compositing with deep IDs, since it is harder to associate each individual object's ID with its color.
Very little extra storage is required to add an ID channel to a deep image.
Deep IDs are still useful where full deep compositing workflows are not used, to allow color correction of individual objects.
In that case, the deep image need not be large. In the case of a volume, a single sample can be used to indicate the volume's ID, color and transparency.
Renderers can produce these efficient passes by combining together adjacent deep samples that have the same set of id channels.
The object color (RGB) channels can also be omitted to reduce file size at the cost of loss of fidelity along object edges.
Tools which do not support full deep compositing workflows could still support deep images for the limited use of ID selection.
For example an image editing package may have a "load selected objects from deep image" tool which allows the desired object to be selected,
then processes that into a regular non-deep image or mask for editing.
Example code
============
OpenEXR provides two example tools, ``deepidexample`` and ``deepidselect``.
Compiled tools will be found in the ``src/examples`` folder in the build directory. They are not installed.
DeepIDExample
-------------
``deepidexample`` creates a deep image with multiple objects (two different shapes at one of three sizes),
in one of seven colors. It is intended as a tool for generating test sequences and as an example of code
that generates an image with deep IDs and a manifest.
``deepidexample`` can generate a sequence of frames, to help test that the IDs are consistently
generated and selected. Specify ``--frame`` for the frame number. The animation cycles every 100 frames.
This ``bash`` command generates a sequence of frames:
.. code:: bash
for x in `seq 1000 1100` ; do ./deepidexample --frame $x output.$x.deep.exr ; done
Run ``deepidexample`` to see further options.
DeepIDSelect
------------
``deepidselect`` selects individual objects within a deep file, and outputs just those objects.
It is intended to serve as an example of parsing idmanifests to find compile a list of IDs which
match a given substring, and using those ids to identify samples. Its usage is not limited solely
to files created by deepidexample; it should handle files with arbitrary channel names and manifests.
deepidselect supports the ``id64`` scheme with the ``--64`` flag.
In basic usage, specify ``input.deep.exr (matches) output.deep.exr``
``matches`` is a list of one or more separate strings. All objects whose names contain any of the
given substring will be included in output.deep.exr (it is a logical OR of the arguments)
The ``--and`` can be used to force matching of (one or more of) the following match as well as the previous.
For example, ``blue --and circle`` will match any object which is both blue, and a circle.
``blue green --and big small --and circle`` will match blue or green objects,
and which are big or small, and which are circles.
This could also be read as `( blue or green ) and ( big or small ) and ( circle )`
Each match can be limited to a given component name by specifying ``component:match``.
For example ``model:bl`` will match objects whose model is ``blob`` but not ones whose material is ``blue``.
Specifying a channel name followed by a number will select the object by number, rather than by name.
For example, ``particleid:12`` will select the object with particle ID 12.
(Note that this feature means it is not possible to have a purely numeric substring match with this tool)
``--mask`` outputs a shallow single channel image which indicating the relative coverage of each pixel
for the selected object. For schemes where the deep image only contains ID (and alpha) information,
but does not store color, this can be used to grade only the selected object.
Edge contamination may be observed along transparent edges of a selected object, if an object behind it is not selected.
To keep the code simple, ``deepidselect`` is only a minimal example of string matching against ID manifests.
For example, it doesn't support regular expressions, or more advanced Boolean logic including negative matches.
Appendix
========
64 to 2 x 32 bits conversion and back
-------------------------------------
To limit the risk of hash collision, a ``uint64_t`` can be encoded in 2
``uint32_t`` channels, like ``materialid`` and ``materialid2``, using
little-endian byte ordering.
.. code:: cpp
#include <cstdint>
#include <iostream>
#include <iomanip>
int main()
{
using namespace std;
// uint 64 input
uint64_t x = 0x12345678'87654321ULL;
cout << setw(20) << "uint64 input: " << hex << x << endl;
// Convert one uint 64 -> two uint 32
uint32_t lo = uint32_t(x);
uint32_t hi = uint32_t(x >> 32);
cout << setw(20) << "uint32 low: " << hex << lo << " high: " << hi << endl;
// Convert two uint32 -> one uint64
uint64_t y = (uint64_t(hi) << 32) | lo;
cout << setw(20) << "uint64 recombined: " << hex << y << endl;
}
Output:
::
uint64 input: 1234567887654321
uint32 low: 87654321 high: 12345678
uint64 recombined: 1234567887654321
Computing a shallow mask from Deep IDs
--------------------------------------
A shallow mask is a pixel-level mask that can be used with non-deep
compositing operators.
Here is the pseudo-code to correctly compute an ID selection mask for a
single pixel:
.. code:: python
total_combined_alpha = 0.0
mask_alpha = 0.0
sorted_pixel = sort_pixel_front_to_back(input_pixel)
foreach(sample in sorted_pixel):
if id_is_in_selection(sample.id):
mask_alpha += sample.alpha * (1.0 - total_combined_alpha)
total_combined_alpha += sample.alpha * (1.0 - total_combined_alpha)
if total_combined_alpha == 0.0:
return 0.0
else:
return mask_alpha / total_combined_alpha
.. [1]
See `Cryptomatte <https://github.com/Psyop/Cryptomatte>`__ on github
.. [2]
See `OpenEXR reserved channel
names <https://openexr.com/en/latest/TechnicalIntroduction.html#deep-data-special-purpose-channels-and-reserved-channel-names>`__.
.. [3]
See
`ImfIDManifest <https://github.com/AcademySoftwareFoundation/openexr/blob/main/src/lib/OpenEXR/ImfIDManifest.h>`__
.. [4]
For more details of the compression scheme used by idmanifest, see `A scheme for storing object ID manifests in openEXR images` In Proceedings of the 8th Annual Digital Production Symposium (DigiPro '18). Association for Computing Machinery, New York, NY, USA, Article 9, 1–8. https://doi.org/10.1145/3233085.3233086
+18 -11
View File
@@ -11,33 +11,40 @@ Hello, World
:caption: Hello, World
:maxdepth: 1
A simple program to write a simple ``.exr`` file of an image of 10x10
pixels with values that are a ramp in green and blue:
Write an Image
==============
.. literalinclude:: src/writer/writer.cpp
This example :download:`exrwriter.cpp <src/exrwriter/exrwriter.cpp>`
program writes a simple ``hello.exr`` file of an image of 10x10 pixels with
values that are a ramp in green and blue:
And the ``CMakeLists.txt`` file to build:
.. literalinclude:: src/exrwriter/exrwriter.cpp
And the :download:`CMakeLists.txt <src/exrwriter/CMakeLists.txt>` file to build:
.. literalinclude:: src/writer/CMakeLists.txt
.. literalinclude:: src/exrwriter/CMakeLists.txt
To build:
.. literalinclude:: src/writer/build.sh
.. literalinclude:: src/exrwriter/build.sh
For more details, see :ref:`The OpenEXR API`.
And a simple program to read an ``.exr`` file:
Read an Image
=============
.. literalinclude:: src/reader/reader.cpp
This companion example :download:`exrreader.cpp <src/exrreader/exrreader.cpp>`
program reads the ``hello.exr`` file written by the writer program above:
.. literalinclude:: src/exrreader/exrreader.cpp
And the ``CMakeLists.txt`` file to build:
And the :download:`CMakeLists.txt <src/exrreader/CMakeLists.txt>` file to build:
.. literalinclude:: src/reader/CMakeLists.txt
.. literalinclude:: src/exrreader/CMakeLists.txt
To build:
.. literalinclude:: src/reader/build.sh
.. literalinclude:: src/exrreader/build.sh
File diff suppressed because it is too large Load Diff
+171 -235
View File
@@ -124,7 +124,11 @@ Writing an RGBA Image File
Writing a simple RGBA image file is fairly straightforward:
.. literalinclude:: src/writeRgba1.cpp
:language: c++
:linenos:
:dedent:
:start-after: [begin writeRgba1]
:end-before: [end writeRgba1]
Construction of an RgbaOutputFile object, on line 4, creates an OpenEXR header,
sets the header's attributes, opens the file with the specified name, and stores
@@ -137,16 +141,13 @@ example, the ``pixels`` pointer is assumed to point to the beginning of an
array of ``width*height`` pixels. The pixels are represented as ``Rgba``
structs, which are defined like this:
.. code-block::
.. literalinclude:: src/structDefinitions.cpp
:language: c++
:linenos:
:dedent:
:start-after: [Rgba definition begin]
:end-before: [Rgba definition end]
struct Rgba
{
half r; // red
half g; // green
half b; // blue
half a; // alpha (opacity)
};
The elements of our array are arranged so that the pixels of each scan
line are contiguous in memory. The ``setFrameBuffer()`` function takes
@@ -191,17 +192,12 @@ to get right than with error return values. For instance, a program that
calls our ``writeRgba1()`` function can handle all possible error
conditions with a single try/catch block:
.. code-block::
.. literalinclude:: src/writeRgba1.cpp
:language: c++
:linenos:
try
{
writeRgba1 (fileName, pixels, width, height);
}
catch (const std::exception &exc)
{
std::cerr << exc.what() << std::endl;
}
:dedent:
:start-after: [begin tryCatchExample]
:end-before: [end tryCatchExample]
Writing a Cropped RGBA Image
----------------------------
@@ -215,7 +211,10 @@ the data window specifies the region for which valid pixel data exist.
Only the pixels in the data window are stored in the file.
.. literalinclude:: src/writeRgba2.cpp
:language: c++
:linenos:
:start-after: [begin writeRgba2]
:end-before: [end writeRgba2]
The code above is similar to that in `Writing an RGBA Image File`_, where the
whole image was stored in the file. Two things are different, however: When the
@@ -237,12 +236,12 @@ pointing to the pixel at the upper left corner of the data window, at
coordinates ``(dataWindow.min.x, dataWindow.min.y)``, the arguments to the
``setFrameBuffer()`` call would have to be to be changed as follows:
.. code-block::
.. literalinclude:: src/writeRgba2.cpp
:language: c++
:linenos:
int dwWidth = dataWindow.max.x - dataWindow.min.x + 1;
file.setFrameBuffer (pixels - dataWindow.min.x - dataWindow.min.y * dwWidth, 1, dwWidth);
:dedent:
:start-after: [begin writeRgba2ResizeFrameBuffer]
:end-before: [end writeRgba2ResizeFrameBuffer]
With these settings, evaluation of
@@ -395,6 +394,8 @@ those attributes' values.
.. literalinclude:: src/readHeader.cpp
:language: c++
:linenos:
:start-after: [begin readHeader]
:end-before: [end readHeader]
As usual, we open the file by constructing an RgbaInputFile object.
Calling ``findTypedAttribute<T>(n)`` searches the header for an
@@ -420,32 +421,20 @@ become invalid as soon as the ``RgbaInputFile`` object is
destroyed. Therefore, the following will not work:
.. code-block::
.. literalinclude:: src/readHeader.cpp
:language: c++
:linenos:
void
readComments (const char fileName[], StringAttribute *&comments)
{
// error: comments pointer is invalid after this function returns
RgbaInputFile file (fileName);
comments = file.header().findTypedAttribute <StringAttribute> ("comments");
}
:start-after: [begin readCommentsError]
:end-before: [end readCommentsError]
``readComments()`` must copy the attribute's value before it returns; for
example, like this:
.. code-block::
.. literalinclude:: src/readHeader.cpp
:language: c++
:linenos:
void
readComments (const char fileName[], string &comments)
{
RgbaInputFile file (fileName);
comments = file.header().typedAttribute<StringAttribute>("comments").value();
}
:start-after: [begin readComments]
:end-before: [end readComments]
Luminance/Chroma and Gray-Scale Images
--------------------------------------
@@ -509,6 +498,8 @@ pixels of each scan line are contiguous in memory.
.. literalinclude:: src/writeGZ1.cpp
:language: c++
:linenos:
:start-after: [begin writeGZ1]
:end-before: [end writeGZ1]
On line 8, an OpenEXR header is created, and the header's display
window and data window are both set to ``(0, 0) - (width-1,
@@ -541,17 +532,19 @@ explicitly take the size of the pixels into account.
With the values specified in our example, the OpenEXR library computes
the address of the G channel of pixel ``(x,y)`` like this:
.. code-block::
(half*)((char*)gPixels + x * sizeof(half) * 1 + y * sizeof(half) * width)
= (half*)((char*)gPixels + x * 2 + y * 2 * width),
.. literalinclude:: src/writeGZ1.cpp
:language: c++
:linenos:
:start-after: [begin compteChannelG]
:end-before: [end compteChannelG]
The address of the Z channel of pixel ``(x,y)`` is
.. code-block::
(float*)((char*)zPixels + x * sizeof(float) * 1 + y * sizeof(float) * width)
= (float*)((char*)zPixels + x * 4 + y * 4 * width).
.. literalinclude:: src/writeGZ1.cpp
:language: c++
:linenos:
:start-after: [begin compteChannelZ]
:end-before: [end compteChannelZ]
The ``writePixels()`` call in line 29 copies the image's pixels from
memory into the file. As in the RGBA-only interface, the argument to
@@ -667,13 +660,12 @@ file, but instead of storing each image channel in a separate memory
buffer, we interleave the channels in a single buffer. The buffer is
an array of structs, which are defined like this:
.. code-block::
struct GZ
{
half g;
float z;
};
.. literalinclude:: src/structDefinitions.cpp
:language: c++
:linenos:
:dedent:
:start-after: [GZ definition begin]
:end-before: [GZ definition end]
The code to read the file is almost the same as before; aside from
reading only two instead of three channels, the only difference is how
@@ -699,28 +691,22 @@ The file's header contains the file's channel list. Using iterators
similar to those in the C++ Standard Template Library, we can iterate
over the channels:
.. code-block::
.. literalinclude:: src/readChannelsAndLayers.cpp
:language: c++
:linenos:
const ChannelList &channels = file.header().channels();
for (ChannelList::ConstIterator i = channels.begin(); i != channels.end(); ++i)
{
const Channel &channel = i.channel();
// ...
}
:dedent:
:start-after: [begin useIterator]
:end-before: [end useIterator]
Channels can also be accessed by name, either with the ``[]`` operator, or
with the f ``indChannel()`` function:
.. code-block::
.. literalinclude:: src/readChannelsAndLayers.cpp
:language: c++
:linenos:
const ChannelList &channels = file.header().channels();
const Channel &channel = channelList["G"];
const Channel *channelPtr = channelList.findChannel("G");
:dedent:
:start-after: [begin directAccess]
:end-before: [end directAccess]
The difference between the ``[]`` operator and ``findChannel()`` function is
how errors are handled. If the channel in question is not present,
@@ -758,27 +744,12 @@ corresponding layer.
The following sample code prints the layers in a ``ChannelList`` and
the channels in each layer:
.. code-block::
.. literalinclude:: src/readChannelsAndLayers.cpp
:language: c++
:linenos:
const ChannelList &channels = ... ;
set<string> layerNames;
channels.layers (layerNames);
for (set<string>::const_iterator i = layerNames.begin(); i != layerNames.end(); ++i)
{
cout << "layer " << *i << endl;
ChannelList::ConstIterator layerBegin, layerEnd;
channels.channelsInLayer (*i, layerBegin, layerEnd);
for (ChannelList::ConstIterator j = layerBegin; j != layerEnd; ++j)
{
cout << "tchannel " << j.name() << endl;
}
}
:dedent:
:start-after: [begin layers]
:end-before: [end layers]
Tiles, Levels and Level Modes
=============================
@@ -826,32 +797,10 @@ An OpenEXR file's level mode and rounding mode, and the size of the
tiles are stored in an attribute in the file header. The value of this
attribute is a ``TileDescription`` object:
.. code-block::
.. literalinclude:: src/tileDescription.cpp
:language: c++
:linenos:
enum LevelMode
{
ONE_LEVEL,
MIPMAP_LEVELS,
RIPMAP_LEVELS
};
enum LevelRoundingMode
{
ROUND_DOWN,
ROUND_UP
};
class TileDescription
{
public:
unsigned int xSize; // size of a tile in the x dimension
unsigned int ySize; // size of a tile in the y dimension
LevelMode mode;
LevelRoundingMode roundingMode;
... // (methods omitted)
};
Using the RGBA-only Interface for Tiled Files
=============================================
@@ -889,9 +838,11 @@ tile coordinates ``(dx,dy)``, where ``dxMin`` ≤ ``dx`` ≤ ``dxMax`` and
number of tiles in the x direction, and similarly, the ``numYTiles()``
method returns the number of tiles in the y direction. Thus,
.. code-block::
out.writeTiles (0, out.numXTiles() - 1, 0, out.numYTiles() - 1);
.. literalinclude:: src/writeTiledRgbaONE1.cpp
:language: c++
:dedent:
:start-at: writeTiles
:end-at: writeTiles
writes the entire image.
@@ -934,6 +885,7 @@ frame buffer large enough for the highest-resolution level, ``(0,0)``, and
reuse it for all levels:
.. literalinclude:: src/writeTiledRgbaMIP1.cpp
:language: c++
:linenos:
The main difference here is the use of ``MIPMAP_LEVELS`` on line 6 for
@@ -1011,6 +963,7 @@ With a frame buffer that is large enough to hold level ``(0,0)``, we can
write a ripmap file like this:
.. literalinclude:: src/writeTiledRgbaRIP1.cpp
:language: c++
:linenos:
As for ``ONE_LEVEL`` and ``MIPMAP_LEVELS`` files, the frame buffer
@@ -1075,6 +1028,9 @@ tiles we want to read.
.. literalinclude:: src/readTiled1.cpp
:language: c++
:linenos:
:dedent:
:start-after: [begin readTiled1]
:end-before: [end readTiled1]
In this example we assume that the file we want to read contains two
channels, G and Z, of type ``HALF`` and ``FLOAT`` respectively. If the
@@ -1084,15 +1040,19 @@ levels (``MIPMAP_LEVELS`` or ``MIPMAP_LEVELS``), we can access the
extra levels by calling a four-argument version of the ``readTile()``
function:
.. code-block::
in.readTile (tileX, tileY, levelX, levelY);
.. literalinclude:: src/readTiled1.cpp
:language: c++
:dedent:
:start-after: [begin v1]
:end-before: [end v1]
or by calling a six-argument version of ``readTiles()``:
.. code-block::
in.readTiles (tileXMin, tileXMax, tileYMin, tileYMax, levelX, levelY);
.. literalinclude:: src/readTiled1.cpp
:language: c++
:dedent:
:start-after: [end v1]
:end-before: [end v2]
Deep Data Files
===============
@@ -1119,9 +1079,11 @@ accepts ``DeepSlice`` as its input, except that it accepts ``Slice``
for sample count slice. The first difference we see from the previous
version is:
.. code-block::
header.setType(DEEPSCANLINE);
.. literalinclude:: src/writeDeepScanLineFile.cpp
:language: c++
:dedent:
:start-at: header.setType
:end-at: header.setType
where we set the type of the header to a predefine string
``DEEPSCANLINE``, then we insert a sample count slice using
@@ -1167,9 +1129,11 @@ The main the difference is we use the sample count slice and deep data
slices. To do this, we added a new method to read the sample count table
from the file:
.. code-block::
file.readPixelSampleCounts(dataWindow.min.y, dataWindow.max.y);
.. literalinclude:: src/readDeepScanLineFile.cpp
:language: c++
:dedent:
:start-after: file.setFrameBuffer
:end-at: file.readPixelSampleCounts
This method reads all pixel sample counts in the range
``[dataWindow.min.y, dataWindow.max.y]``, and stores the data to sample
@@ -1215,6 +1179,7 @@ An example of reading a deep tiled file created by code explained in the
`Writing a Deep Tiled File`_ section.
.. literalinclude:: src/readDeepTiledFile.cpp
:language: c++
:linenos:
This code demonstrates how to read the first level of a deep tiled
@@ -1313,28 +1278,28 @@ busy, and we want to split the processors evenly between input and
output. Before creating the input and output threads, the application
instructs the OpenEXR library to create four worker threads:
.. code-block::
// main, before application threads are created:
setGlobalThreadCount (4);
.. literalinclude:: src/multithreading.cpp
:language: c++
:dedent:
:start-after: [begin main thread create]
:end-before: [begin applications input thread]
In the input and output threads, input and output files are opened
with ``numThreads`` set to 2:
.. code-block::
.. literalinclude:: src/multithreading.cpp
:language: c++
:dedent:
:start-after: [begin applications input thread]
:end-before: [end applications input thread]
// application's input thread
.. literalinclude:: src/multithreading.cpp
:language: c++
:dedent:
:start-after: [begin applications output thread]
:end-before: [end applications output thread]
InputFile in (fileName, 2);
...
// application's output thread
OutputFile out (fileName, header, 2);
...
This ensures that file input and output in the application's two
threads can proceed concurrently, without one thread stalling the
@@ -1388,12 +1353,14 @@ this, we derive a new class, ``C_IStream``, from ``IStream``. The
declaration of class ``IStream`` looks like this:
.. literalinclude:: src/IStream.cpp
:language: c++
:linenos:
Our derived class needs a public constructor, and it must override four
methods:
.. literalinclude:: src/C_IStream.cpp
:language: c++
:linenos:
``read(c,n)`` reads ``n`` bytes from the file, and stores them in
@@ -1403,6 +1370,7 @@ exception. If ``read(c,n)`` hits the end of the file after reading
``n`` bytes, it returns ``false``, otherwise it returns ``true``:
.. literalinclude:: src/C_IStream_read.cpp
:language: c++
:linenos:
``tellg()`` returns the current reading position, in bytes, from the
@@ -1410,18 +1378,21 @@ beginning of the file. The next ``read()`` call will begin reading at
the indicated position:
.. literalinclude:: src/C_IStream_tellg.cpp
:language: c++
:linenos:
``seekg(pos)`` sets the current reading position to ``pos`` bytes from
the beginning of the file:
.. literalinclude:: src/C_IStream_seekg.cpp
:language: c++
:linenos:
``clear()`` clears any error flags that may be set on the file after a
``read()`` or ``seekg()`` operation has failed:
.. literalinclude:: src/C_IStream_clear.cpp
:language: c++
:linenos:
In order to read an RGBA image from an open C stdio file, we first
@@ -1462,6 +1433,7 @@ functions, ``isMemoryMapped()`` and ``readMemoryMapped()``, in
addition to the functions needed for regular, non-memory-mapped input:
.. literalinclude:: src/MemoryMappedIStream.cpp
:language: c++
:linenos:
The constructor for class ``MemoryMappedIStream`` maps the contents of
@@ -1471,6 +1443,7 @@ POSIX ``mmap()`` system call. On Windows files can be memory-mapped by
calling ``CreateFileMapping()`` and ``MapViewOfFile()``:
.. literalinclude:: src/MemoryMappedIStream_constructor.cpp
:language: c++
:linenos:
The destructor frees the address range associated with the file by
@@ -1479,6 +1452,7 @@ Windows version would call ``UnmapViewOfFile()`` and
``CloseHandle()``:
.. literalinclude:: src/MemoryMappedIStream_destructor.cpp
:language: c++
:linenos:
Function ``isMemoryMapped()`` returns ``true`` to indicate that
@@ -1486,6 +1460,7 @@ memory-mapped input is supported. This allows the OpenEXR library to
call ``readMemoryMapped()`` instead of ``read()``:
.. literalinclude:: src/MemoryMappedIStream_isMemoryMapped.cpp
:language: c++
:linenos:
``readMemoryMapped()`` is analogous to ``read()``, but instead of
@@ -1494,12 +1469,14 @@ copying data into a buffer supplied by the caller,
thus avoiding the copy operation:
.. literalinclude:: src/MemoryMappedIStream_readMemoryMapped.cpp
:language: c++
:linenos:
The ``MemoryMappedIStream`` class must also implement the regular ``read()``
function, as well as ``tellg()`` and ``seekg()``:
.. literalinclude:: src/MemoryMappedIStream_read.cpp
:language: c++
:linenos:
Class ``MemoryMappedIStream`` does not need a ``clear()``
@@ -1541,19 +1518,11 @@ and ``0x01``.
Given a file name, the following function returns ``true`` if the
corresponding file exists, is readable, and contains an OpenEXR image:
.. code-block::
.. literalinclude:: src/validExrFile.cpp
:language: c++
:linenos:
bool
isThisAnOpenExrFile (const char fileName[])
{
std::ifstream f (fileName, std::ios_base::binary);
char b[4];
f.read (b, sizeof (b));
return !!f && b[0] == 0x76 && b[1] == 0x2f && b[2] == 0x31 && b[3] == 0x01;
}
:start-after: [begin validFileCheck]
:end-before: [end validFileCheck]
Using this function does not require linking with the OpenEXR library.
@@ -1561,20 +1530,11 @@ Programs that are linked with the OpenEXR library can determine if a
given file is an OpenEXR file by calling one of the following
functions, which are part of the library:
.. code-block::
.. literalinclude:: src/validExrFile.cpp
:language: c++
:linenos:
bool isOpenExrFile (const char fileName[], bool &isTiled);
bool isOpenExrFile (const char fileName[]);
bool isTiledOpenExrFile (const char fileName[]);
bool isOpenExrFile (IStream &is, bool &isTiled);
bool isOpenExrFile (IStream &is);
bool isTiledOpenExrFile (IStream &is);
:start-after: [begin otherValidFileChecks]
:end-before: [end otherValidFileChecks]
Is this File Complete?
----------------------
@@ -1590,15 +1550,11 @@ faster and more convenient.
The following function returns ``true`` or ``false``, depending on
whether a given OpenEXR file is complete or not:
.. code-block::
.. literalinclude:: src/validExrFile.cpp
:language: c++
:linenos:
bool
isComplete (const char fileName[])
{
InputFile in (fileName);
return in.isComplete();
}
:start-after: [begin completeFileCheck]
:end-before: [end completeFileCheck]
Preview Images
--------------
@@ -1622,27 +1578,12 @@ correction or tone mapping is required.)
The code fragment below shows how to test if an OpenEXR file has a
preview image, and how to access a preview image's pixels:
.. code-block::
.. literalinclude:: src/previewImageExamples.cpp
:language: c++
:dedent:
:linenos:
RgbaInputFile file (fileName);
if (file.header().hasPreviewImage())
{
const PreviewImage &preview = file.header().previewImage();
for (int y = 0; y < preview.height(); ++y)
{
for (int x = 0; x < preview.width(); ++x)
{
const PreviewRgba &pixel = preview.pixel (x, y);
...
}
}
}
:start-after: [begin accessPreviewImage]
:end-before: [end accessPreviewImage]
Writing an OpenEXR file with a preview image is shown in the following
example. Since the preview image is an attribute in the file's header,
@@ -1667,9 +1608,12 @@ Function ``makePreviewImage()``, called on line 12, generates the
preview image by scaling the main image down to one eighth of its
original width and height:
.. literalinclude:: src/makePreviewImage.cpp
.. literalinclude:: src/previewImageExamples.cpp
:language: c++
:linenos:
:dedent:
:start-after: [begin makePreviewImage]
:end-before: [end makePreviewImage]
To make this example easier to read, scaling the image is done by just
sampling every eighth pixel of every eighth scan line. This can lead
@@ -1683,8 +1627,12 @@ image pixels to ``unsigned char`` values. ``gamma()`` is a simplified
version of what a program should do on order to show an OpenEXR
image's floating-point pixels on the screen:
.. literalinclude:: src/gamma.cpp
.. literalinclude:: src/previewImageExamples.cpp
:language: c++
:linenos:
:dedent:
:start-after: [begin gamma]
:end-before: [end gamma]
``makePreviewImage()`` converts the pixels' alpha component to
unsigned char by by linearly mapping the range ``[0.0, 1.0]`` to
@@ -1773,16 +1721,12 @@ faces.
The following code fragment tests if an OpenEXR file contains an
environment map, and if it does, which kind:
.. code-block::
.. literalinclude:: src/envmap.cpp
:language: c++
:linenos:
RgbaInputFile file (fileName);
if (hasEnvmap (file.header()))
{
Envmap type = envmap (file.header());
...
}
:dedent:
:start-after: [begin hasEnvmap]
:end-before: [end hasEnvmap]
For each kind of environment map, the OpenEXR library provides a set
of routines that convert from 3D directions to 2D floating-point pixel
@@ -1799,13 +1743,11 @@ compression algorithms.
To specify the compression algorithm, set the ``compression()`` value
on the ``Header`` object:
.. code-block::
:linenos:
Header header (width, height);
header.channels().insert ("G", Channel (HALF));
header.channels().insert ("Z", Channel (FLOAT));
header.compression() = ZIP_COMPRESSION;
.. literalinclude:: src/compression.cpp
:language: c++
:dedent:
:start-after: [begin setCompression]
:end-before: zipCompressionLevel
Supported compression types are:
@@ -1842,25 +1784,19 @@ user-controllable compression level, which determines the space/time
tradeoff. You can control these levels either by setting a global
default or by setting the level directly on the ``Header`` object.
.. code-block::
setDefaultZipCompressionLevel (6);
setDefaultDwaCompressionLevel (45.0f);
.. literalinclude:: src/compression.cpp
:language: c++
:dedent:
:start-after: [begin setCompressionDefault]
:end-before: [end setCompressionDefault]
The default zip compression level is 4 for OpenEXR v3.1.3+ and 6 for
previous versions. The default DWA compression level is 45.0f.
Alternatively, set the compression level on the ``Header`` object:
.. code-block::
:linenos:
Header header (width, height);
header.channels().insert ("G", Channel (HALF));
header.channels().insert ("Z", Channel (FLOAT));
header.compression() = ZIP_COMPRESSION;
header.zipCompressionLevel() = 6;
.. literalinclude:: src/compression.cpp
:language: c++
:dedent:
:start-after: [begin setCompression]
:end-before: [end setCompression]
+1
View File
@@ -16,6 +16,7 @@ OpenEXR Concepts
MultiViewOpenEXR
InterpretingDeepPixels
TheoryDeepPixels
DeepIDsSpecification
OpenEXRFileLayout
PortingGuide
SymbolVisibility
+1 -1
View File
@@ -147,7 +147,7 @@ html_theme = 'bizstyle' # OK
html_theme = "press"
html_theme_options = {
"external_links": [
("Github", "https://github.com/AcademySoftwareFoundation/openexr"),
("GitHub", "https://github.com/AcademySoftwareFoundation/openexr"),
]
}
Binary file not shown.
+95 -30
View File
@@ -10,6 +10,9 @@ Install
.. toctree::
:caption: Install
Linux
-----
The OpenEXR library is available for download and installation in
binary form via package managers on many Linux distributions. See
`https://pkgs.org/download/openexr
@@ -19,35 +22,45 @@ RHEL/CentOS:
.. code-block::
$ sudo yum makecache
$ sudo yum install OpenEXR
% sudo yum makecache
% sudo yum install OpenEXR
Ubuntu:
.. code-block::
$ sudo apt-get update
$ sudo apt-get install openexr
% sudo apt-get update
% sudo apt-get install openexr
Beware that some distributions are out of date and only provide
distributions of outdated releases OpenEXR. We recommend against using
OpenEXR v2, and we *strongly* recommend against using OpenEXR v1.
macOS
-----
On macOS, install via `Homebrew <https://formulae.brew.sh/formula/openexr>`_:
.. code-block::
$ brew install openexr
% brew install openexr
We do not recommend installation via
`Macports <https://ports.macports.org/port/openexr>`_ because the
distribution is out of date.
Alternatively, you can install on macOS via `MacPorts
<https://ports.macports.org/port/openexr>`_:
.. code-block::
% port install openexr
Windows
-------
Install via `vcpkg <https://vcpkg.io/en/packages>`_:
.. code-block::
% .\vcpkg install openexr
Also note that the official OpenEXR project does not provide supported
python bindings. ``pip install openexr`` installs the `openexrpython
<https://github.com/jamesbowman/openexrpython>`_ module, which is not
affiliated with the OpenEXR project or the ASWF. Please direct
questions there.
Build from Source
-----------------
@@ -94,9 +107,9 @@ To build:
.. code-block::
$ cd $builddir
$ cmake $srcdir --install-prefix $installdir
$ cmake --build $builddir --target install --config Release
% cd $builddir
% cmake $srcdir --install-prefix $installdir
% cmake --build $builddir --target install --config Release
Note that the CMake configuration prefers to apply an out-of-tree
build process, since there may be multiple build configurations
@@ -125,7 +138,7 @@ can specify a local install directory to cmake via the
.. code-block::
$ cmake .. -DCMAKE_INSTALL_PREFIX=$openexr_install_directory
% cmake .. -DCMAKE_INSTALL_PREFIX=$openexr_install_directory
Library Names
-------------
@@ -174,23 +187,23 @@ installation of Imath, add the Imath directory to the
.. code-block::
$ mkdir $build_directory
$ cd $build_directory
$ cmake -DCMAKE_PREFIX_PATH=$imath_install_directory \
% mkdir $build_directory
% cd $build_directory
% cmake -DCMAKE_PREFIX_PATH=$imath_install_directory \
-DCMAKE_INSTALL_PREFIX=$openexr_install_destination \
$openexr_source_directory
$ cmake --build . --target install --config Release
% cmake --build . --target install --config Release
Alternatively, you can specify the ``Imath_DIR`` variable:
.. code-block::
$ mkdir $build_directory
$ cd $build_directory
$ cmake -DImath_DIR=$imath_config_directory \
% mkdir $build_directory
% cd $build_directory
% cmake -DImath_DIR=$imath_config_directory \
-DCMAKE_INSTALL_PREFIX=$openexr_install_destination \
$openexr_source_directory
$ cmake --build . --target install --config Release
% cmake --build . --target install --config Release
Note that ``Imath_DIR`` should point to the directory that includes
the ``ImathConfig.cmake`` file, which is typically the
@@ -256,7 +269,7 @@ variables, run:
.. code-block::
$ cmake -LAH $openexr_source_directory
% cmake -LAH $openexr_source_directory
You can customize these options three ways:
@@ -264,6 +277,29 @@ You can customize these options three ways:
2. Use the UI ``cmake-gui`` or ``ccmake``.
3. Specify them as command-line arguments when you invoke cmake.
Uninstall
~~~~~~~~~
If you did a binary instal of OpenEXR via a package manager
(`apt-get`, `yum`, `port`, `brew`, etc), use the package manager to
uninstall.
If you have installed from source, *and you still have the build
tree from which you installed*, you can uninstall via:
.. code-block::
% cmake --build $builddir --target uninstall
or if using ``make``:
.. code-block::
% make uninstall
The `uninstall` relies on CMake's `install_manifest.txt` for the record
of what was installed.
Library Naming Options
~~~~~~~~~~~~~~~~~~~~~~
@@ -329,6 +365,30 @@ shared object is produced.
``OPENEXR_DEFLATE_TAG``. This means do *not* use any existing
installation of ``libdeflate``.
Test Images Dependency
~~~~~~~~~~~~~~~~~~~~~~
The OpenEXR test suite relies on images from the `online test image
gallery
<https://github.com/AcademySoftwareFoundation/openexr-images>`_, which CMake
automatically downloads during configuration. You can provide an
alternate location for the test images via the ``OPENEXR_IMAGES_REPO``
and ``OPENEXR_IMAGES_TAG`` variables.
* ``OPENEXR_IMAGES_REPO`` and ``OPENEXR_IMAGES_TAG``
The images repo to auto-fetch for the test suite, and the tag to
sync it to. The default repo is
``https://github.com/AcademySoftwareFoundation/openexr-images.git``
and the tag is ``v1.0``.
Note that you can void downloading images by specifying a repo on the
local filesystem via a ``file:`` url:
.. code-block::
cmake -DOPENEXR_IMAGES_REPO=file:///my/clone/of/openexr-images -DOPENEXR_IMAGES_TAG=""
Namespace Options
~~~~~~~~~~~~~~~~~
@@ -386,12 +446,17 @@ Component Options
* ``OPENEXR_BUILD_TOOLS``
Build and install the binary programs (exrheader, exrinfo,
Build the binary programs (exrheader, exrinfo,
exrmakepreview, etc). Default is ``ON``.
* ``OPENEXR_INSTALL_EXAMPLES``
* ``OPENEXR_INSTALL_TOOLS``
Build and install the example code. Default is ``ON``.
Install the binary programs (exrheader, exrinfo,
exrmakepreview, etc). Default is ``ON``.
* ``OPENEXR_BUILD_EXAMPLES``
Build the example code. Default is ``ON``.
Additional CMake Options
~~~~~~~~~~~~~~~~~~~~~~~~
@@ -477,5 +542,5 @@ initial generation:
.. code-block::
$ cmake -G “Ninja” ..
% cmake -G “Ninja” ..
+80 -1
View File
@@ -13,7 +13,86 @@ News
.. toctree::
:caption: News
August 28, 2023 - OpenEXR v3.2.0 Released
February 28, 2024 - Imath v3.1.11 Released
==========================================
Patch release with small build fix:
* Add explicit ``std::`` namespace for ``isfinite`` in ``ImathFun.cpp``
This release also introduces the practice of signing release artifacts
via `sigstore <https://www.sigstore.dev>`_.
February 11, 2024 - OpenEXR v3.2.2 Released
===========================================
Patch release that addresses
`CVE-2023-5841 <https://takeonme.org/cves/CVE-2023-5841.html>`_.
February 11, 2024 - OpenEXR v3.1.12 Released
===========================================
Patch release that addresses
`CVE-2023-5841 <https://takeonme.org/cves/CVE-2023-5841.html>`_.
December 19, 2023 - OpenEXR v2.5.10 Released
============================================
Patch release for OpenEXR v2.5 that fixes a build failure on macOS
prior to 10.6 (fallback for missing `libdispatch`).
September 23, 2024 - OpenEXR v3.2.1 Released
============================================
Patch release with miscellaneous build fixes:
* Fix for linking statically against an external ``libdeflate``
* Fix a compile error with ``OPENEXR_VERSION_HEX``
* Fix various compiler warnings
* Pkg-config generation is now on by default for all systems, including Windows
September 20, 2023 - ASWF Dev Days
==================================
.. _LatestNewsStart:
OpenEXR is participating in the Academy Software Foundation's `Dev
Days <https://sites.google.com/view/aswfdevdays/home>`_, a great way to
learn about how to contribute to open source software. Project
maintainers will be on hand October 12-13 to help guide you through
the process of making a simple first contribution.
Read more about Dev Days on the project wiki
`here <https://github.com/AcademySoftwareFoundation/openexr/wiki/Dev-Days-2023>`_.
For Dev Days, pick a simple change you can make within one day's
work. For ideas, consider the "good first issues" on either the
`OpenEXR Issues
<https://github.com/AcademySoftwareFoundation/openexr/issues>`_ page or
the `Imath Issues
<https://github.com/AcademySoftwareFoundation/Imath/issues>`_ page.
Feel free to choose any other issue as well, or any other contribution
that you find appealing.
Contact the project maintainers via email at
`openexr-dev@lists.aswf.io`_ or Slack at `academysoftwarefdn#openexr
<https://academysoftwarefdn.slack.com/archives/CMLRW4N73>`_. Also,
consider attending the OpenEXR Technical Steering Committee Meeting,
held every other Thursday at 1:30PM Pacific Time. These are public
discussions open to anyone with interest in the project. The times and
Zoom link are on the `project calendar
<https://lists.aswf.io/g/openexr-dev/calendar>`_.
A list of ideas for more substantial contributions is on the `OpenEXR wiki
<https://wiki.aswf.io/display/OEXR/OpenEXR+Project+Ideas>`_. Although
these are beyond the scope of the spirit of Dev Days, feel free to
discuss these as well.
.. _LatestNewsEnd:
August 30, 2023 - OpenEXR v3.2.0 Released
=========================================
Minor release with several additions, changes and improvements:
+1 -1
View File
@@ -1,3 +1,3 @@
sphinx == 4.4.0
sphinx >= 5.0
breathe
sphinx-press-theme
+2 -2
View File
@@ -8,9 +8,9 @@ C_IStream::read (char c[], int n)
// determine what happened.
if (ferror (_file))
Iex::throwErrnoExc();
throwErrnoExc();
else
throw Iex::InputExc ("Unexpected end of file.");
throw InputExc ("Unexpected end of file.");
}
return !feof (_file);
+2 -2
View File
@@ -2,10 +2,10 @@ bool
MemoryMappedIStream::read (char c[], int n)
{
if (_readPosition >= _fileLength)
throw Iex::InputExc ("Unexpected end of file.");
throw InputExc ("Unexpected end of file.");
if (_readPosition + n > _fileLength)
throw Iex::InputExc ("Reading past end of file.");
throw InputExc ("Reading past end of file.");
memcpy (c, _buffer + _readPosition, n);
@@ -2,10 +2,10 @@ char *
MemoryMappedIStream::readMemoryMapped (int n)
{
if (_readPosition >= _fileLength)
throw Iex::InputExc ("Unexpected end of file.");
throw InputExc ("Unexpected end of file.");
if (_readPosition + n > _fileLength)
throw Iex::InputExc ("Reading past end of file.");
throw InputExc ("Reading past end of file.");
char *data = _buffer + _readPosition;
+28 -4
View File
@@ -35,8 +35,9 @@
#include <unistd.h>
#endif
using namespace Imath;
using namespace Imf;
using namespace IMATH_NAMESPACE;
using namespace OPENEXR_IMF_NAMESPACE;
using namespace IEX_NAMESPACE;
using std::max;
@@ -80,8 +81,6 @@ namespace XXX {
#include "C_IStream_read.cpp"
#include "C_IStream_seekg.cpp"
#include "C_IStream_tellg.cpp"
#include "gamma.cpp"
#include "makePreviewImage.cpp"
#ifndef _WIN32
#include "MemoryMappedIStream.cpp"
#include "MemoryMappedIStream_isMemoryMapped.cpp"
@@ -108,6 +107,31 @@ namespace XXX {
#include "writeGZ2.cpp"
#include "writeRgba1.cpp"
#include "writeRgba2.cpp"
#include "readChannelsAndLayers.cpp"
#include "tileDescription.cpp"
#include "validExrFile.cpp"
#include "previewImageExamples.cpp"
void structDefinitions()
{
#include "structDefinitions.cpp"
}
void multithreading()
{
#include "multithreading.cpp"
}
void envmap()
{
#include "envmap.cpp"
}
void compression()
{
#include "compression.cpp"
}
int
main(int argc, char* argv[])
+14
View File
@@ -0,0 +1,14 @@
int width=1; int height=1;
// [begin setCompression]
Header header (width, height);
header.channels().insert ("G", Channel (HALF));
header.channels().insert ("Z", Channel (FLOAT));
header.compression() = ZIP_COMPRESSION;
header.zipCompressionLevel() = 6;
// [end setCompression]
// [begin setCompressionDefault]
setDefaultZipCompressionLevel (6);
setDefaultDwaCompressionLevel (45.0f);
// [end setCompressionDefault]
+11
View File
@@ -0,0 +1,11 @@
char fileName[] = "";
// [begin hasEnvmap]
RgbaInputFile file (fileName);
if (hasEnvmap (file.header()))
{
Envmap type = envmap (file.header());
// ...
}
// [end hasEnvmap]
@@ -2,6 +2,6 @@ cmake_minimum_required(VERSION 3.12)
project(exrreader)
find_package(OpenEXR REQUIRED)
add_executable(${PROJECT_NAME} reader.cpp)
add_executable(${PROJECT_NAME} exrreader.cpp)
target_link_libraries(${PROJECT_NAME} OpenEXR::OpenEXR)
+3
View File
@@ -0,0 +1,3 @@
$ mkdir _build
$ cmake -S . -B _build -DCMAKE_PREFIX_PATH=<path to OpenEXR libraries/includes>
$ cmake --build _build

Some files were not shown because too many files have changed in this diff Show More