Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a1a00ffeec | ||
|
|
edc86ed415 | ||
|
|
7239a2652f | ||
|
|
53786883ac | ||
|
|
a830413002 | ||
|
|
c8b731abf5 | ||
|
|
cbb0ca61cd | ||
|
|
454cd4d62a | ||
|
|
1969623dd4 | ||
|
|
afb5b6fc02 | ||
|
|
8d25bedd83 | ||
|
|
1a59a1a7f6 | ||
|
|
e4b8d0226a | ||
|
|
d5c8b71dbe | ||
|
|
cca5eb44a0 | ||
|
|
f627f72dae | ||
|
|
0ef34bbc6e | ||
|
|
58f2ab0fbb | ||
|
|
efdfcff21a | ||
|
|
09914784e2 | ||
|
|
d9f606e484 | ||
|
|
9c3e18aade | ||
|
|
27fb7abe70 | ||
|
|
690ee3edf7 | ||
|
|
17811af04e | ||
|
|
2d3fce400b | ||
|
|
c3804276bb | ||
|
|
6a0a5c0143 | ||
|
|
0e46554b2a | ||
|
|
441cf460a9 | ||
|
|
4ae29178d3 | ||
|
|
daa66664e3 | ||
|
|
b765658cfb | ||
|
|
74f3a2d191 | ||
|
|
c825a08bec | ||
|
|
4198d004ed | ||
|
|
e29a6dafe1 | ||
|
|
2641f8b341 | ||
|
|
b6caee5b8b | ||
|
|
6925579b36 | ||
|
|
d6c7eed7e7 | ||
|
|
f37f7d1c43 | ||
|
|
e7a34c5e04 | ||
|
|
93336dae74 | ||
|
|
162abaa322 | ||
|
|
5261e5ef19 | ||
|
|
467e7e58ff | ||
|
|
55b199c13e | ||
|
|
8061f57626 | ||
|
|
88cd3c713b | ||
|
|
0414107b3f | ||
|
|
38c4b59e08 | ||
|
|
74776cbaf8 | ||
|
|
00ee0fb31b | ||
|
|
d9360a608b | ||
|
|
9a999c95b6 | ||
|
|
0b3f83fde6 | ||
|
|
7adf2c8f6f | ||
|
|
c0208feee9 | ||
|
|
d5cc7b7ebe | ||
|
|
d8cb68dfd6 | ||
|
|
280606593f | ||
|
|
561969efe2 | ||
|
|
e0cb398300 | ||
|
|
5c0381835d | ||
|
|
8902ae6e71 | ||
|
|
f996a5f076 | ||
|
|
8bedf106ef | ||
|
|
737b2707a0 |
+1
-1
@@ -1 +1 @@
|
||||
6.3.2
|
||||
7.0.2
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 -- //...
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,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
|
||||
|
||||
@@ -57,3 +57,4 @@ docs/_test_images/
|
||||
|
||||
# Ignore Bazel generated files
|
||||
bazel-*
|
||||
MODULE.bazel.lock
|
||||
|
||||
@@ -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
File diff suppressed because it is too large
Load Diff
+40
-10
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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
@@ -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")
|
||||
|
||||
@@ -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
@@ -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.
|
||||
|
||||
|
||||
|
||||
|
||||
Vendored
+9
-9
@@ -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
@@ -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()
|
||||
|
||||
###################################################
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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@
|
||||
|
||||
|
||||
@@ -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@")
|
||||
|
||||
@@ -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
@@ -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()
|
||||
|
||||
@@ -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()
|
||||
@@ -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"
|
||||
@@ -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
@@ -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 )
|
||||
|
||||
@@ -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()
|
||||
@@ -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) {}
|
||||
}
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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++;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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; }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 )
|
||||
|
||||
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
/**************************************/
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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) | \
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/**************************************/
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
@@ -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()
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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
|
||||
|
||||
@@ -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")
|
||||
|
||||
|
||||
@@ -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.")
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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")
|
||||
|
||||
|
||||
|
||||
@@ -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")
|
||||
|
||||
|
||||
@@ -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")
|
||||
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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")
|
||||
|
||||
|
||||
@@ -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")
|
||||
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,142 @@
|
||||
<!-- SPDX-License-Identifier: BSD-3-Clause -->
|
||||
<!-- Copyright (c) Contributors to the OpenEXR Project -->
|
||||
|
||||
[](https://github.com/AcademySoftwareFoundation/openexr/blob/main/LICENSE.md)
|
||||
[](https://bestpractices.coreinfrastructure.org/projects/2799)
|
||||
[](https://securityscorecards.dev/viewer/?uri=github.com/AcademySoftwareFoundation/openexr)
|
||||
[](https://github.com/AcademySoftwareFoundation/openexr/actions?query=workflow%3ACI)
|
||||
[](https://github.com/AcademySoftwareFoundation/openexr/actions?query=workflow%3AAnalysis)
|
||||
[](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).
|
||||
|
||||
|
||||
@@ -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"]
|
||||
@@ -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'],
|
||||
)
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
|
||||
|
||||
|
||||
+854
-173
File diff suppressed because it is too large
Load Diff
@@ -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]
|
||||
@@ -16,6 +16,7 @@ OpenEXR Concepts
|
||||
MultiViewOpenEXR
|
||||
InterpretingDeepPixels
|
||||
TheoryDeepPixels
|
||||
DeepIDsSpecification
|
||||
OpenEXRFileLayout
|
||||
PortingGuide
|
||||
SymbolVisibility
|
||||
|
||||
+1
-1
@@ -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
@@ -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
@@ -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,3 +1,3 @@
|
||||
sphinx == 4.4.0
|
||||
sphinx >= 5.0
|
||||
breathe
|
||||
sphinx-press-theme
|
||||
|
||||
@@ -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,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
@@ -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[])
|
||||
|
||||
@@ -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]
|
||||
@@ -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)
|
||||
|
||||
Executable
+3
@@ -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
Reference in New Issue
Block a user