From 92db29b9e7e298c4c2cc67c8a74944c2e7e716e5 Mon Sep 17 00:00:00 2001 From: Doug Walker Date: Thu, 7 Dec 2023 20:02:08 -0500 Subject: [PATCH] Integrate main into RB-2.3 branch and update library version (#1918) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fix support for X86 32-bit (#1842) Signed-off-by: Mark Reid (cherry picked from commit 16b3157f2e1f2b84cbb67adf04a6c58c60d381b2) Signed-off-by: Doug Walker * Add some small arm neon optimizations (#1847) * Remove unused includes Signed-off-by: Mark Reid * Use neon hardware support for f16 conversions Signed-off-by: Mark Reid * Add some small neon optimizations use blendv,floor and fma intrinsics were possible Signed-off-by: Mark Reid --------- Signed-off-by: Mark Reid (cherry picked from commit 14f0afa2d8295af6f746d9edd3264a43d2c04623) Signed-off-by: Doug Walker * Add links to new release notes documentation (#1848) Signed-off-by: Kevin Wheatley Co-authored-by: Doug Walker (cherry picked from commit 87126fad7f27a703850e098a9a75e0ef206b3da9) Signed-off-by: Doug Walker * Changing version to 2.4.0 (#1852) Signed-off-by: Cédrik Fuoco Co-authored-by: Doug Walker (cherry picked from commit 381d1fc371d612cf20e5666256b65f1b07f2b956) Signed-off-by: Doug Walker * Correctly recover CXX_FLAGS in CheckSupportSSE2.cmake (#1861) Signed-off-by: Chongyun Lee Co-authored-by: Doug Walker (cherry picked from commit c429400170ccd34902d8a6b26e70c43e26d57751) Signed-off-by: Doug Walker * Fix regression in cccid handling when no value is supplied (#1855) In v1 of OCIO FileTransforms are able to load .cc files without specifying a cccid. In v2 this broke causing an exception to be raised instead of using the first cc found in the file. Signed-off-by: Kevin Wheatley Co-authored-by: Doug Walker (cherry picked from commit c7ad35340af721c6c3c4780c3a3666b8ce326ba2) Signed-off-by: Doug Walker * Fix missing cache id reset on look update. (#1873) Signed-off-by: Eric Renaud-Houde (cherry picked from commit dddbee023dc2d4c21b16fe4f79ca1374c19ecbc0) Signed-off-by: Doug Walker * ocioview: Curve Inspector improvements (#1845) * Curve inspector improvements - Move README to root app folder - Change curve inspector grid to always render as a square with 10 segments. - Add transform init callback to set new transform subscriptions to the current viewer if set to passthrough. Signed-off-by: Michael Dolan * Improve log range calculation Signed-off-by: Michael Dolan * Improve channel sample comparison Signed-off-by: Michael Dolan * Update src/apps/ocioview/ocioview/inspect/curve_inspector.py Signed-off-by: Michael Dolan Co-authored-by: Doug Walker Signed-off-by: Michael Dolan --------- Signed-off-by: Michael Dolan Co-authored-by: Doug Walker (cherry picked from commit 8add374dade5a7e3ee802269b71e1c0b75f8b8f0) Signed-off-by: Doug Walker * Fix missing Default View Transform on equal operator (#1886) Add the missing assignment of the the default view transform when a config is copied using the equal operator Signed-off-by: Michael De Caria Signed-off-by: Doug Walker Co-authored-by: Doug Walker (cherry picked from commit bc8569b2db1dedec162eaa1302da29711f8cad3a) Signed-off-by: Doug Walker * Remove circular import caused by typing annotations. (#1882) Signed-off-by: Thomas Mansencal Signed-off-by: Doug Walker Co-authored-by: Doug Walker (cherry picked from commit 1fad4666c3d289831d9bb1f32debdc4d4fb43943) Signed-off-by: Doug Walker * fix(grammatical): Spelling mistakes (#1892) Signed-off-by: AbhineshJha Signed-off-by: Doug Walker Co-authored-by: Doug Walker (cherry picked from commit 0d00b2c538e83e3f2e024aadacb2b2eff1da9391) Signed-off-by: Doug Walker * Improve ocioview mac support and simplify dependencies (#1853) * PySide 6, remove imath, add imageio support Signed-off-by: Rémi Achard Remove Imath Signed-off-by: Rémi Achard Support imageio as fallback for openimageio Signed-off-by: Rémi Achard Further adjustments following latest updates Signed-off-by: Rémi Achard Fix pixel probe Signed-off-by: Remi Achard Add OpenColorIO to requirements Signed-off-by: Remi Achard * Fix rebase issue Signed-off-by: Remi Achard --------- Signed-off-by: Remi Achard Signed-off-by: Thomas Mansencal Signed-off-by: Michael Dolan Co-authored-by: Thomas Mansencal Co-authored-by: Michael Dolan (cherry picked from commit 45544ce6679ecb163873d6ca1692a783fb65444e) Signed-off-by: Doug Walker * Issue #1874 Cast to unsigned char for isspace. (#1888) * Issue #1874 Cast to unsigned char for isspace. Signed-off-by: pylee * Add unit test. Signed-off-by: pylee * Add test comment as suggested in code review. Signed-off-by: pylee --------- Signed-off-by: pylee Signed-off-by: Doug Walker Co-authored-by: Doug Walker (cherry picked from commit ed852073f2b020c306ab4c098205d557bff8f243) Signed-off-by: Doug Walker * Tentative fix for the doxygen installation in the CI (Windows) (#1890) Signed-off-by: Cédrik Fuoco Co-authored-by: Doug Walker (cherry picked from commit b94a184e4b66ca9dff72eca13fb1e7a3d0ce65f7) Signed-off-by: Doug Walker * Simplify the Findyaml-cpp module (#1891) This fixes compatibility with yaml-cpp 0.8, which previously failed because of a `get_property` call with the wrong target name. I took the liberty to add a few simplifications along the way. Signed-off-by: Tobias Mayer Co-authored-by: Doug Walker (cherry picked from commit 1d3b69502eeb0f0b1d381d347efcab5b18ae9f3c) Signed-off-by: Doug Walker * Skip processor concatenation if the display color space is also data. (#1896) * Skip processor concatenation if the display view transform is also data. Signed-off-by: Eric Renaud-Houde * Moved missing display color space exception before processor creation. Signed-off-by: Eric Renaud-Houde --------- Signed-off-by: Eric Renaud-Houde Co-authored-by: Doug Walker (cherry picked from commit 52b496528ca42e0c16dfebb33a8efbef0795f55d) Signed-off-by: Doug Walker * Restore GPU workflow and minor updates to CI (#1899) * Restore GPU workflow runs Signed-off-by: Rémi Achard * Enable undefined behaviour sanitizer Signed-off-by: Rémi Achard * Fix SIMD option for platform_latest Signed-off-by: Rémi Achard * Fix install_docs_env on CI workflow (not used at the moment) Signed-off-by: Rémi Achard * Fix OpenEXR build flag Signed-off-by: Rémi Achard --------- Signed-off-by: Rémi Achard Co-authored-by: Doug Walker (cherry picked from commit 382dcb6ce7f029e72c8f1e335d917670196adf51) Signed-off-by: Doug Walker * Improve handling of pystring include dir (#1901) Signed-off-by: Rémi Achard Co-authored-by: Doug Walker (cherry picked from commit 9078753990d7f976a0bfcd55cfa63f2e1de3a53b) Signed-off-by: Doug Walker * Improve compatibility with minizip-ng COMPAT mode (#1902) Signed-off-by: Rémi Achard Co-authored-by: Doug Walker (cherry picked from commit ffd0f70ad594bb3fa77a362484eb3d308af6d95e) Signed-off-by: Doug Walker * Fix NamedTransform context var issue (#1905) Signed-off-by: Doug Walker (cherry picked from commit 4d64b32bc7e7b41fad1c3b9d1371b452e9f1f63a) Signed-off-by: Doug Walker * Fix env serialization for v1 configs (#1904) Signed-off-by: Doug Walker (cherry picked from commit 4f4f30e1d32b05fbbd5b498db6562584bac00dd4) Signed-off-by: Doug Walker * Fix yaml-cpp build issues (#1907) Signed-off-by: Rémi Achard Co-authored-by: Doug Walker (cherry picked from commit 41441bb2e6090094990c677cb3a09ee65d5a1f79) Signed-off-by: Doug Walker * Adsk Contrib - Improve heuristics for finding known color spaces (#1913) * Improve heuristics Signed-off-by: Doug Walker * Add some comments Signed-off-by: Doug Walker --------- Signed-off-by: Doug Walker (cherry picked from commit d8852b5d6c5df757a728d410f1a042cee781226e) Signed-off-by: Doug Walker * Add Python 3.12 wheels (#1898) Signed-off-by: Rémi Achard Co-authored-by: Doug Walker (cherry picked from commit f2cfec330e2d2c9bcce3635d15d18e7391d7202f) Signed-off-by: Doug Walker * Increment library version to 2.3.1 Signed-off-by: Doug Walker * Fix wrong RPATH being injected into Python bindings DSO (#1849) Signed-off-by: Kevin Wheatley Co-authored-by: Doug Walker (cherry picked from commit ba2b41e309eac66af8d555d98b1af999625697fb) Signed-off-by: Doug Walker --------- Signed-off-by: Mark Reid Signed-off-by: Doug Walker Signed-off-by: Kevin Wheatley Signed-off-by: Cédrik Fuoco Signed-off-by: Chongyun Lee Signed-off-by: Eric Renaud-Houde Signed-off-by: Michael Dolan Signed-off-by: Michael De Caria Signed-off-by: Doug Walker Signed-off-by: Thomas Mansencal Signed-off-by: AbhineshJha Signed-off-by: Remi Achard Signed-off-by: pylee Signed-off-by: Tobias Mayer Signed-off-by: Rémi Achard Co-authored-by: Mark Reid Co-authored-by: Kevin Wheatley Co-authored-by: Cédrik Fuoco <105517825+cedrik-fuoco-adsk@users.noreply.github.com> Co-authored-by: Uchiha Kakashi <45286352+licy183@users.noreply.github.com> Co-authored-by: Éric Renaud-Houde Co-authored-by: Michael Dolan Co-authored-by: Michael De Caria Co-authored-by: Thomas Mansencal Co-authored-by: Abhinesh <142514166+AbhineshJha@users.noreply.github.com> Co-authored-by: Rémi Achard Co-authored-by: PenneLee Co-authored-by: tobim --- .github/workflows/ci_workflow.yml | 19 +- .github/workflows/dependencies_latest.yml | 19 +- .github/workflows/gpu_workflow.yml | 2 - .github/workflows/platform_latest.yml | 6 +- .github/workflows/wheel_workflow.yml | 25 +- CHANGELOG.md | 5 + CMakeLists.txt | 7 +- CONTRIBUTING.md | 2 +- README.md | 14 +- include/OpenColorIO/OpenColorIO.h | 4 + include/OpenColorIO/OpenColorTypes.h | 2 +- pyproject.toml | 8 +- setup.cfg | 1 + share/ci/scripts/windows/install_docs_env.sh | 3 +- share/ci/scripts/windows/install_doxygen.sh | 20 +- .../cmake/macros/ocio_handle_dependency.cmake | 2 +- share/cmake/modules/Findminizip-ng.cmake | 17 +- share/cmake/modules/Findpystring.cmake | 3 +- share/cmake/modules/Findyaml-cpp.cmake | 76 +- .../modules/install/InstallOpenEXR.cmake | 2 +- .../modules/install/Installpystring.cmake | 2 +- .../modules/install/Installyaml-cpp.cmake | 20 +- share/cmake/utils/CheckSupportSSE2.cmake | 2 +- share/cmake/utils/CompilerFlags.cmake | 4 +- src/OpenColorIO/AVX.h | 1 - src/OpenColorIO/AVX2.h | 1 - src/OpenColorIO/CMakeLists.txt | 2 +- src/OpenColorIO/CPUInfo.cpp | 7 + src/OpenColorIO/CPUInfoConfig.h.in | 3 + src/OpenColorIO/Config.cpp | 41 +- src/OpenColorIO/ConfigUtils.cpp | 231 +- src/OpenColorIO/Context.cpp | 3 +- src/OpenColorIO/OCIOYaml.cpp | 7 +- src/OpenColorIO/OCIOZArchive.cpp | 5 +- src/OpenColorIO/Op.cpp | 3 +- src/OpenColorIO/PathUtils.cpp | 4 +- src/OpenColorIO/SSE2.h | 40 +- src/OpenColorIO/fileformats/FileFormatCCC.cpp | 2 +- src/OpenColorIO/fileformats/FileFormatCDL.cpp | 4 +- src/OpenColorIO/fileformats/FileFormatCTF.cpp | 3 +- .../fileformats/FileFormatDiscreet1DL.cpp | 4 +- src/OpenColorIO/fileformats/FileFormatICC.cpp | 3 +- .../fileformats/FileFormatIridasLook.cpp | 4 +- src/OpenColorIO/ops/lut1d/Lut1DOpCPU_SSE2.cpp | 10 + src/OpenColorIO/ops/lut3d/Lut3DOpCPU_SSE2.cpp | 14 + .../transforms/ColorSpaceTransform.cpp | 59 +- src/OpenColorIO/transforms/FileTransform.cpp | 3 +- src/apps/ocioview/{ocioview => }/README.md | 4 +- src/apps/ocioview/main.py | 9 +- src/apps/ocioview/ocioview/config_dock.py | 2 +- src/apps/ocioview/ocioview/constants.py | 2 +- .../ocioview/inspect/code_inspector.py | 6 +- .../ocioview/inspect/curve_inspector.py | 491 +- .../ocioview/inspect/log_inspector.py | 2 +- src/apps/ocioview/ocioview/inspect_dock.py | 2 +- .../items/active_display_view_edit.py | 2 +- .../items/active_display_view_model.py | 4 +- .../ocioview/items/color_space_edit.py | 2 +- .../ocioview/items/color_space_model.py | 2 +- .../ocioview/items/config_item_edit.py | 4 +- .../ocioview/items/config_item_model.py | 4 +- .../ocioview/items/config_properties_edit.py | 2 +- .../ocioview/items/config_properties_model.py | 2 +- src/apps/ocioview/ocioview/items/delegates.py | 4 +- .../ocioview/ocioview/items/display_model.py | 2 +- .../ocioview/items/display_view_edit.py | 2 +- .../ocioview/ocioview/items/file_rule_edit.py | 2 +- .../ocioview/items/file_rule_model.py | 2 +- src/apps/ocioview/ocioview/items/look_edit.py | 2 +- .../ocioview/ocioview/items/look_model.py | 2 +- .../ocioview/items/named_transform_edit.py | 2 +- .../ocioview/items/named_transform_model.py | 2 +- src/apps/ocioview/ocioview/items/role_edit.py | 2 +- .../ocioview/ocioview/items/role_model.py | 2 +- src/apps/ocioview/ocioview/items/rule_edit.py | 2 +- .../ocioview/items/shared_view_edit.py | 2 +- .../ocioview/items/shared_view_model.py | 2 +- src/apps/ocioview/ocioview/items/view_edit.py | 2 +- .../ocioview/ocioview/items/view_model.py | 2 +- .../ocioview/items/view_transform_edit.py | 2 +- .../ocioview/items/view_transform_model.py | 2 +- .../ocioview/items/viewing_rule_edit.py | 2 +- .../ocioview/items/viewing_rule_model.py | 2 +- src/apps/ocioview/ocioview/main_window.py | 2 +- src/apps/ocioview/ocioview/message_router.py | 2 +- src/apps/ocioview/ocioview/settings.py | 2 +- src/apps/ocioview/ocioview/style.py | 2 +- .../ocioview/ocioview/transform_manager.py | 38 +- .../ocioview/transforms/allocation_edit.py | 2 +- .../ocioview/transforms/builtin_edit.py | 2 +- .../ocioview/ocioview/transforms/cdl_edit.py | 2 +- .../ocioview/transforms/color_space_edit.py | 2 +- .../ocioview/transforms/display_view_edit.py | 2 +- .../ocioview/transforms/exponent_edit.py | 2 +- .../transforms/exponent_with_linear_edit.py | 2 +- .../transforms/exposure_contrast_edit.py | 2 +- .../ocioview/ocioview/transforms/file_edit.py | 2 +- .../transforms/fixed_function_edit.py | 2 +- .../ocioview/transforms/log_affine_edit.py | 2 +- .../ocioview/transforms/log_camera_edit.py | 2 +- .../ocioview/ocioview/transforms/log_edit.py | 2 +- .../ocioview/ocioview/transforms/look_edit.py | 2 +- .../ocioview/transforms/matrix_edit.py | 2 +- .../ocioview/transforms/range_edit.py | 2 +- .../ocioview/transforms/transform_edit.py | 2 +- .../transforms/transform_edit_stack.py | 4 +- src/apps/ocioview/ocioview/undo.py | 12 +- src/apps/ocioview/ocioview/utils.py | 2 +- .../ocioview/ocioview/viewer/image_plane.py | 260 +- .../ocioview/ocioview/viewer/image_viewer.py | 22 +- src/apps/ocioview/ocioview/viewer_dock.py | 2 +- .../ocioview/ocioview/widgets/check_box.py | 2 +- .../ocioview/ocioview/widgets/combo_box.py | 5 +- .../ocioview/ocioview/widgets/item_view.py | 4 +- src/apps/ocioview/ocioview/widgets/layout.py | 2 +- .../ocioview/ocioview/widgets/line_edit.py | 6 +- .../ocioview/ocioview/widgets/list_widget.py | 9 +- .../ocioview/ocioview/widgets/log_view.py | 2 +- .../ocioview/ocioview/widgets/structure.py | 2 +- .../ocioview/ocioview/widgets/table_widget.py | 2 +- .../ocioview/ocioview/widgets/text_edit.py | 2 +- src/apps/ocioview/requirements.txt | 6 +- src/apps/pyociodisplay/pyociodisplay.py | 3 +- src/bindings/python/CMakeLists.txt | 2 +- src/cmake/Config.cmake.in | 7 +- src/utils/StringUtils.h | 4 +- tests/cpu/BitDepthUtils_tests.cpp | 6 - tests/cpu/ColorSpace_tests.cpp | 153 +- tests/cpu/Config_tests.cpp | 88 +- tests/cpu/Context_tests.cpp | 5 +- tests/cpu/UnitTestUtils.h | 3 +- .../ExposureContrastOpData_tests.cpp | 5 - tests/cpu/ops/lut3d/Lut3DOp_tests.cpp | 6 - .../transforms/ColorSpaceTransform_tests.cpp | 18 + tests/cpu/transforms/FileTransform_tests.cpp | 71 + .../files/cdl_test_cc_file_with_extension.ccc | 12 + .../files/cdl_test_cc_file_with_extension.cdl | 12 + tests/data/files/clf/lut3d_as_matrix.clf | 18 + tests/data/files/sRGB_to_linear.spi1d | 4107 +++++++++++++++++ tests/python/ColorSpaceTest.py | 4 +- tests/python/TransformsTest.py | 6 +- tests/utils/StringUtils_tests.cpp | 6 + vendor/openfx/OCIOUtils.cpp | 3 +- 143 files changed, 5612 insertions(+), 652 deletions(-) rename src/apps/ocioview/{ocioview => }/README.md (94%) create mode 100644 tests/data/files/cdl_test_cc_file_with_extension.ccc create mode 100644 tests/data/files/cdl_test_cc_file_with_extension.cdl create mode 100644 tests/data/files/clf/lut3d_as_matrix.clf create mode 100644 tests/data/files/sRGB_to_linear.spi1d diff --git a/.github/workflows/ci_workflow.yml b/.github/workflows/ci_workflow.yml index 2bc34c3ed..9ac6e7544 100644 --- a/.github/workflows/ci_workflow.yml +++ b/.github/workflows/ci_workflow.yml @@ -77,6 +77,7 @@ jobs: cc-compiler: clang compiler-desc: Clang vfx-cy: 2023 + install-ext-packages: MISSING - build: 11 build-type: Release build-shared: 'ON' @@ -89,6 +90,7 @@ jobs: cc-compiler: gcc compiler-desc: GCC vfx-cy: 2023 + install-ext-packages: ALL - build: 10 build-type: Release build-shared: 'OFF' @@ -101,6 +103,7 @@ jobs: cc-compiler: gcc compiler-desc: GCC vfx-cy: 2023 + install-ext-packages: ALL # ------------------------------------------------------------------- # VFX CY2022 (Python 3.9) # ------------------------------------------------------------------- @@ -116,6 +119,7 @@ jobs: cc-compiler: clang compiler-desc: Clang vfx-cy: 2022 + install-ext-packages: ALL - build: 8 build-type: Release build-shared: 'ON' @@ -128,6 +132,7 @@ jobs: cc-compiler: gcc compiler-desc: GCC vfx-cy: 2022 + install-ext-packages: MISSING - build: 7 build-type: Release build-shared: 'OFF' @@ -140,6 +145,7 @@ jobs: cc-compiler: gcc compiler-desc: GCC vfx-cy: 2022 + install-ext-packages: ALL # ------------------------------------------------------------------- # VFX CY2021 (Python 3.7) # ------------------------------------------------------------------- @@ -155,6 +161,7 @@ jobs: cc-compiler: clang compiler-desc: Clang vfx-cy: 2021 + install-ext-packages: MISSING - build: 5 build-type: Release build-shared: 'OFF' @@ -167,6 +174,7 @@ jobs: cc-compiler: clang compiler-desc: Clang vfx-cy: 2021 + install-ext-packages: ALL - build: 4 build-type: Debug build-shared: 'ON' @@ -179,6 +187,7 @@ jobs: cc-compiler: gcc compiler-desc: GCC vfx-cy: 2021 + install-ext-packages: ALL # ------------------------------------------------------------------- # VFX CY2020 (Python 3.7) # ------------------------------------------------------------------- @@ -194,6 +203,7 @@ jobs: cc-compiler: clang compiler-desc: Clang vfx-cy: 2020 + install-ext-packages: MISSING - build: 2 build-type: Debug build-shared: 'OFF' @@ -206,6 +216,7 @@ jobs: cc-compiler: gcc compiler-desc: GCC vfx-cy: 2020 + install-ext-packages: ALL - build: 1 build-type: Release build-shared: 'ON' @@ -218,6 +229,7 @@ jobs: cc-compiler: gcc compiler-desc: GCC vfx-cy: 2020 + install-ext-packages: ALL env: CXX: ${{ matrix.cxx-compiler }} CC: ${{ matrix.cc-compiler }} @@ -245,7 +257,7 @@ jobs: -DOCIO_BUILD_GPU_TESTS=OFF \ -DOCIO_USE_SIMD=${{ matrix.use-simd }} \ -DOCIO_USE_OIIO_FOR_APPS=${{ matrix.use-oiio }} \ - -DOCIO_INSTALL_EXT_PACKAGES=ALL \ + -DOCIO_INSTALL_EXT_PACKAGES=${{ matrix.install-ext-packages }} \ -DOCIO_WARNING_AS_ERROR=ON \ -DPython_EXECUTABLE=$(which python) working-directory: _build @@ -527,7 +539,10 @@ jobs: - name: Checkout uses: actions/checkout@v3 - name: Install docs env - run: share/ci/scripts/windows/install_docs_env.sh + run: | + DOXYGEN_PATH=$GITHUB_WORKSPACE/doxygen + share/ci/scripts/windows/install_docs_env.sh "$DOXYGEN_PATH" + echo "$DOXYGEN_PATH" >> $GITHUB_PATH shell: bash if: matrix.build-docs == 'ON' - name: Install tests env diff --git a/.github/workflows/dependencies_latest.yml b/.github/workflows/dependencies_latest.yml index fe729a4fe..d37a9882e 100644 --- a/.github/workflows/dependencies_latest.yml +++ b/.github/workflows/dependencies_latest.yml @@ -54,6 +54,7 @@ jobs: cc-compiler: gcc compiler-desc: GCC vfx-cy: 2022 + use-oiio: 'ON' - build: 2 build-docs: 'OFF' build-openfx: 'OFF' @@ -62,6 +63,7 @@ jobs: cc-compiler: gcc compiler-desc: GCC vfx-cy: 2021 + use-oiio: 'OFF' # ------------------------------------------------------------------- # Clang # ------------------------------------------------------------------- @@ -73,6 +75,7 @@ jobs: cc-compiler: clang compiler-desc: Clang vfx-cy: 2022 + use-oiio: 'OFF' - build: 4 build-docs: 'ON' build-openfx: 'ON' @@ -81,6 +84,7 @@ jobs: cc-compiler: clang compiler-desc: Clang vfx-cy: 2021 + use-oiio: 'ON' env: CXX: ${{ matrix.cxx-compiler }} CC: ${{ matrix.cc-compiler }} @@ -132,7 +136,7 @@ jobs: -DOCIO_INSTALL_EXT_PACKAGES=NONE \ -DOCIO_WARNING_AS_ERROR=OFF \ -DPython_EXECUTABLE=$(which python) \ - -DOCIO_USE_OIIO_CMAKE_CONFIG=ON + -DOCIO_USE_OIIO_FOR_APPS=${{ matrix.use-oiio }} working-directory: _build - name: Build run: | @@ -176,11 +180,13 @@ jobs: build-openfx: 'ON' cxx-standard: 17 python-version: '3.11' + use-oiio: 'ON' - build: 2 build-docs: 'ON' build-openfx: 'ON' cxx-standard: 14 python-version: '3.9' + use-oiio: 'OFF' steps: - name: Setup Python uses: actions/setup-python@v4 @@ -235,7 +241,7 @@ jobs: -DOCIO_INSTALL_EXT_PACKAGES=NONE \ -DOCIO_WARNING_AS_ERROR=OFF \ -DPython_EXECUTABLE=$(which python) \ - -DOCIO_USE_OIIO_CMAKE_CONFIG=ON + -DOCIO_USE_OIIO_FOR_APPS=${{ matrix.use-oiio }} working-directory: _build - name: Build run: | @@ -279,11 +285,13 @@ jobs: build-openfx: 'ON' cxx-standard: 17 python-version: '3.11' + use-oiio: 'ON' - build: 2 build-docs: 'ON' build-openfx: 'ON' cxx-standard: 14 python-version: '3.9' + use-oiio: 'OFF' steps: - name: Setup Python uses: actions/setup-python@v4 @@ -292,7 +300,10 @@ jobs: - name: Checkout uses: actions/checkout@v3 - name: Install docs env - run: share/ci/scripts/windows/install_docs_env.sh + run: | + DOXYGEN_PATH=$GITHUB_WORKSPACE/doxygen + share/ci/scripts/windows/install_docs_env.sh "$DOXYGEN_PATH" + echo "$DOXYGEN_PATH" >> $GITHUB_PATH shell: bash if: matrix.build-docs == 'ON' - name: Install tests env @@ -359,7 +370,7 @@ jobs: -DOCIO_WARNING_AS_ERROR=OFF \ -DPython_EXECUTABLE=$(which python) \ -DOCIO_BUILD_PYTHON=OFF \ - -DOCIO_USE_OIIO_CMAKE_CONFIG=ON + -DOCIO_USE_OIIO_FOR_APPS=${{ matrix.use-oiio }} shell: bash working-directory: _build - name: Build diff --git a/.github/workflows/gpu_workflow.yml b/.github/workflows/gpu_workflow.yml index 4bd5a98f6..7933a2fb8 100644 --- a/.github/workflows/gpu_workflow.yml +++ b/.github/workflows/gpu_workflow.yml @@ -16,8 +16,6 @@ on: tags-ignore: - v0.* - v1.* - paths: - - .github/workflows/gpu_workflow.yml jobs: # --------------------------------------------------------------------------- diff --git a/.github/workflows/platform_latest.yml b/.github/workflows/platform_latest.yml index 9d4d04183..b57398be0 100644 --- a/.github/workflows/platform_latest.yml +++ b/.github/workflows/platform_latest.yml @@ -104,7 +104,7 @@ jobs: -DOCIO_BUILD_OPENFX=ON \ -DOCIO_BUILD_GPU_TESTS=OFF \ -DOCIO_BUILD_PYTHON=${{ matrix.build-python}} \ - -DOCIO_USE_SSE=ON \ + -DOCIO_USE_SIMD=ON \ -DOCIO_USE_OIIO_FOR_APPS=OFF \ -DOCIO_INSTALL_EXT_PACKAGES=ALL \ -DOCIO_WARNING_AS_ERROR=ON \ @@ -219,7 +219,7 @@ jobs: -DOCIO_BUILD_OPENFX=ON \ -DOCIO_BUILD_GPU_TESTS=OFF \ -DOCIO_BUILD_PYTHON=${{ matrix.build-python}} \ - -DOCIO_USE_SSE=ON \ + -DOCIO_USE_SIMD=ON \ -DOCIO_USE_OIIO_FOR_APPS=OFF \ -DOCIO_INSTALL_EXT_PACKAGES=ALL \ -DOCIO_WARNING_AS_ERROR=ON \ @@ -333,7 +333,7 @@ jobs: -DOCIO_BUILD_OPENFX=ON \ -DOCIO_BUILD_GPU_TESTS=OFF \ -DOCIO_BUILD_PYTHON=${{ matrix.build-python}} \ - -DOCIO_USE_SSE=ON \ + -DOCIO_USE_SIMD=ON \ -DOCIO_USE_OIIO_FOR_APPS=OFF \ -DOCIO_INSTALL_EXT_PACKAGES=ALL \ -DOCIO_WARNING_AS_ERROR=ON \ diff --git a/.github/workflows/wheel_workflow.yml b/.github/workflows/wheel_workflow.yml index 702243b78..5dff59df1 100644 --- a/.github/workflows/wheel_workflow.yml +++ b/.github/workflows/wheel_workflow.yml @@ -95,6 +95,9 @@ jobs: - build: CPython 3.11 64 bits python: cp311-manylinux* arch: x86_64 + - build: CPython 3.12 64 bits + python: cp312-manylinux* + arch: x86_64 # ------------------------------------------------------------------- # CPython ARM 64 bits # ------------------------------------------------------------------- @@ -113,6 +116,9 @@ jobs: - build: CPython 3.11 ARM 64 bits python: cp311-manylinux* arch: aarch64 + - build: CPython 3.12 ARM 64 bits + python: cp312-manylinux* + arch: aarch64 steps: - uses: actions/checkout@v3 @@ -128,7 +134,7 @@ jobs: platforms: all - name: Build wheels - uses: pypa/cibuildwheel@v2.13.1 + uses: pypa/cibuildwheel@v2.16.2 env: CIBW_BUILD: ${{ matrix.python }} CIBW_ARCHS: ${{ matrix.arch }} @@ -143,7 +149,7 @@ jobs: macos: name: Build wheels on macOS - runs-on: macos-11 + runs-on: macos-12 # Don't run on OCIO forks if: | github.event_name != 'schedule' || @@ -169,6 +175,9 @@ jobs: - build: CPython 3.11 64 bits python: cp311-* arch: x86_64 + - build: CPython 3.12 64 bits + python: cp312-* + arch: x86_64 # ------------------------------------------------------------------- # CPython ARM 64 bits # ------------------------------------------------------------------- @@ -184,6 +193,9 @@ jobs: - build: CPython 3.11 ARM 64 bits python: cp311-* arch: arm64 + - build: CPython 3.12 ARM 64 bits + python: cp312-* + arch: arm64 steps: - uses: actions/checkout@v3 @@ -194,7 +206,7 @@ jobs: python-version: '3.8' - name: Build wheels - uses: pypa/cibuildwheel@v2.13.1 + uses: pypa/cibuildwheel@v2.16.2 env: CIBW_BUILD: ${{ matrix.python }} CIBW_ARCHS: ${{ matrix.arch }} @@ -209,7 +221,7 @@ jobs: windows: name: Build wheels on Windows - runs-on: windows-2019 + runs-on: windows-2022 # Don't run on OCIO forks if: | github.event_name != 'schedule' || @@ -235,6 +247,9 @@ jobs: - build: CPython 3.11 64 bits python: cp311-* arch: AMD64 + - build: CPython 3.12 64 bits + python: cp312-* + arch: AMD64 steps: - uses: actions/checkout@v3 @@ -245,7 +260,7 @@ jobs: python-version: '3.8' - name: Build wheels - uses: pypa/cibuildwheel@v2.13.1 + uses: pypa/cibuildwheel@v2.16.2 env: CIBW_BUILD: ${{ matrix.python }} CIBW_ARCHS: ${{ matrix.arch }} diff --git a/CHANGELOG.md b/CHANGELOG.md index ac889cc4b..cf611a448 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,8 +3,13 @@ This file documents releases up to 1.1.1. For a description of version 2.0 and later please refer to the Releases page on GitHub: + https://github.com/AcademySoftwareFoundation/OpenColorIO/releases +and for more details please see the release notes in latest documentation pages: + +https://opencolorio.readthedocs.io/en/latest/releases/_index.html#releases + **Version 1.1.1 (April 2 2019):** * Added optional compatibility for building apps with OpenImageIO 1.9+ * Added USE_SSE checks to fix Linux build failure diff --git a/CMakeLists.txt b/CMakeLists.txt index a36a0d8bd..7b62a993c 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -29,7 +29,7 @@ endif() # Project definition. project(OpenColorIO - VERSION 2.3.0 + VERSION 2.3.1 DESCRIPTION "OpenColorIO (OCIO) is a complete color management solution" HOMEPAGE_URL https://github.com/AcademySoftwareFoundation/OpenColorIO LANGUAGES CXX C) @@ -180,8 +180,11 @@ option(OCIO_USE_OIIO_FOR_APPS "Request OIIO to build apps (ociolutimage, ociocon if (NOT APPLE) - if ("${CMAKE_SYSTEM_PROCESSOR}" MATCHES "(AMD64|IA64|EM64T|X86|x86_64|i386|i686)") + if ("${CMAKE_SYSTEM_PROCESSOR}" MATCHES "(AMD64|IA64|EM64T|x86_64|X86|i386|i686)") # Intel-based architecture (not APPLE) + if ("${CMAKE_SYSTEM_PROCESSOR}" MATCHES "(X86|i386|i686)") + set(OCIO_ARCH_X86_32 1) + endif() set(OCIO_ARCH_X86 1) set(OCIO_BUILD_ENABLE_OPTIMIZATIONS_SSE ON) set(OCIO_BUILD_ENABLE_OPTIMIZATIONS_AVX ON) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 0d0a442c4..7fdba8c7a 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -243,7 +243,7 @@ expected to validate the behavior of every part of OCIO: * Any change to existing functionality should have tests added if they don't already exist. -The test should should be run, via ``ctest``, before submitting a pull request. +The test should be run, via ``ctest``, before submitting a pull request. ## Versioning Policy diff --git a/README.md b/README.md index cbd928af5..03276fa64 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,7 @@ Houdini, Silhouette FX, and [others](https://opencolorio.org/#supported_apps). OpenColorIO is free and open source software ([LICENSE](LICENSE)), and -one of several projects actvively sponsored by the ASWF +one of several projects actively sponsored by the ASWF ([Academy Software Foundation](https://www.aswf.io/)). OpenColorIO Project Mission @@ -50,12 +50,12 @@ content creation applications and pipelines. OpenColorIO aims to: -* be stable, secure, and thouroughly tested on Linux, macOS, and Windows +* be stable, secure, and thoroughly tested on Linux, macOS, and Windows * be performant on modern CPUs and GPUs * be simple, scalable, and well documented * be compatible with critical color and imaging standards * provide lossless color processing wherever possible -* maintain config backwards compatability across major versions +* maintain config backwards compatibility across major versions * have every new feature carefully reviewed by leaders from the motion picture, VFX, animation, and video game industries * have a healthy and active community @@ -65,13 +65,14 @@ OpenColorIO Project Governance ------------------------------ OpenColorIO is governed by the Academy Software Foundation (ASWF). See -[GOVERNANCE.md](GOVERNANCE.md) for detailed infomation about how the project +[GOVERNANCE.md](GOVERNANCE.md) for detailed information about how the project operates. Web Resources ------------- * Website: +* Documentation: * Mailing lists: * Developer: * User: @@ -99,6 +100,11 @@ The following reference implementations are provided: * Other * nuke-default +Sources for the newer builtin ACES configuration files can be found in the releases section of the +[OpenColorIO-Config-ACES](https://github.com/AcademySoftwareFoundation/OpenColorIO-Config-ACES) +repository. + + Acknowledgements ---------------- diff --git a/include/OpenColorIO/OpenColorIO.h b/include/OpenColorIO/OpenColorIO.h index 706c583b8..784cf8d2f 100644 --- a/include/OpenColorIO/OpenColorIO.h +++ b/include/OpenColorIO/OpenColorIO.h @@ -2456,6 +2456,10 @@ class OCIOEXPORT Processor */ bool hasChannelCrosstalk() const; + /** + * Returns a hash string generated by hashing the cachedIDs of the (unoptimized) list of ops + * contained in the Processor. (This forms part of the key used by the config's processor cache.) + */ const char * getCacheID() const; /** diff --git a/include/OpenColorIO/OpenColorTypes.h b/include/OpenColorIO/OpenColorTypes.h index 0aa30bd2d..33654baf8 100644 --- a/include/OpenColorIO/OpenColorTypes.h +++ b/include/OpenColorIO/OpenColorTypes.h @@ -760,7 +760,7 @@ extern OCIOEXPORT const char * OCIO_INACTIVE_COLORSPACES_ENVVAR; * The envvar 'OCIO_OPTIMIZATION_FLAGS' provides a way to force a given optimization level. * Remove the variable or set the value to empty to not use it. Set the value of the variable * to the desired optimization level as either an integer or hexadecimal value. - * Ex: OCIO_OPTIMIZATION_FLAGS="20479" or "0x4FFF" for OPTIMIZATION_LOSSLESS. + * Ex: OCIO_OPTIMIZATION_FLAGS="144457667" or "0x89c3fc3" for OPTIMIZATION_LOSSLESS. */ extern OCIOEXPORT const char * OCIO_OPTIMIZATION_FLAGS_ENVVAR; diff --git a/pyproject.toml b/pyproject.toml index 3d4842d2b..337ae4a4f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,10 @@ requires = [ "wheel", "cmake>=3.13", "ninja; sys_platform != 'win32' and platform_machine != 'arm64'", - # Documentation requirements + # Documentation requirements (see docs/requirements.txt for details) + "urllib3<2", + "docutils>=0.18.1", + "sphinx<=7.1.2", "six", "testresources", "recommonmark", @@ -34,4 +37,5 @@ before-build = "share/ci/scripts/linux/yum/install_docs_env.sh" before-build = "share/ci/scripts/macos/install_docs_env.sh" [tool.cibuildwheel.windows] -before-build = "bash -c share/ci/scripts/windows/install_docs_env.sh" +environment = { PATH="$GITHUB_WORKSPACE/doxygen;$PATH" } +before-build = 'bash -c "share/ci/scripts/windows/install_docs_env.sh $GITHUB_WORKSPACE/doxygen"' diff --git a/setup.cfg b/setup.cfg index 16f06e6d1..f53c6050c 100644 --- a/setup.cfg +++ b/setup.cfg @@ -12,6 +12,7 @@ classifiers = Programming Language :: Python :: 3.9 Programming Language :: Python :: 3.10 Programming Language :: Python :: 3.11 + Programming Language :: Python :: 3.12 Programming Language :: Python :: Implementation :: CPython Programming Language :: C++ description = OpenColorIO (OCIO) is a complete color management solution geared towards motion picture production with an emphasis on visual effects and computer animation. diff --git a/share/ci/scripts/windows/install_docs_env.sh b/share/ci/scripts/windows/install_docs_env.sh index 30662eeb9..db18dd2fb 100755 --- a/share/ci/scripts/windows/install_docs_env.sh +++ b/share/ci/scripts/windows/install_docs_env.sh @@ -5,6 +5,7 @@ set -ex HERE=$(dirname $0) +DOXYGEN_LOCATION="$1" -bash $HERE/install_doxygen.sh latest +bash $HERE/install_doxygen.sh "$DOXYGEN_LOCATION" pip install -r $HERE/../../../../docs/requirements.txt diff --git a/share/ci/scripts/windows/install_doxygen.sh b/share/ci/scripts/windows/install_doxygen.sh index 5bbbded09..a08783f76 100755 --- a/share/ci/scripts/windows/install_doxygen.sh +++ b/share/ci/scripts/windows/install_doxygen.sh @@ -4,10 +4,18 @@ set -ex -DOXYGEN_VERSION="$1" +DOXYGEN_LOCATION="$1" -if [ "$DOXYGEN_VERSION" == "latest" ]; then - choco install doxygen.install -else - choco install doxygen.install --version=${DOXYGEN_VERSION} -fi +# Utility to parse JSON object. +choco install jq + +# Get the URL of the latest zip package for Doxygen. +url=$(curl -s 'https://api.github.com/repos/doxygen/doxygen/releases/latest' | jq -r '.assets[] | select(.name | test("doxygen-.*windows.x64.bin.zip")) | .browser_download_url') + +# Download the zip. +mkdir $DOXYGEN_LOCATION +cd $DOXYGEN_LOCATION +powershell 'iwr -URI '$url' -OutFile doxygen.zip' + +# Unzip the file into $DOXYGEN_LOCATION. +unzip -o doxygen.zip \ No newline at end of file diff --git a/share/cmake/macros/ocio_handle_dependency.cmake b/share/cmake/macros/ocio_handle_dependency.cmake index f7bb59356..dd848494e 100644 --- a/share/cmake/macros/ocio_handle_dependency.cmake +++ b/share/cmake/macros/ocio_handle_dependency.cmake @@ -216,7 +216,7 @@ macro (ocio_handle_dependency dep_name) endif() if(ocio_dep_REQUIRED) - if(NOT ${dep_name}_FOUND AND NOT ocio_dep_VERSION) + if(NOT ${dep_name}_FOUND) message(SEND_ERROR "${ColorError}${dep_name} is required, will abort at the end.${ColorReset}") endif() endif() diff --git a/share/cmake/modules/Findminizip-ng.cmake b/share/cmake/modules/Findminizip-ng.cmake index 4d6609383..3609b0ef8 100644 --- a/share/cmake/modules/Findminizip-ng.cmake +++ b/share/cmake/modules/Findminizip-ng.cmake @@ -8,6 +8,7 @@ # minizip-ng_LIBRARY - minizip-ng library to link to # minizip-ng_INCLUDE_DIR - Where to find mz.h and other headers # minizip-ng_VERSION - The version of the library +# minizip-ng_COMPAT - Whether minizip-ng MZ_COMPAT was used or not # # Global targets defined by this module: # MINIZIP::minizip-ng - IMPORTED target, if found @@ -114,7 +115,9 @@ if(NOT OCIO_INSTALL_EXT_PACKAGES STREQUAL ALL) PATH_SUFFIXES include include/minizip-ng + include/minizip minizip-ng/include + minizip/include ) # Minizip-ng uses prefix "lib" on all platform by default. @@ -202,4 +205,16 @@ if(_minizip-ng_TARGET_CREATE) mark_as_advanced(minizip-ng_INCLUDE_DIR minizip-ng_LIBRARY minizip-ng_VERSION) target_link_libraries(MINIZIP::minizip-ng INTERFACE ZLIB::ZLIB) -endif() \ No newline at end of file +endif() + +############################################################################### +### Detect compatibility mode ### + +set(minizip-ng_COMPAT FALSE) +if(minizip-ng_INCLUDE_DIR) + list(GET minizip-ng_INCLUDE_DIR 0 _minizip-ng_INCLUDE_DIR) + if(EXISTS "${_minizip-ng_INCLUDE_DIR}/mz_compat.h") + set(minizip-ng_COMPAT TRUE) + endif() +endif() +mark_as_advanced(minizip-ng_COMPAT) diff --git a/share/cmake/modules/Findpystring.cmake b/share/cmake/modules/Findpystring.cmake index c46611547..50fc64635 100644 --- a/share/cmake/modules/Findpystring.cmake +++ b/share/cmake/modules/Findpystring.cmake @@ -28,11 +28,12 @@ if(NOT OCIO_INSTALL_EXT_PACKAGES STREQUAL ALL) # Find include directory find_path(pystring_INCLUDE_DIR NAMES - pystring/pystring.h + pystring.h HINTS ${pystring_ROOT} PATH_SUFFIXES include + include/pystring pystring/include ) diff --git a/share/cmake/modules/Findyaml-cpp.cmake b/share/cmake/modules/Findyaml-cpp.cmake index 5a850f960..110ae2e4c 100644 --- a/share/cmake/modules/Findyaml-cpp.cmake +++ b/share/cmake/modules/Findyaml-cpp.cmake @@ -10,10 +10,9 @@ # yaml-cpp_VERSION - Library's version # # Global targets defined by this module: -# yaml-cpp +# yaml-cpp::yaml-cpp # # For compatibility with the upstream CMake package, the following variables and targets are defined: -# yaml-cpp::yaml-cpp - Alias of the yaml-cpp target # YAML_CPP_LIBRARIES - Libraries to link against yaml-cpp # YAML_CPP_INCLUDE_DIR - Include directory # @@ -41,16 +40,26 @@ if(CMAKE_BUILD_TYPE MATCHES "[Dd][Ee][Bb][Uu][Gg]") set(BUILD_TYPE_DEBUG ON) endif() +if(yaml-cpp_FIND_QUIETLY) + set(quiet QUIET) +endif() + if(NOT OCIO_INSTALL_EXT_PACKAGES STREQUAL ALL) set(_yaml-cpp_REQUIRED_VARS yaml-cpp_LIBRARY) + # Search for yaml-cpp-config.cmake if(NOT DEFINED yaml-cpp_ROOT) - # Search for yaml-cpp-config.cmake - find_package(yaml-cpp ${yaml-cpp_FIND_VERSION} CONFIG QUIET) + find_package(yaml-cpp ${yaml-cpp_FIND_VERSION} CONFIG ${quiet}) endif() if(yaml-cpp_FOUND) - get_target_property(yaml-cpp_LIBRARY yaml-cpp LOCATION) + # Alias target for yaml-cpp < 0.8 compatibility + if(TARGET yaml-cpp AND NOT TARGET yaml-cpp::yaml-cpp) + add_library(yaml-cpp::yaml-cpp ALIAS yaml-cpp) + endif() + + get_target_property(yaml-cpp_INCLUDE_DIR yaml-cpp::yaml-cpp INTERFACE_INCLUDE_DIRECTORIES) + get_target_property(yaml-cpp_LIBRARY yaml-cpp::yaml-cpp LOCATION) else() # As yaml-cpp-config.cmake search fails, search an installed library @@ -59,14 +68,14 @@ if(NOT OCIO_INSTALL_EXT_PACKAGES STREQUAL ALL) list(APPEND _yaml-cpp_REQUIRED_VARS yaml-cpp_INCLUDE_DIR yaml-cpp_VERSION) # Search for yaml-cpp.pc - find_package(PkgConfig QUIET) - pkg_check_modules(PC_yaml-cpp QUIET "yaml-cpp>=${yaml-cpp_FIND_VERSION}") + find_package(PkgConfig ${quiet}) + pkg_check_modules(PC_yaml-cpp ${quiet} "yaml-cpp>=${yaml-cpp_FIND_VERSION}") # Try to detect the version installed, if any. if(NOT PC_yaml-cpp_FOUND) - pkg_search_module(PC_yaml-cpp QUIET "yaml-cpp") + pkg_search_module(PC_yaml-cpp ${quiet} "yaml-cpp") endif() - + # Find include directory find_path(yaml-cpp_INCLUDE_DIR NAMES @@ -91,7 +100,7 @@ if(NOT OCIO_INSTALL_EXT_PACKAGES STREQUAL ALL) # Prefer static lib names set(_yaml-cpp_STATIC_LIB_NAMES "${CMAKE_STATIC_LIBRARY_PREFIX}yaml-cpp${CMAKE_STATIC_LIBRARY_SUFFIX}") - + # Starting from 0.7.0, all platforms uses the suffix "d" for debug. # See https://github.com/jbeder/yaml-cpp/blob/master/CMakeLists.txt#L141 if(BUILD_TYPE_DEBUG) @@ -127,42 +136,37 @@ if(NOT OCIO_INSTALL_EXT_PACKAGES STREQUAL ALL) include(FindPackageHandleStandardArgs) find_package_handle_standard_args(yaml-cpp - REQUIRED_VARS + REQUIRED_VARS ${_yaml-cpp_REQUIRED_VARS} VERSION_VAR yaml-cpp_VERSION ) -endif() -############################################################################### -### Create target - -if(yaml-cpp_FOUND AND NOT TARGET yaml-cpp) - add_library(yaml-cpp UNKNOWN IMPORTED GLOBAL) - set(_yaml-cpp_TARGET_CREATE TRUE) + mark_as_advanced(yaml-cpp_INCLUDE_DIR yaml-cpp_LIBRARY yaml-cpp_VERSION) endif() ############################################################################### -### Configure target ### +### Create target -if(_yaml-cpp_TARGET_CREATE) - set_target_properties(yaml-cpp PROPERTIES +if (yaml-cpp_FOUND AND NOT TARGET yaml-cpp::yaml-cpp) + add_library(yaml-cpp::yaml-cpp UNKNOWN IMPORTED GLOBAL) + set_target_properties(yaml-cpp::yaml-cpp PROPERTIES IMPORTED_LOCATION ${yaml-cpp_LIBRARY} INTERFACE_INCLUDE_DIRECTORIES ${yaml-cpp_INCLUDE_DIR} ) - mark_as_advanced(yaml-cpp_INCLUDE_DIR yaml-cpp_LIBRARY yaml-cpp_VERSION) -endif() - -############################################################################### -### Set variables for compatibility ### - -if(TARGET yaml-cpp AND NOT TARGET yaml-cpp::yaml-cpp) - add_library(yaml-cpp::yaml-cpp ALIAS yaml-cpp) -endif() - -if(yaml-cpp_INCLUDE_DIR) - set(YAML_CPP_INCLUDE_DIR "${yaml-cpp_INCLUDE_DIR}") -endif() - -set(YAML_CPP_LIBRARIES yaml-cpp::yaml-cpp) + # Required because Installyaml-cpp.cmake creates `yaml-cpp::yaml-cpp` + # as an alias, and aliases get resolved in exported targets, causing the + # find_dependency(yaml-cpp) call in OpenColorIOConfig.cmake to fail. + # This can be removed once Installyaml-cpp.cmake targets yaml-cpp 0.8. + if (NOT TARGET yaml-cpp) + add_library(yaml-cpp ALIAS yaml-cpp::yaml-cpp) + endif () +endif () + +if (yaml-cpp_FOUND) + # TODO: Remove this variable and use the `yaml-cpp::yaml-cpp` target + # directly when the minimum version of yaml-cpp is updated to 0.8. + get_target_property(YAML_CPP_INCLUDE_DIR yaml-cpp::yaml-cpp INCLUDE_DIRECTORIES) + set(YAML_CPP_LIBRARIES yaml-cpp::yaml-cpp) +endif () diff --git a/share/cmake/modules/install/InstallOpenEXR.cmake b/share/cmake/modules/install/InstallOpenEXR.cmake index 44109ea17..be9a70872 100644 --- a/share/cmake/modules/install/InstallOpenEXR.cmake +++ b/share/cmake/modules/install/InstallOpenEXR.cmake @@ -110,7 +110,7 @@ if(NOT OpenEXR_FOUND AND OCIO_INSTALL_EXT_PACKAGES AND NOT OCIO_INSTALL_EXT_PACK -DBUILD_SHARED_LIBS=OFF -DBUILD_TESTING=OFF -DOPENEXR_INSTALL_EXAMPLES=OFF - -DOPENEXR_INSTALL_TOOLS=OFF + -DOPENEXR_BUILD_TOOLS=OFF # Try to use in-source built Imath first, if available. -DCMAKE_PREFIX_PATH=${_EXT_DIST_ROOT} ) diff --git a/share/cmake/modules/install/Installpystring.cmake b/share/cmake/modules/install/Installpystring.cmake index 3045e4fbc..48ca15229 100644 --- a/share/cmake/modules/install/Installpystring.cmake +++ b/share/cmake/modules/install/Installpystring.cmake @@ -38,7 +38,7 @@ if(NOT pystring_FOUND AND OCIO_INSTALL_EXT_PACKAGES AND NOT OCIO_INSTALL_EXT_PAC else() set(pystring_VERSION ${pystring_FIND_VERSION}) endif() - set(pystring_INCLUDE_DIR "${_EXT_DIST_ROOT}/${CMAKE_INSTALL_INCLUDEDIR}") + set(pystring_INCLUDE_DIR "${_EXT_DIST_ROOT}/${CMAKE_INSTALL_INCLUDEDIR}/pystring") set(pystring_LIBRARY "${_EXT_DIST_ROOT}/${CMAKE_INSTALL_LIBDIR}/${CMAKE_STATIC_LIBRARY_PREFIX}pystring${CMAKE_STATIC_LIBRARY_SUFFIX}") diff --git a/share/cmake/modules/install/Installyaml-cpp.cmake b/share/cmake/modules/install/Installyaml-cpp.cmake index a117fdcff..6b0549799 100644 --- a/share/cmake/modules/install/Installyaml-cpp.cmake +++ b/share/cmake/modules/install/Installyaml-cpp.cmake @@ -21,8 +21,8 @@ ############################################################################### ### Create target (if previous 'find_package' call hasn't) ### -if(NOT TARGET yaml-cpp) - add_library(yaml-cpp UNKNOWN IMPORTED GLOBAL) +if(NOT TARGET yaml-cpp::yaml-cpp) + add_library(yaml-cpp::yaml-cpp UNKNOWN IMPORTED GLOBAL) set(_yaml-cpp_TARGET_CREATE TRUE) endif() @@ -138,7 +138,7 @@ if(NOT yaml-cpp_FOUND AND OCIO_INSTALL_EXT_PACKAGES AND NOT OCIO_INSTALL_EXT_PAC --parallel ) - add_dependencies(yaml-cpp yaml-cpp_install) + add_dependencies(yaml-cpp::yaml-cpp yaml-cpp_install) if(OCIO_VERBOSE) message(STATUS "Installing yaml-cpp: ${yaml-cpp_LIBRARY} (version \"${yaml-cpp_VERSION}\")") endif() @@ -149,7 +149,7 @@ endif() ### Configure target ### if(_yaml-cpp_TARGET_CREATE) - set_target_properties(yaml-cpp PROPERTIES + set_target_properties(yaml-cpp::yaml-cpp PROPERTIES IMPORTED_LOCATION ${yaml-cpp_LIBRARY} INTERFACE_INCLUDE_DIRECTORIES ${yaml-cpp_INCLUDE_DIR} ) @@ -157,15 +157,5 @@ if(_yaml-cpp_TARGET_CREATE) mark_as_advanced(yaml-cpp_INCLUDE_DIR yaml-cpp_LIBRARY yaml-cpp_VERSION) endif() -############################################################################### -### Set variables for compatibility ### - -if(TARGET yaml-cpp AND NOT TARGET yaml-cpp::yaml-cpp) - add_library(yaml-cpp::yaml-cpp ALIAS yaml-cpp) -endif() - -if(yaml-cpp_INCLUDE_DIR) - set(YAML_CPP_INCLUDE_DIR "${yaml-cpp_INCLUDE_DIR}") -endif() - +set(YAML_CPP_INCLUDE_DIR "${yaml-cpp_INCLUDE_DIR}") set(YAML_CPP_LIBRARIES yaml-cpp::yaml-cpp) diff --git a/share/cmake/utils/CheckSupportSSE2.cmake b/share/cmake/utils/CheckSupportSSE2.cmake index 8c929c7e9..6a07155de 100644 --- a/share/cmake/utils/CheckSupportSSE2.cmake +++ b/share/cmake/utils/CheckSupportSSE2.cmake @@ -59,7 +59,7 @@ else() message(STATUS "Performing Test COMPILER_SUPPORTS_SSE2 - Failed") endif() -set(CMAKE_REQUIRED_FLAGS "${_cmake_cxx_flags_orig}") +set(CMAKE_CXX_FLAGS "${_cmake_cxx_flags_orig}") unset(_cmake_cxx_flags_orig) if(__universal_build) diff --git a/share/cmake/utils/CompilerFlags.cmake b/share/cmake/utils/CompilerFlags.cmake index b53d6bb35..91e438f45 100644 --- a/share/cmake/utils/CompilerFlags.cmake +++ b/share/cmake/utils/CompilerFlags.cmake @@ -107,8 +107,8 @@ if(USE_GCC OR USE_CLANG) endif() if(OCIO_ENABLE_SANITIZER) - set(PLATFORM_COMPILE_OPTIONS "${PLATFORM_COMPILE_OPTIONS};-fno-omit-frame-pointer;-fsanitize=address") - set(PLATFORM_LINK_OPTIONS "${PLATFORM_LINK_OPTIONS};-fsanitize=address") + set(PLATFORM_COMPILE_OPTIONS "${PLATFORM_COMPILE_OPTIONS};-fno-omit-frame-pointer;-fsanitize=address;-fsanitize=undefined") + set(PLATFORM_LINK_OPTIONS "${PLATFORM_LINK_OPTIONS};-fsanitize=address;-fsanitize=undefined") endif() endif() diff --git a/src/OpenColorIO/AVX.h b/src/OpenColorIO/AVX.h index 6cb2ea588..b4184f89d 100644 --- a/src/OpenColorIO/AVX.h +++ b/src/OpenColorIO/AVX.h @@ -9,7 +9,6 @@ #if OCIO_USE_AVX #include -#include #include #include "BitDepthUtils.h" diff --git a/src/OpenColorIO/AVX2.h b/src/OpenColorIO/AVX2.h index 3237533bc..85bf48dc8 100644 --- a/src/OpenColorIO/AVX2.h +++ b/src/OpenColorIO/AVX2.h @@ -9,7 +9,6 @@ #if OCIO_USE_AVX2 #include -#include #include #include "BitDepthUtils.h" diff --git a/src/OpenColorIO/CMakeLists.txt b/src/OpenColorIO/CMakeLists.txt index 7ff40bfd7..26b4bb4cf 100755 --- a/src/OpenColorIO/CMakeLists.txt +++ b/src/OpenColorIO/CMakeLists.txt @@ -389,7 +389,7 @@ elseif(APPLE) # Check for minizip first since our Findminizip module sets minizip-ng_LIBRARY. - if (minizip_LIBRARY) + if (minizip_LIBRARY OR minizip-ng_COMPAT) get_filename_component(_minizip-ng_LIBDIR "${minizip_LIBRARY}" DIRECTORY) set(_minizip-ng_NAME "minizip") elseif(minizip-ng_LIBRARY) diff --git a/src/OpenColorIO/CPUInfo.cpp b/src/OpenColorIO/CPUInfo.cpp index 7aae56ad9..28fcd9477 100644 --- a/src/OpenColorIO/CPUInfo.cpp +++ b/src/OpenColorIO/CPUInfo.cpp @@ -50,6 +50,13 @@ static inline void cpuid(int index, int *data) { #if _MSC_VER __cpuid(data, index); +#elif OCIO_ARCH_X86_32 + __asm__ volatile ( + "mov %%ebx, %%esi \n\t" + "cpuid \n\t" + "xchg %%ebx, %%esi" + : "=a" (data[0]), "=S" (data[1]), "=c" (data[2]), "=d" (data[3]) + : "0" (index), "2"(0)); #else __asm__ volatile ( "mov %%rbx, %%rsi \n\t" diff --git a/src/OpenColorIO/CPUInfoConfig.h.in b/src/OpenColorIO/CPUInfoConfig.h.in index a94a7bb84..b8f5045d2 100644 --- a/src/OpenColorIO/CPUInfoConfig.h.in +++ b/src/OpenColorIO/CPUInfoConfig.h.in @@ -3,10 +3,13 @@ #cmakedefine01 OCIO_ARCH_X86 +#cmakedefine01 OCIO_ARCH_X86_32 // Relevant only for arm64 architecture. #if defined(__aarch64__) #cmakedefine01 OCIO_USE_SSE2NEON +#else + #define OCIO_USE_SSE2NEON 0 #endif // On the Apple platform, a universal build is created for both x86_64 and arm64 architectures. diff --git a/src/OpenColorIO/Config.cpp b/src/OpenColorIO/Config.cpp index f09b9ee71..03119d12b 100644 --- a/src/OpenColorIO/Config.cpp +++ b/src/OpenColorIO/Config.cpp @@ -12,6 +12,8 @@ #include #include +#include + #include #include "builtinconfigs/BuiltinConfigRegistry.h" @@ -34,7 +36,6 @@ #include "Platform.h" #include "PrivateTypes.h" #include "Processor.h" -#include "pystring/pystring.h" #include "transforms/FileTransform.h" #include "utils/StringUtils.h" #include "ViewingRules.h" @@ -435,7 +436,7 @@ class Config::Impl { m_viewTransforms.push_back(vt->createEditableCopy()); } - + m_defaultViewTransform = rhs.m_defaultViewTransform; m_defaultLumaCoefs = rhs.m_defaultLumaCoefs; m_strictParsing = rhs.m_strictParsing; @@ -2821,6 +2822,23 @@ bool Config::isColorSpaceLinear(const char * colorSpace, ReferenceSpaceType refe auto procToReference = config.getImpl()->getProcessorWithoutCaching( config, t, TRANSFORM_DIR_FORWARD ); + + // TODO: It could be useful to try and avoid evaluating points through ops that are + // expensive but highly unlikely to be linear (with inverse Lut3D being the prime example). + // There are some heuristics that are used in ConfigUtils.cpp that are intended to filter + // out color spaces from consideration before a processor is even calculated. However, + // those are not entirely appropriate here since one could imagine wanting to know if a + // color space involving a FileTransform (an ASC CDL being a good example) is linear. + // Likewise, one might want to know whether a color space involving a Look or ColorSpace + // Transform is linear (both of those are filtered out by the ConfigUtils heuristics). + // It seems like the right approach here is to go ahead and build the processor and + // thereby convert File/Look/ColorSpace Transforms into ops. But currently there is + // no method on the Processor class to know if it contains a Lut3D. There is the + // ProcessorMetadata files list, though that is not as precise. For example, it would + // not say if a CLF or CTF file contains a Lut3D or just a matrix. Probably the best + // solution would be to have each op sub-class provide an isLinear method and then + // surface that on the Processor class, iterating over each op in the Processor. + auto optCPUProc = procToReference->getOptimizedCPUProcessor(OPTIMIZATION_NONE); optCPUProc->apply(desc, descDst); @@ -4100,6 +4118,10 @@ void Config::addLook(const ConstLookRcPtr & look) if(StringUtils::Lower(getImpl()->m_looksList[i]->getName()) == namelower) { getImpl()->m_looksList[i] = look->createEditableCopy(); + + AutoMutex lock(getImpl()->m_cacheidMutex); + getImpl()->resetCacheIDs(); + return; } } @@ -4715,20 +4737,29 @@ ConstProcessorRcPtr Config::GetProcessorFromConfigs(const ConstContextRcPtr & sr "the source color space."); } + const char* csName = dstConfig->getDisplayViewColorSpaceName(dstDisplay, dstView); + const char* displayColorSpaceName = View::UseDisplayName(csName) ? dstDisplay : csName; + ConstColorSpaceRcPtr displayColorSpace = dstConfig->getColorSpace(displayColorSpaceName); + if (!displayColorSpace) + { + throw Exception("Can't create the processor for the destination config: " + "display color space not found."); + } + auto p2 = dstConfig->getProcessor(dstContext, dstInterchangeName, dstDisplay, dstView, direction); if (!p2) { throw Exception("Can't create the processor for the destination config " - "and the destination color space."); + "and the destination display view transform."); } ProcessorRcPtr processor = Processor::Create(); processor->getImpl()->setProcessorCacheFlags(srcConfig->getImpl()->m_cacheFlags); - // If the source color spaces is a data space, its corresponding processor + // If either of the color spaces are data spaces, its corresponding processor // will be empty, but need to make sure the entire result is also empty to // better match the semantics of how data spaces are handled. - if (!srcColorSpace->isData()) + if (!srcColorSpace->isData() && !displayColorSpace->isData()) { if (direction == TRANSFORM_DIR_INVERSE) { diff --git a/src/OpenColorIO/ConfigUtils.cpp b/src/OpenColorIO/ConfigUtils.cpp index 959e0eb6c..2e774726f 100644 --- a/src/OpenColorIO/ConfigUtils.cpp +++ b/src/OpenColorIO/ConfigUtils.cpp @@ -3,6 +3,7 @@ #include "ConfigUtils.h" #include "MathUtils.h" +#include "pystring/pystring.h" #include "utils/StringUtils.h" namespace OCIO_NAMESPACE @@ -165,7 +166,7 @@ bool GetInterchangeRolesForColorSpaceConversion(const char ** srcInterchangeCSNa // Return true if the color space name or its aliases contains sRGB (case-insensitive). // -bool containsSRGB(ConstColorSpaceRcPtr & cs) +bool containsSRGB(const ConstColorSpaceRcPtr & cs) { std::string name = StringUtils::Lower(cs->getName()); if (StringUtils::Find(name, "srgb") != std::string::npos) @@ -223,10 +224,31 @@ const char * getRefSpaceName(const ConstConfigRcPtr & cfg) return ""; } +// Find the first scene-referred color space with isdata: true. +// +const char * getDataSpaceName(const ConstConfigRcPtr & cfg) +{ + int nbCs = cfg->getNumColorSpaces(SEARCH_REFERENCE_SPACE_SCENE, COLORSPACE_ALL); + + for (int i = 0; i < nbCs; i++) + { + const char * csname = cfg->getColorSpaceNameByIndex(SEARCH_REFERENCE_SPACE_SCENE, + COLORSPACE_ALL, + i); + ConstColorSpaceRcPtr cs = cfg->getColorSpace(csname); + + if (cs->isData()) + { + return csname; + } + } + return ""; +} + // Return false if the supplied Processor modifies any of the supplied float values // by more than the supplied absolute tolerance amount. // -bool isIdentityTransform(ConstProcessorRcPtr proc, +bool isIdentityTransform(const ConstProcessorRcPtr & proc, std::vector & RGBAvals, float absTolerance) { @@ -249,30 +271,145 @@ bool isIdentityTransform(ConstProcessorRcPtr proc, return true; } -// Helper to avoid color spaces that are without transforms or are data spaces. +// Determine whether a Processor contains a MatrixTransform with off-diagonal coefficients. // -bool hasNoTransform(const ConstColorSpaceRcPtr & cs) +bool hasNonTrivialMatrixTransform(const ConstProcessorRcPtr & proc) { - if (cs->isData()) + GroupTransformRcPtr gt = proc->createGroupTransform(); + // The result of createGroupTransform only contains transforms that correspond to ops, + // in other words, there are no complex transforms such as File, Builtin, or ColorSpace, + // and the only GroupTransform is the enclosing one. + for (int i = 0; i < gt->getNumTransforms(); ++i) { - return true; + ConstTransformRcPtr transform = gt->getTransform(i); + if (transform->getTransformType() == TRANSFORM_TYPE_MATRIX) + { + // Check that there is a significant off-diagonal coefficient in the matrix. + // This is to avoid matrices that are not actual color primary conversions, + // for example, the scale and offset that are sometimes prepended to a Lut1D. + ConstMatrixTransformRcPtr mtx = DynamicPtrCast(transform); + double values[16]; + mtx->getMatrix(values); + for (int j = 0; j < 3 ; ++j) // only checking rgb, not alpha + { + for (int k = 0; k < 3 ; ++k) // only checking rgb, not alpha + { + if (j != k) + { + if (std::abs(values[j * 4 + k]) > 0.1) + { + return true; + } + } + } + } + } } - ConstTransformRcPtr srcTransform = cs->getTransform(COLORSPACE_DIR_TO_REFERENCE); - if (!srcTransform) + return false; +} + +// Determine if the transform contains a type that is inappropriate for the heuristics. +// +bool containsBlockedTransform(const ConstTransformRcPtr & transform) +{ + // If it's a GroupTransform, need to recurse into it to check the contents. + if (transform->getTransformType() == TRANSFORM_TYPE_GROUP) { - srcTransform = cs->getTransform(COLORSPACE_DIR_FROM_REFERENCE); - if (srcTransform) + ConstGroupTransformRcPtr gt = DynamicPtrCast(transform); + for (int i = 0; i < gt->getNumTransforms(); ++i) { - return false; + ConstTransformRcPtr tr = gt->getTransform(i); + if (containsBlockedTransform(tr)) + { + return true; + } } - else + } + + // Prevent FileTransforms from being used, except for spi1d and spimtx since these + // may be used with OCIO v1 configs to implement the type of color spaces the heuristics + // are designed to look for. (E.g. The sRGB Texture space in the legacy ACES configs.) + else if (transform->getTransformType() == TRANSFORM_TYPE_FILE) + { + ConstFileTransformRcPtr ft = DynamicPtrCast(transform); + std::string filepath = ft->getSrc(); + std::string root, extension; + pystring::os::path::splitext(root, extension, filepath); + extension = StringUtils::Lower(extension); + if (extension != ".spi1d" && extension != ".spimtx") { return true; } } + + // Prevent transforms that may be hiding a FileTransform. + else if (transform->getTransformType() == TRANSFORM_TYPE_COLORSPACE + || transform->getTransformType() == TRANSFORM_TYPE_DISPLAY_VIEW + || transform->getTransformType() == TRANSFORM_TYPE_LOOK) + { + return true; + } + + // Putting this here since Lut3D is the main type of transform to avoid, + // however given that the input transform comes directly from a color space + // and has not been converted to a processor yet, it should never actually + // have a transform of this type (it would still be a FileTransform). + else if (transform->getTransformType() == TRANSFORM_TYPE_LUT3D) + { + return true; + } return false; } +// Look at the to_ref/from_ref transforms in the color space and exclude color spaces +// that are probably not what the heuristics are looking for and could be prohibitively +// expensive to fully check. +// +// Because this check is done before a processor is built, it is inexpensive but it may +// be inaccurate. In other words, it's possible that this check will exclude some +// reasonable color spaces, but that's better than trying to invert 3d-LUTs, etc. +// +// cs -- Color space object to check. +// refSpaceType -- Exclude if the color space is not of the same reference space type. +// blockRefSpaces -- Exclude the color space if it does not have any transforms. +// +bool excludeColorSpaceFromHeuristics(const ConstColorSpaceRcPtr & cs, + ReferenceSpaceType refSpaceType, + bool blockRefSpaces) +{ + if (cs->isData()) + { + return true; + } + + if (cs->getReferenceSpaceType() != refSpaceType) + { + return true; + } + + ConstTransformRcPtr transform = cs->getTransform(COLORSPACE_DIR_TO_REFERENCE); + if (transform) + { + // The to_ref transform is what the heuristics try to use first, so if that + // does not contain problematic transforms, it's ok to proceed without checking + // the from_ref transform. + + return containsBlockedTransform(transform); + } + + // There is no to_ref transform, check if the from_ref is present. + transform = cs->getTransform(COLORSPACE_DIR_FROM_REFERENCE); + if (transform) + { + return containsBlockedTransform(transform); + } + // Color space contains no transforms (it's a reference space). + else + { + return blockRefSpaces; + } +} + // Test the supplied color space against a set of color spaces in the built-in config. // If a match is found, it indicates what reference space is used by the config. // Return the index into the list of built-in linear spaces, or -1 if not found. @@ -288,17 +425,6 @@ int getReferenceSpaceFromLinearSpace(const ConstConfigRcPtr & srcConfig, const ConstColorSpaceRcPtr & cs, const ConstConfigRcPtr & builtinConfig) { - // Currently only handling scene-referred spaces in the heuristics. - if (cs->getReferenceSpaceType() == REFERENCE_SPACE_DISPLAY) - { - return -1; - } - // Don't check spaces without transforms / data spaces. - if (hasNoTransform(cs)) - { - return -1; - } - // Define a set of (somewhat arbitrary) RGB values to test whether the combined transform is // enough of an identity. std::vector vals = { 0.7f, 0.4f, 0.02f, 0.f, @@ -353,14 +479,9 @@ int getReferenceSpaceFromLinearSpace(const ConstConfigRcPtr & srcConfig, // int getReferenceSpaceFromSRGBSpace(const ConstConfigRcPtr & srcConfig, const char * srcRefName, - const ConstColorSpaceRcPtr cs, + const ConstColorSpaceRcPtr & cs, const ConstConfigRcPtr & builtinConfig) { - // Currently only handling scene-referred spaces in the heuristics. - if (cs->getReferenceSpaceType() == REFERENCE_SPACE_DISPLAY) - { - return -1; - } // Get a transform in the to-reference direction. ConstTransformRcPtr toRefTransform; ConstTransformRcPtr ctransform = cs->getTransform(COLORSPACE_DIR_TO_REFERENCE); @@ -405,6 +526,18 @@ int getReferenceSpaceFromSRGBSpace(const ConstConfigRcPtr & srcConfig, ConstProcessorRcPtr proc = srcConfig->getProcessor(toRefTransform, TRANSFORM_DIR_FORWARD); + // Ensure that the color space is not only an sRGB curve, it needs to have a color matrix + // too or else the last step below could succeed by pairing the built-in sRGB space with + // a linear space that cancels out the matrix in the built-in sRGB space. + // NB: This is being done after the getProcessor call rather than simply looking at + // the raw color space transform contents since once it becomes a processor, the complex + // transforms (e.g. File, ColorSpace, Builtins) that could be hiding a matrix are + // converted into ops. + if (!hasNonTrivialMatrixTransform(proc)) + { + return -1; + } + ConstCPUProcessorRcPtr cpu = proc->getOptimizedCPUProcessor(OPTIMIZATION_NONE); // Convert the non-linear values to linear. cpu->apply(desc, descDst); @@ -422,6 +555,7 @@ int getReferenceSpaceFromSRGBSpace(const ConstConfigRcPtr & srcConfig, out[i] = 1.055f * std::pow(out[i], 1.f / 2.4f) - 0.055f; } // Compare against the original source values. + // (This assumes equal channel sRGB values remain so in the reference space of src config.) if (!EqualWithAbsError(vals[i], out[i], 1e-3f)) { return -1; @@ -442,6 +576,8 @@ int getReferenceSpaceFromSRGBSpace(const ConstConfigRcPtr & srcConfig, // sRGB texture space. If the result is an identity, then that tells what the source config // reference space is. + // At this point, cs has the expected non-linearity and a non-trivial matrix to its reference space. + // Now try to identify the matrix. for (int i = 0; i < getNumberOfbuiltinLinearSpaces(); i++) { ConstProcessorRcPtr proc = Config::GetProcessorFromConfigs(srcConfig, @@ -511,7 +647,7 @@ void IdentifyInterchangeSpace(const char ** srcInterchange, // Identify the name of a reference space in the source config. *srcInterchange = getRefSpaceName(srcConfig); - if (!*srcInterchange) + if (!**srcInterchange) { std::ostringstream os; os << "The supplied config does not have a color space for the reference."; @@ -531,8 +667,16 @@ void IdentifyInterchangeSpace(const char ** srcInterchange, for (int i = 0; i < nbCs; i++) { ConstColorSpaceRcPtr cs = srcConfig->getColorSpace(srcConfig->getColorSpaceNameByIndex(i)); + if (containsSRGB(cs)) { + // Exclude color spaces that may be too expensive to test or otherwise inappropriate. + // Currently only handling scene-referred spaces in the heuristics. + if (excludeColorSpaceFromHeuristics(cs, REFERENCE_SPACE_SCENE, true)) + { + continue; + } + refColorSpacePrimsIndex = getReferenceSpaceFromSRGBSpace(srcConfig, *srcInterchange, cs, @@ -549,6 +693,14 @@ void IdentifyInterchangeSpace(const char ** srcInterchange, for (int i = 0; i < nbCs; i++) { ConstColorSpaceRcPtr cs = srcConfig->getColorSpace(srcConfig->getColorSpaceNameByIndex(i)); + + // Exclude color spaces that may be too expensive to test or otherwise inappropriate. + // Currently only handling scene-referred spaces in the heuristics. + if (excludeColorSpaceFromHeuristics(cs, REFERENCE_SPACE_SCENE, true)) + { + continue; + } + if (srcConfig->isColorSpaceLinear(cs->getName(), REFERENCE_SPACE_SCENE)) { refColorSpacePrimsIndex = getReferenceSpaceFromLinearSpace(srcConfig, @@ -602,6 +754,18 @@ const char * IdentifyBuiltinColorSpace(const ConstConfigRcPtr & srcConfig, throw Exception(os.str().c_str()); } + if (builtinColorSpace->isData()) + { + const char * dataName = getDataSpaceName(srcConfig); + if (!*dataName) + { + std::ostringstream os; + os << "The requested space is a data space but the supplied config does not have a data space."; + throw Exception(os.str().c_str()); + } + return dataName; + } + ReferenceSpaceType builtinRefSpaceType = builtinColorSpace->getReferenceSpaceType(); // Identify interchange spaces. Passing an empty string for the source color space @@ -633,8 +797,8 @@ const char * IdentifyBuiltinColorSpace(const ConstConfigRcPtr & srcConfig, 0.f, 0.f, 0.f, 0.f, 1.f, 1.f, 1.f, 0.f }; - // Loop over each non-data color space in the source config and test if the conversion to - // the specified space in the built-in config is an identity. + // Loop over the active, non-excluded, color spaces in the source config and test if the + // conversion to the specified space in the built-in config is an identity. // // Note that there is a possibility that both the source and built-in sides of the // transform could be an identity (e.g., if the user asks for ACES2065-1 and that is @@ -646,7 +810,8 @@ const char * IdentifyBuiltinColorSpace(const ConstConfigRcPtr & srcConfig, for (int i = 0; i < nbCs; i++) { ConstColorSpaceRcPtr cs = srcConfig->getColorSpace(srcConfig->getColorSpaceNameByIndex(i)); - if (cs->isData() || (cs->getReferenceSpaceType() != builtinRefSpaceType)) + + if (excludeColorSpaceFromHeuristics(cs, builtinRefSpaceType, false)) { continue; } diff --git a/src/OpenColorIO/Context.cpp b/src/OpenColorIO/Context.cpp index bb6fb07f3..3a1294be5 100644 --- a/src/OpenColorIO/Context.cpp +++ b/src/OpenColorIO/Context.cpp @@ -7,6 +7,8 @@ #include #include +#include + #include #include "ContextVariableUtils.h" @@ -15,7 +17,6 @@ #include "OCIOZArchive.h" #include "PathUtils.h" #include "PrivateTypes.h" -#include "pystring/pystring.h" #include "utils/StringUtils.h" namespace OCIO_NAMESPACE diff --git a/src/OpenColorIO/OCIOYaml.cpp b/src/OpenColorIO/OCIOYaml.cpp index 62cbb0d00..8681631a7 100644 --- a/src/OpenColorIO/OCIOYaml.cpp +++ b/src/OpenColorIO/OCIOYaml.cpp @@ -4,6 +4,8 @@ #include #include +#include + #include #include "Display.h" @@ -19,7 +21,6 @@ #include "ParseUtils.h" #include "PathUtils.h" #include "Platform.h" -#include "pystring/pystring.h" #include "utils/StringUtils.h" #include "ViewingRules.h" #include "yaml-cpp/yaml.h" @@ -4737,9 +4738,9 @@ inline void save(YAML::Emitter & out, const Config & config) out << YAML::Newline; out << YAML::Newline; - if (configMajorVersion >= 2) + if (configMajorVersion >= 2 || config.getNumEnvironmentVars() > 0) { - // Print the environment even if empty. + // For v2 configs, write the environment section, even if empty. out << YAML::Key << "environment"; out << YAML::Value << YAML::BeginMap; for(int i = 0; i < config.getNumEnvironmentVars(); ++i) diff --git a/src/OpenColorIO/OCIOZArchive.cpp b/src/OpenColorIO/OCIOZArchive.cpp index 24d836e95..982fce682 100644 --- a/src/OpenColorIO/OCIOZArchive.cpp +++ b/src/OpenColorIO/OCIOZArchive.cpp @@ -8,10 +8,12 @@ #include #include +#include + #include + #include "Mutex.h" #include "Platform.h" -#include "pystring/pystring.h" #include "utils/StringUtils.h" #include "transforms/FileTransform.h" @@ -24,7 +26,6 @@ #include "mz_strm_mem.h" #include "mz_strm_os.h" #include "mz_strm_split.h" -#include "mz_strm_zlib.h" #include "mz_zip.h" #include "mz_zip_rw.h" diff --git a/src/OpenColorIO/Op.cpp b/src/OpenColorIO/Op.cpp index e1bd564ff..81dfe9867 100755 --- a/src/OpenColorIO/Op.cpp +++ b/src/OpenColorIO/Op.cpp @@ -4,6 +4,8 @@ #include #include +#include + #include #include "Logging.h" @@ -20,7 +22,6 @@ #include "ops/lut1d/Lut1DOp.h" #include "ops/lut3d/Lut3DOp.h" #include "ops/range/RangeOp.h" -#include "pystring/pystring.h" namespace OCIO_NAMESPACE { diff --git a/src/OpenColorIO/PathUtils.cpp b/src/OpenColorIO/PathUtils.cpp index 9dc8c6b74..bd9fb1135 100644 --- a/src/OpenColorIO/PathUtils.cpp +++ b/src/OpenColorIO/PathUtils.cpp @@ -5,12 +5,13 @@ #include #include +#include + #include #include "Mutex.h" #include "PathUtils.h" #include "Platform.h" -#include "pystring/pystring.h" #include "utils/StringUtils.h" #include "OCIOZArchive.h" @@ -218,4 +219,3 @@ int ParseColorSpaceFromString(const Config & config, const char * str) } } // namespace OCIO_NAMESPACE - diff --git a/src/OpenColorIO/SSE2.h b/src/OpenColorIO/SSE2.h index 2527ff084..918694fc8 100644 --- a/src/OpenColorIO/SSE2.h +++ b/src/OpenColorIO/SSE2.h @@ -18,8 +18,6 @@ #endif #endif -#include - #include #include "BitDepthUtils.h" @@ -76,6 +74,8 @@ static inline void sse2RGBATranspose_4x4(__m128 row0, __m128 row1, __m128 row2, out_a = _mm_movehl_ps(tmp3, tmp1); } +#if !OCIO_USE_SSE2NEON + static inline __m128i sse2_blendv(__m128i a, __m128i b, __m128i mask) { return _mm_xor_si128(_mm_and_si128(_mm_xor_si128(a, b), mask), a); @@ -164,6 +164,8 @@ static inline __m128 sse2_cvtph_ps(__m128i a) return _mm_or_ps(o, sign); } +#endif + // Note Packing functions perform no 0.0 - 1.0 normalization // but perform 0 - max value clamping for integer formats template struct SSE2RGBAPack {}; @@ -290,21 +292,48 @@ struct SSE2RGBAPack __m128i rgba_00_01 = _mm_loadu_si128((const __m128i*)(in + 0)); __m128i rgba_02_03 = _mm_loadu_si128((const __m128i*)(in + 8)); +#if OCIO_USE_SSE2NEON + // use neon hardware support for f16 to f32 + __m128 rgba0 = vreinterpretq_m128_f32( + vcvt_f32_f16(vget_low_f16(vreinterpretq_f16_s64(vreinterpretq_s64_m128i(rgba_00_01)))) + ); + __m128 rgba1 = vreinterpretq_m128_f32( + vcvt_f32_f16(vget_high_f16(vreinterpretq_f16_s64(vreinterpretq_s64_m128i(rgba_00_01)))) + ); + __m128 rgba2 = vreinterpretq_m128_f32( + vcvt_f32_f16(vget_low_f16(vreinterpretq_f16_s64(vreinterpretq_s64_m128i(rgba_02_03)))) + ); + __m128 rgba3 = vreinterpretq_m128_f32( + vcvt_f32_f16(vget_high_f16(vreinterpretq_f16_s64(vreinterpretq_s64_m128i(rgba_02_03)))) + ); +#else __m128 rgba0 = sse2_cvtph_ps(rgba_00_01); __m128 rgba1 = sse2_cvtph_ps(_mm_shuffle_epi32(rgba_00_01, _MM_SHUFFLE(1,0,3,2))); __m128 rgba2 = sse2_cvtph_ps(rgba_02_03); __m128 rgba3 = sse2_cvtph_ps(_mm_shuffle_epi32(rgba_02_03, _MM_SHUFFLE(1,0,3,2))); - +#endif sse2RGBATranspose_4x4(rgba0, rgba1, rgba2, rgba3, r, g, b, a); } static inline void Store(half *out, __m128 r, __m128 g, __m128 b, __m128 a) { __m128 rgba0, rgba1, rgba2, rgba3; - __m128i rgba; - sse2RGBATranspose_4x4(r, g, b, a, rgba0, rgba1, rgba2, rgba3); +#if OCIO_USE_SSE2NEON + // use neon hardware support for f32 to f16 + float16x8_t rgba; + float16x4_t rgba00_01 = vcvt_f16_f32(vreinterpretq_f32_m128(rgba0)); + float16x4_t rgba03_03 = vcvt_f16_f32(vreinterpretq_f32_m128(rgba1)); + float16x4_t rgba04_05 = vcvt_f16_f32(vreinterpretq_f32_m128(rgba2)); + float16x4_t rgba06_07 = vcvt_f16_f32(vreinterpretq_f32_m128(rgba3)); + rgba = vcombine_f16(rgba00_01, rgba03_03); + vst1q_f16((float16_t *)(out+0), rgba); + + rgba = vcombine_f16(rgba04_05, rgba06_07); + vst1q_f16((float16_t *)(out+8), rgba); +#else + __m128i rgba; __m128i rgba00_01 = sse2_cvtps_ph(rgba0); __m128i rgba02_03 = sse2_cvtps_ph(rgba1); __m128i rgba04_05 = sse2_cvtps_ph(rgba2); @@ -315,6 +344,7 @@ struct SSE2RGBAPack rgba = _mm_xor_si128(rgba04_05, _mm_shuffle_epi32(rgba06_07, _MM_SHUFFLE(1,0,3,2))); _mm_storeu_si128((__m128i*)(out+8), rgba); +#endif } }; diff --git a/src/OpenColorIO/fileformats/FileFormatCCC.cpp b/src/OpenColorIO/fileformats/FileFormatCCC.cpp index 3670c57bc..bad7723ef 100755 --- a/src/OpenColorIO/fileformats/FileFormatCCC.cpp +++ b/src/OpenColorIO/fileformats/FileFormatCCC.cpp @@ -217,7 +217,7 @@ void LocalFileFormat::buildFileOps(OpRcPtrVec & ops, { // Use 0 for empty string. int cccindex=0; - if (StringToInt(&cccindex, cccid.c_str(), true)) + if (cccid.empty() || StringToInt(&cccindex, cccid.c_str(), true)) { int maxindex = ((int)cachedFile->m_transformVec.size())-1; if (cccindex<0 || cccindex>maxindex) diff --git a/src/OpenColorIO/fileformats/FileFormatCDL.cpp b/src/OpenColorIO/fileformats/FileFormatCDL.cpp index 3a687a1c5..5da6aae95 100755 --- a/src/OpenColorIO/fileformats/FileFormatCDL.cpp +++ b/src/OpenColorIO/fileformats/FileFormatCDL.cpp @@ -247,7 +247,7 @@ LocalFileFormat::buildFileOps(OpRcPtrVec & ops, { // Use 0 for empty string. int cccindex=0; - if (StringToInt(&cccindex, cccid.c_str(), true)) + if (cccid.empty() || StringToInt(&cccindex, cccid.c_str(), true)) { int maxindex = ((int)cachedFile->m_transformVec.size())-1; if (cccindex<0 || cccindex>maxindex) @@ -274,7 +274,7 @@ LocalFileFormat::buildFileOps(OpRcPtrVec & ops, if (!success) { std::ostringstream os; - os << "You must specify a valid cccid to load from the ccc file"; + os << "You must specify a valid cccid to load from the cdl file"; os << " (either by name or index). id='" << cccid << "' "; os << "is not found in the file, and is not parsable as an "; os << "integer index."; diff --git a/src/OpenColorIO/fileformats/FileFormatCTF.cpp b/src/OpenColorIO/fileformats/FileFormatCTF.cpp index ebed326c9..c9ada57cd 100644 --- a/src/OpenColorIO/fileformats/FileFormatCTF.cpp +++ b/src/OpenColorIO/fileformats/FileFormatCTF.cpp @@ -6,6 +6,8 @@ #include #include +#include + #include #include "expat.h" @@ -23,7 +25,6 @@ #include "OpBuilders.h" #include "ops/noop/NoOps.h" #include "Platform.h" -#include "pystring/pystring.h" #include "TransformBuilder.h" #include "transforms/FileTransform.h" #include "utils/StringUtils.h" diff --git a/src/OpenColorIO/fileformats/FileFormatDiscreet1DL.cpp b/src/OpenColorIO/fileformats/FileFormatDiscreet1DL.cpp index a52bc728f..6a76d968d 100755 --- a/src/OpenColorIO/fileformats/FileFormatDiscreet1DL.cpp +++ b/src/OpenColorIO/fileformats/FileFormatDiscreet1DL.cpp @@ -8,6 +8,8 @@ #include #include +#include + #include #include "BitDepthUtils.h" @@ -16,7 +18,6 @@ #include "ops/lut1d/Lut1DOp.h" #include "ops/lut3d/Lut3DOp.h" #include "ParseUtils.h" -#include "pystring/pystring.h" #include "Platform.h" #include "transforms/FileTransform.h" #include "utils/StringUtils.h" @@ -771,4 +772,3 @@ FileFormat * CreateFileFormatDiscreet1DL() } } // namespace OCIO_NAMESPACE - diff --git a/src/OpenColorIO/fileformats/FileFormatICC.cpp b/src/OpenColorIO/fileformats/FileFormatICC.cpp index 1fcfd9adc..476810112 100755 --- a/src/OpenColorIO/fileformats/FileFormatICC.cpp +++ b/src/OpenColorIO/fileformats/FileFormatICC.cpp @@ -5,6 +5,8 @@ #include #include +#include + #include #include "Logging.h" @@ -15,7 +17,6 @@ #include "ops/matrix/MatrixOp.h" #include "ops/range/RangeOp.h" #include "Platform.h" -#include "pystring/pystring.h" #include "transforms/FileTransform.h" diff --git a/src/OpenColorIO/fileformats/FileFormatIridasLook.cpp b/src/OpenColorIO/fileformats/FileFormatIridasLook.cpp index 7402efd09..0ba209a90 100755 --- a/src/OpenColorIO/fileformats/FileFormatIridasLook.cpp +++ b/src/OpenColorIO/fileformats/FileFormatIridasLook.cpp @@ -5,6 +5,8 @@ #include #include +#include + #include #include "expat.h" @@ -13,7 +15,6 @@ #include "ops/lut3d/Lut3DOp.h" #include "ParseUtils.h" #include "Platform.h" -#include "pystring/pystring.h" #include "transforms/FileTransform.h" #include "utils/StringUtils.h" #include "utils/NumberUtils.h" @@ -579,4 +580,3 @@ FileFormat * CreateFileFormatIridasLook() } } // namespace OCIO_NAMESPACE - diff --git a/src/OpenColorIO/ops/lut1d/Lut1DOpCPU_SSE2.cpp b/src/OpenColorIO/ops/lut1d/Lut1DOpCPU_SSE2.cpp index d7ecfe49c..c170e1791 100644 --- a/src/OpenColorIO/ops/lut1d/Lut1DOpCPU_SSE2.cpp +++ b/src/OpenColorIO/ops/lut1d/Lut1DOpCPU_SSE2.cpp @@ -24,13 +24,23 @@ namespace { static inline __m128 fmadd_ps_sse2(__m128 a, __m128 b, __m128 c) { +#if OCIO_USE_SSE2NEON + return vreinterpretq_m128_f32( + vfmaq_f32(vreinterpretq_f32_m128(c), vreinterpretq_f32_m128(a), vreinterpretq_f32_m128(b)) + ); +#else return _mm_add_ps(_mm_mul_ps(a, b), c); +#endif } static inline __m128 floor_ps_sse2(__m128 v) { +#if OCIO_USE_SSE2NEON + return _mm_floor_ps(v); +#else // NOTE: using truncate cvtt return _mm_cvtepi32_ps(_mm_cvttps_epi32(v)); +#endif } diff --git a/src/OpenColorIO/ops/lut3d/Lut3DOpCPU_SSE2.cpp b/src/OpenColorIO/ops/lut3d/Lut3DOpCPU_SSE2.cpp index e98a0470d..e7b338d36 100644 --- a/src/OpenColorIO/ops/lut3d/Lut3DOpCPU_SSE2.cpp +++ b/src/OpenColorIO/ops/lut3d/Lut3DOpCPU_SSE2.cpp @@ -38,18 +38,32 @@ struct rgbavec_sse2 { static inline __m128 floor_ps_sse2(__m128 v) { +#if OCIO_USE_SSE2NEON + return _mm_floor_ps(v); +#else // NOTE: using truncate cvtt return _mm_cvtepi32_ps(_mm_cvttps_epi32(v)); +#endif } static inline __m128 blendv_ps_sse2(__m128 a, __m128 b, __m128 mask) { +#if OCIO_USE_SSE2NEON + return _mm_blendv_ps(a, b, mask); +#else return _mm_xor_ps(_mm_and_ps(_mm_xor_ps(a, b), mask), a); +#endif } static inline __m128 fmadd_ps_sse2(__m128 a, __m128 b, __m128 c) { +#if OCIO_USE_SSE2NEON + return vreinterpretq_m128_f32( + vfmaq_f32(vreinterpretq_f32_m128(c), vreinterpretq_f32_m128(a), vreinterpretq_f32_m128(b)) + ); +#else return _mm_add_ps(_mm_mul_ps(a, b), c); +#endif } static inline rgbavec_sse2 interp_tetrahedral_sse2(const Lut3DContextSSE2 &ctx, __m128 r, __m128 g, __m128 b, __m128 a) diff --git a/src/OpenColorIO/transforms/ColorSpaceTransform.cpp b/src/OpenColorIO/transforms/ColorSpaceTransform.cpp index c23423d6e..2039ea66b 100755 --- a/src/OpenColorIO/transforms/ColorSpaceTransform.cpp +++ b/src/OpenColorIO/transforms/ColorSpaceTransform.cpp @@ -184,7 +184,7 @@ void BuildColorSpaceOps(OpRcPtrVec & ops, if (!src) { - srcNamedTransform = config.getNamedTransform(srcName.c_str()); + srcNamedTransform = config.getNamedTransform( context->resolveStringVar(srcName.c_str()) ); if (!srcNamedTransform) { ThrowMissingCS(srcName.c_str()); @@ -192,7 +192,7 @@ void BuildColorSpaceOps(OpRcPtrVec & ops, } if (!dst) { - dstNamedTransform = config.getNamedTransform(dstName.c_str()); + dstNamedTransform = config.getNamedTransform( context->resolveStringVar(dstName.c_str()) ); if (!dstNamedTransform) { ThrowMissingCS(dstName.c_str()); @@ -393,6 +393,31 @@ void BuildReferenceConversionOps(OpRcPtrVec & ops, } } +bool CollectContextVariables(const Config & config, + const Context & context, + ConstNamedTransformRcPtr & nt, + ContextRcPtr & usedContextVars) +{ + bool foundContextVars = false; + + if (nt) + { + ConstTransformRcPtr to = nt->getTransform(TRANSFORM_DIR_FORWARD); + if (to && CollectContextVariables(config, context, to, usedContextVars)) + { + foundContextVars = true; + } + + ConstTransformRcPtr from = nt->getTransform(TRANSFORM_DIR_INVERSE); + if (from && CollectContextVariables(config, context, from, usedContextVars)) + { + foundContextVars = true; + } + } + + return foundContextVars; +} + bool CollectContextVariables(const Config & config, const Context & context, ConstColorSpaceRcPtr & cs, @@ -441,15 +466,37 @@ bool CollectContextVariables(const Config & config, } ConstColorSpaceRcPtr src = config.getColorSpace(srcName.c_str()); - if (CollectContextVariables(config, context, src, usedContextVars)) + if (src) { - foundContextVars = true; + if (CollectContextVariables(config, context, src, usedContextVars)) + { + foundContextVars = true; + } + } + else + { + ConstNamedTransformRcPtr nt_src = config.getNamedTransform(srcName.c_str()); + if (CollectContextVariables(config, context, nt_src, usedContextVars)) + { + foundContextVars = true; + } } ConstColorSpaceRcPtr dst = config.getColorSpace(dstName.c_str()); - if (CollectContextVariables(config, context, dst, usedContextVars)) + if (dst) { - foundContextVars = true; + if (CollectContextVariables(config, context, dst, usedContextVars)) + { + foundContextVars = true; + } + } + else + { + ConstNamedTransformRcPtr nt_dst = config.getNamedTransform(dstName.c_str()); + if (CollectContextVariables(config, context, nt_dst, usedContextVars)) + { + foundContextVars = true; + } } return foundContextVars; diff --git a/src/OpenColorIO/transforms/FileTransform.cpp b/src/OpenColorIO/transforms/FileTransform.cpp index 4fd4d5d47..9aeae8df4 100755 --- a/src/OpenColorIO/transforms/FileTransform.cpp +++ b/src/OpenColorIO/transforms/FileTransform.cpp @@ -9,6 +9,8 @@ #include #include +#include + #include #include "Caching.h" @@ -19,7 +21,6 @@ #include "ops/noop/NoOps.h" #include "PathUtils.h" #include "Platform.h" -#include "pystring/pystring.h" #include "utils/StringUtils.h" namespace OCIO_NAMESPACE diff --git a/src/apps/ocioview/ocioview/README.md b/src/apps/ocioview/README.md similarity index 94% rename from src/apps/ocioview/ocioview/README.md rename to src/apps/ocioview/README.md index e62eb4516..a62465307 100644 --- a/src/apps/ocioview/ocioview/README.md +++ b/src/apps/ocioview/README.md @@ -41,10 +41,10 @@ Dependencies * PyOpenColorIO * [OpenImageIO (Python bindings)](https://github.com/OpenImageIO/oiio) -* [Imath (Python bindings)](https://github.com/AcademySoftwareFoundation/Imath) * ``pip install -r requirements.txt`` * [numpy](https://pypi.org/project/numpy/) * [Pygments](https://pypi.org/project/Pygments/) * [PyOpenGL](https://pypi.org/project/PyOpenGL/) - * [PySide2](https://pypi.org/project/PySide2/) + * [PySide6](https://pypi.org/project/PySide6/) * [QtAwesome](https://pypi.org/project/QtAwesome/) + * [imageio](https://pypi.org/project/imageio/) diff --git a/src/apps/ocioview/main.py b/src/apps/ocioview/main.py index 85f657cab..609ef73d8 100644 --- a/src/apps/ocioview/main.py +++ b/src/apps/ocioview/main.py @@ -7,7 +7,7 @@ from pathlib import Path import PyOpenColorIO as ocio -from PySide2 import QtCore, QtWidgets, QtOpenGL +from PySide6 import QtCore, QtGui, QtWidgets, QtOpenGL import ocioview.log_handlers # Import to initialize logging from ocioview.main_window import OCIOView @@ -30,12 +30,11 @@ def excepthook(exc_type, exc_value, exc_tb): sys.excepthook = excepthook # OpenGL core profile needed on macOS to access programmatic pipeline - gl_format = QtOpenGL.QGLFormat() - gl_format.setProfile(QtOpenGL.QGLFormat.CoreProfile) - gl_format.setSampleBuffers(True) + gl_format = QtGui.QSurfaceFormat() + gl_format.setProfile(QtGui.QSurfaceFormat.CoreProfile) gl_format.setSwapInterval(1) gl_format.setVersion(4, 0) - QtOpenGL.QGLFormat.setDefaultFormat(gl_format) + QtGui.QSurfaceFormat.setDefaultFormat(gl_format) # Create app app = QtWidgets.QApplication(sys.argv) diff --git a/src/apps/ocioview/ocioview/config_dock.py b/src/apps/ocioview/ocioview/config_dock.py index a47774fc1..33660b112 100644 --- a/src/apps/ocioview/ocioview/config_dock.py +++ b/src/apps/ocioview/ocioview/config_dock.py @@ -4,7 +4,7 @@ from typing import Optional import PyOpenColorIO as ocio -from PySide2 import QtCore, QtWidgets +from PySide6 import QtCore, QtWidgets from .items import ( ColorSpaceEdit, diff --git a/src/apps/ocioview/ocioview/constants.py b/src/apps/ocioview/ocioview/constants.py index a580e126f..18b7f0909 100644 --- a/src/apps/ocioview/ocioview/constants.py +++ b/src/apps/ocioview/ocioview/constants.py @@ -3,7 +3,7 @@ from pathlib import Path -from PySide2 import QtCore, QtGui +from PySide6 import QtCore, QtGui # Root application directory diff --git a/src/apps/ocioview/ocioview/inspect/code_inspector.py b/src/apps/ocioview/ocioview/inspect/code_inspector.py index b812cbcb6..3fa1e81e3 100644 --- a/src/apps/ocioview/ocioview/inspect/code_inspector.py +++ b/src/apps/ocioview/ocioview/inspect/code_inspector.py @@ -6,7 +6,7 @@ import PyOpenColorIO as ocio from pygments.formatters import HtmlFormatter -from PySide2 import QtCore, QtGui, QtWidgets +from PySide6 import QtCore, QtGui, QtWidgets from ..message_router import MessageRouter from ..utils import get_glyph_icon, processor_to_shader_html @@ -40,9 +40,9 @@ def __init__(self, parent: Optional[QtCore.QObject] = None): html_css = HtmlFormatter(style="material").get_style_defs() # Update line number colors to match palette - html_css = html_css.replace("#263238", palette.color(palette.Base).name()) + html_css = html_css.replace("#263238", palette.color(palette.ColorRole.Base).name()) html_css = html_css.replace( - "#37474F", palette.color(palette.Text).darker(150).name() + "#37474F", palette.color(palette.ColorRole.Text).darker(150).name() ) # Widgets diff --git a/src/apps/ocioview/ocioview/inspect/curve_inspector.py b/src/apps/ocioview/ocioview/inspect/curve_inspector.py index 41d920e2d..9a483a1cc 100644 --- a/src/apps/ocioview/ocioview/inspect/curve_inspector.py +++ b/src/apps/ocioview/ocioview/inspect/curve_inspector.py @@ -8,7 +8,7 @@ import numpy as np import PyOpenColorIO as ocio -from PySide2 import QtCore, QtGui, QtWidgets +from PySide6 import QtCore, QtGui, QtWidgets from ..constants import R_COLOR, G_COLOR, B_COLOR, GRAY_COLOR from ..message_router import MessageRouter @@ -17,11 +17,22 @@ class SampleType(enum.Enum): + """Enum of curve sampling types for representing OCIO transforms.""" + LINEAR = "linear" + """Linear scale.""" + LOG = "log" + """Log scale with a user-defined base.""" class CurveInspector(QtWidgets.QWidget): + """ + Widget for inspecting OCIO transform tone curves, which updates + asynchronously when visible, to reduce unnecessary background + processing. + """ + @classmethod def label(cls) -> str: return "Curve" @@ -43,11 +54,11 @@ def __init__(self, parent: Optional[QtCore.QObject] = None): self.input_range_edit.setToolTip(self.input_range_label.toolTip()) self.input_range_edit.value_changed.connect(self._on_input_range_changed) - self.sample_size_label = get_glyph_icon("ph.line-segments", as_widget=True) - self.sample_size_label.setToolTip("Sample size") - self.sample_size_edit = IntEdit(default=CurveView.SAMPLE_SIZE_DEFAULT) - self.sample_size_edit.setToolTip(self.sample_size_label.toolTip()) - self.sample_size_edit.value_changed.connect(self._on_sample_size_changed) + self.sample_count_label = get_glyph_icon("ph.line-segments", as_widget=True) + self.sample_count_label.setToolTip("Sample count") + self.sample_count_edit = IntEdit(default=CurveView.SAMPLE_COUNT_DEFAULT) + self.sample_count_edit.setToolTip(self.sample_count_label.toolTip()) + self.sample_count_edit.value_changed.connect(self._on_sample_count_changed) self.sample_type_label = get_glyph_icon("mdi6.function-variant", as_widget=True) self.sample_type_label.setToolTip("Sample type") @@ -70,16 +81,16 @@ def __init__(self, parent: Optional[QtCore.QObject] = None): option_layout = QtWidgets.QHBoxLayout() option_layout.addWidget(self.input_range_label) option_layout.addWidget(self.input_range_edit) - option_layout.setStretch(1, 2) - option_layout.addWidget(self.sample_size_label) - option_layout.addWidget(self.sample_size_edit) - option_layout.setStretch(3, 1) + option_layout.setStretch(1, 3) + option_layout.addWidget(self.sample_count_label) + option_layout.addWidget(self.sample_count_edit) + option_layout.setStretch(3, 2) option_layout.addWidget(self.sample_type_label) option_layout.addWidget(self.sample_type_combo) - option_layout.setStretch(5, 1) + option_layout.setStretch(5, 3) option_layout.addWidget(self.log_base_label) option_layout.addWidget(self.log_base_edit) - option_layout.setStretch(7, 1) + option_layout.setStretch(7, 2) layout = QtWidgets.QVBoxLayout() layout.addLayout(option_layout) @@ -88,83 +99,137 @@ def __init__(self, parent: Optional[QtCore.QObject] = None): self.setLayout(layout) def reset(self) -> None: + """Clear rendered curves.""" self.view.reset() @QtCore.Slot(str, float) def _on_input_range_changed(self, label: str, value: float) -> None: + """ + Triggered when the user changes the input range, which defines + the input values to be sampled through the transform. + + :param label: Float edit label + :param value: Float edit value + """ self.view.set_input_range( self.input_range_edit.component_value("min"), self.input_range_edit.component_value("max"), ) @QtCore.Slot(int) - def _on_sample_size_changed(self, sample_size: int) -> None: - if sample_size >= CurveView.SAMPLE_SIZE_MIN: - self.view.set_sample_size(sample_size) + def _on_sample_count_changed(self, sample_count: int) -> None: + """ + Triggered when the user changes the number of samples to be + processed within the input range through the transform. + + :param sample_count: Number of samples. Typically, a power of 2 + number. + """ + if sample_count >= CurveView.SAMPLE_COUNT_MIN: + self.view.set_sample_count(sample_count) else: - with SignalsBlocked(self.sample_size_edit): - self.sample_size_edit.set_value(CurveView.SAMPLE_SIZE_MIN) - self.view.set_sample_size(CurveView.SAMPLE_SIZE_MIN) + with SignalsBlocked(self.sample_count_edit): + self.sample_count_edit.set_value(CurveView.SAMPLE_COUNT_MIN) + self.view.set_sample_count(CurveView.SAMPLE_COUNT_MIN) @QtCore.Slot(int) def _on_sample_type_changed(self, index: int) -> None: + """ + Triggered when the user changes the sample type, which defines + how samples are distributed within the input range. + + :param index: Curve type index + """ sample_type = self.sample_type_combo.member() self.log_base_edit.setEnabled(sample_type == SampleType.LOG) self.view.set_sample_type(sample_type) @QtCore.Slot(int) def _on_log_base_changed(self, log_base: int) -> None: + """ + Triggered when the user changes the base for the log sample + type. + + :param log_base: Log scale base + """ self.view.set_log_base(log_base) class CurveView(QtWidgets.QGraphicsView): + """ + Widget for rendering OCIO transform tone curves. + """ - SAMPLE_SIZE_MIN = 2**8 - SAMPLE_SIZE_DEFAULT = 2**10 + SAMPLE_COUNT_MIN = 2**8 + SAMPLE_COUNT_DEFAULT = 2**10 INPUT_MIN_DEFAULT = 0.0 INPUT_MAX_DEFAULT = 1.0 + LOG_BASE_DEFAULT = 2 CURVE_SCALE = 100 FONT_HEIGHT = 4 # The curve viewer only shows 5 digit decimal precision, so this should work fine # as a minimum when input min is 0. - LOG_EPSILON = 1e-5 - LOG_BASE_DEFAULT = 2 + EPSILON = 1e-5 def __init__( self, input_min: float = INPUT_MIN_DEFAULT, input_max: float = INPUT_MAX_DEFAULT, - sample_size: int = SAMPLE_SIZE_DEFAULT, + sample_count: int = SAMPLE_COUNT_DEFAULT, sample_type: SampleType = SampleType.LINEAR, log_base: int = LOG_BASE_DEFAULT, parent: Optional[QtWidgets.QWidget] = None, ): + """ + :param input_min: Input range minimum value + :param input_max: Input range maximum value + :param sample_count: Number of samples in the input range, + which will typically be a power of 2 value. + :param sample_type: Sample scale/distribution type + :param log_base: Log scale base when sample_type is + `SampleType.LOG`. + """ super().__init__(parent=parent) self.setRenderHint(QtGui.QPainter.Antialiasing, True) self.setViewportUpdateMode(QtWidgets.QGraphicsView.FullViewportUpdate) self.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) self.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) self.setMouseTracking(True) - self.scale(1, -1) + self.scale(1, -1) # Flip to make origin the bottom-left corner + # Input range if input_max <= input_min: input_min = self.INPUT_MIN_DEFAULT input_max = self.INPUT_MAX_DEFAULT - self._log_base = max(2, log_base) self._input_min = input_min self._input_max = input_max - self._sample_size = max(self.SAMPLE_SIZE_MIN, sample_size) + + # Sample characteristics + self._sample_count = max(self.SAMPLE_COUNT_MIN, sample_count) self._sample_type = sample_type + self._log_base = max(2, log_base) + + # Cached sample data self._sample_ellipse: Optional[QtWidgets.QGraphicsEllipseItem] = None self._sample_text: Optional[QtWidgets.QGraphicsTextItem] = None self._sample_rect: Optional[QtCore.QRectF] = None - self._samples_x_lin: np.ndarray = None - self._samples_x_log: np.ndarray = None - self._sample_y_min: float = self._input_min - self._sample_y_max: float = self._input_max + self._samples: dict[str, np.ndarray] = {} + self._nearest_samples: dict[str, tuple[float, float, float]] = {} + + self._x_lin: np.ndarray = np.array([], dtype=np.float32) + self._x_log: np.ndarray = np.array([], dtype=np.float32) + self._x_min: float = self._input_min + self._x_max: float = self._input_max + self._y_min: float = self._input_min + self._y_max: float = self._input_max + + # Cached curve data + self._curve_init = False + self._curve_tf = QtGui.QTransform() + self._curve_tf_inv = QtGui.QTransform() self._curve_paths: dict[str, QtGui.QPainterPath] = {} self._curve_items: dict[str, QtWidgets.QGraphicsPathItem] = {} self._curve_rect = QtCore.QRectF( @@ -173,10 +238,11 @@ def __init__( self._input_max - self._input_min, self._input_max - self._input_min, ) - self._nearest_samples: dict[str, tuple[float, float, float]] = {} + + # Cached processor from which the OCIO transform is derived self._prev_cpu_proc = None - self._curve_init = False + # Graphics scene self._scene = QtWidgets.QGraphicsScene() self.setScene(self._scene) @@ -200,8 +266,8 @@ def hideEvent(self, event: QtGui.QHideEvent) -> None: msg_router.set_cpu_processor_updates_allowed(False) def resizeEvent(self, event: QtGui.QResizeEvent) -> None: + """Re-fit graph on resize, to always be centered.""" super().resizeEvent(event) - self._fit() def mouseMoveEvent(self, event: QtGui.QMouseEvent) -> None: @@ -213,37 +279,52 @@ def mouseMoveEvent(self, event: QtGui.QMouseEvent) -> None: self._nearest_samples.clear() - pos = self.mapToScene(event.pos()) / self.CURVE_SCALE + pos = self._curve_tf_inv.map(self.mapToScene(event.pos())) pos_arr = np.array([pos.x(), pos.y()], dtype=np.float32) for color_name, samples in self._samples.items(): - all_dist = np.linalg.norm(samples - pos_arr, axis=2) + all_dist = np.linalg.norm(samples - pos_arr, axis=1) nearest_dist_index = np.argmin(all_dist) self._nearest_samples[color_name] = ( ( - self._samples_x_lin[nearest_dist_index] + self._x_lin[nearest_dist_index] if self._sample_type == SampleType.LINEAR - else self._samples_x_log[nearest_dist_index] + else self._x_log[nearest_dist_index] ), - samples[0][nearest_dist_index][0], - samples[0][nearest_dist_index][1], + samples[nearest_dist_index][0], + samples[nearest_dist_index][1], ) self._invalidate() + def wheelEvent(self, event: QtGui.QWheelEvent) -> None: + """ + Ignore wheel events to prevent scrolling around graphics scene. + """ + event.ignore() + def reset(self) -> None: """Clear all curves.""" self._samples.clear() - self._curve_items.clear() - self._curve_paths.clear() self._nearest_samples.clear() - self._prev_cpu_proc = None + self._curve_init = False + self._curve_paths.clear() + self._curve_items.clear() + + self._prev_cpu_proc = None self._scene.clear() self._invalidate() def set_input_range(self, input_min: float, input_max: float) -> None: + """ + Set the input range, which defines the input values to be + sampled through the transform. + + :param input_min: Input range minimum value + :param input_max: Input range maximum value + """ if ( input_min != self._input_min or input_max != self._input_max ) and input_max > input_min: @@ -252,31 +333,62 @@ def set_input_range(self, input_min: float, input_max: float) -> None: self._update_curves() def set_input_min(self, input_min: float) -> None: + """ + Set the input minimum, which defines the lowest value to be + sampled through the transform. + + :param input_min: Input range minimum value + """ if input_min != self._input_min and input_min < self._input_max: self._input_min = input_min self._update_curves() def set_input_max(self, input_max: float) -> None: + """ + Set the input maximum, which defines the highest value to be + sampled through the transform. + + :param input_max: Input range minimum value + """ if input_max != self._input_max and input_max > self._input_min: self._input_max = input_max self._update_curves() - def set_sample_size(self, sample_size: int) -> None: - if sample_size != self._sample_size and sample_size >= self.SAMPLE_SIZE_MIN: - self._sample_size = sample_size + def set_sample_count(self, sample_count: int) -> None: + """ + Set the number of samples to be processed within the input + range through the transform. + + :param sample_count: Number of samples. Typically, a power of 2 + number. + """ + if sample_count != self._sample_count and sample_count >= self.SAMPLE_COUNT_MIN: + self._sample_count = sample_count self._update_curves() def set_sample_type(self, sample_type: SampleType) -> None: + """ + Set the sample type, which defines how samples are distributed + within the input range. + + :param sample_type: Curve type index + """ if sample_type != self._sample_type: self._sample_type = sample_type self._update_curves() def set_log_base(self, log_base: int) -> None: + """ + Set the base for the log sample type. + + :param log_base: Log scale base + """ if log_base != self._log_base and log_base >= 2: self._log_base = log_base self._update_curves() def drawBackground(self, painter: QtGui.QPainter, rect: QtCore.QRectF) -> None: + """Draw curve grid and axis values.""" # Flood fill background painter.setPen(QtCore.Qt.NoPen) painter.setBrush(QtCore.Qt.black) @@ -285,101 +397,41 @@ def drawBackground(self, painter: QtGui.QPainter, rect: QtCore.QRectF) -> None: if not self._curve_init: return - # Calculate grid rect - curve_t, curve_b, curve_l, curve_r = ( - self._curve_rect.top(), - self._curve_rect.bottom(), - self._curve_rect.left(), - self._curve_rect.right(), - ) - - # Round to nearest 10 for grid bounds - grid_t = curve_t // 10 * 10 - if grid_t > math.ceil(curve_t): - grid_t -= 10 - grid_b = curve_b // 10 * 10 - if grid_b < math.floor(curve_b): - grid_b += 10 - grid_l = curve_l // 10 * 10 - if grid_l > math.ceil(curve_l): - grid_l -= 10 - grid_r = curve_r // 10 * 10 - if grid_r < math.floor(curve_r): - grid_r += 10 + font = painter.font() + font.setPixelSize(self.FONT_HEIGHT) + painter.setFont(font) text_pen = QtGui.QPen(GRAY_COLOR) - grid_pen = QtGui.QPen(GRAY_COLOR.darker(200)) grid_pen.setWidthF(0) - font = painter.font() - font.setPixelSize(self.FONT_HEIGHT) - + painter.setPen(grid_pen) painter.setBrush(QtCore.Qt.NoBrush) - painter.setFont(font) - - # Calculate samples to display - sample_step = math.ceil(self._sample_size / 10.0) - min_x = max_x = min_y = max_y = None - - if self._sample_type == SampleType.LINEAR: - sample_x_values = self._samples_x_lin - else: # SampleType.LOG - sample_x_values = self._samples_x_log - - sample_y_data = [] - for i, sample_y in enumerate( - np.linspace(self._sample_y_min, self._sample_y_max, 11, dtype=np.float32) - ): - pos_y = sample_y * self.CURVE_SCALE - sample_y_data.append((pos_y, sample_y)) - - if min_y is None or pos_y < min_y: - min_y = pos_y - if max_y is None or pos_y > max_y: - max_y = pos_y - - sample_x_data = [] - for i, sample_x in enumerate(sample_x_values): - if not (i % sample_step == 0 or i == self._sample_size - 1): - continue - - pos_x = self._samples_x_lin[i] * self.CURVE_SCALE - sample_x_data.append((pos_x, sample_x)) - - if min_x is None or pos_x < min_x: - min_x = pos_x - if max_x is None or pos_x > max_x: - max_x = pos_x - if min_x is None: - min_x = grid_l - if max_x is None: - max_x = grid_r - if min_y is None: - min_y = grid_t - if max_x is None: - max_y = grid_b - - self._sample_rect = QtCore.QRectF( - max_x + 5, min_y, 40, len(self._curve_items) * 20 - ) + # Draw grid border + painter.drawRect(self._curve_rect) # Draw grid rows y_text_origin = QtGui.QTextOption(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter) y_text_origin.setWrapMode(QtGui.QTextOption.NoWrap) - for pos_y, sample_y in sample_y_data: - painter.setPen(grid_pen) - painter.drawLine(QtCore.QLineF(min_x, pos_y, max_x, pos_y)) + for i, y in enumerate( + np.linspace(self._y_min, self._y_max, 11, dtype=np.float32) + ): + p1 = self._curve_tf.map(QtCore.QPointF(self._x_min, y)) + p2 = self._curve_tf.map(QtCore.QPointF(self._x_max, y)) + + if self._y_min < y < self._y_max: + painter.setPen(grid_pen) + painter.drawLine(QtCore.QLineF(p1, p2)) - if pos_y > grid_t: - label_value = round(sample_y, 2) + if y > self._y_min: + label_value = round(y, 2) if label_value == 0.0: label_value = abs(label_value) painter.save() - painter.translate(QtCore.QPointF(min_x, pos_y)) + painter.translate(p1) painter.scale(1, -1) painter.setPen(text_pen) painter.drawText( @@ -391,28 +443,39 @@ def drawBackground(self, painter: QtGui.QPainter, rect: QtCore.QRectF) -> None: x_text_origin = QtGui.QTextOption(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter) x_text_origin.setWrapMode(QtGui.QTextOption.NoWrap) - for pos_x, sample_x in sample_x_data: - painter.setPen(grid_pen) - painter.drawLine(QtCore.QLineF(pos_x, min_y, pos_x, max_y)) + sample_step = math.ceil(self._sample_count / 10.0) + + for i, x in enumerate(self._x_lin): + if not (i % sample_step == 0 or i == self._sample_count - 1): + continue + + p1 = self._curve_tf.map(QtCore.QPointF(x, self._y_min)) + p2 = self._curve_tf.map(QtCore.QPointF(x, self._y_max)) - if pos_x > grid_l: + if self._x_min < x < self._x_max: + painter.setPen(grid_pen) + painter.drawLine(QtCore.QLineF(p1, p2)) + + if x > self._x_min: label_value = round( - sample_x, 5 if self._sample_type == SampleType.LOG else 2 + x if self._sample_type == SampleType.LINEAR else self._x_log[i], + 2 if self._sample_type == SampleType.LINEAR else 5, ) if label_value == 0.0: label_value = abs(label_value) painter.save() - painter.translate(QtCore.QPointF(pos_x, min_y)) + painter.translate(p1) painter.scale(1, -1) painter.rotate(90) painter.setPen(text_pen) painter.drawText( - QtCore.QRect(2.5 + 1, -10, 40, 20), str(label_value), x_text_origin + QtCore.QRectF(2.5 + 1, -10, 40, 20), str(label_value), x_text_origin ) painter.restore() def drawForeground(self, painter: QtGui.QPainter, rect: QtCore.QRectF) -> None: + """Draw nearest sample point and coordinates.""" if not self._curve_init: return @@ -436,16 +499,18 @@ def drawForeground(self, painter: QtGui.QPainter, rect: QtCore.QRectF) -> None: painter.setBrush(QtGui.QColor(color_name)) painter.drawEllipse( - QtCore.QPointF( - nearest_sample[1] * self.CURVE_SCALE, - nearest_sample[2] * self.CURVE_SCALE, + self._curve_tf.map( + QtCore.QPointF( + nearest_sample[1], + nearest_sample[2], + ) ), - 1.25, - 1.25, + 1.5, + 1.5, ) + # Draw sample values if sample_l is not None: - # Draw sample values painter.setBrush(QtCore.Qt.NoBrush) x_label_value = f"{nearest_sample[0]:.05f}" @@ -461,27 +526,56 @@ def drawForeground(self, painter: QtGui.QPainter, rect: QtCore.QRectF) -> None: if color_name == GRAY_COLOR.name(): palette = self.palette() - painter.setPen(palette.color(palette.Text)) + painter.setPen(palette.color(palette.ColorRole.Text)) else: painter.setPen(QtGui.QColor(color_name)) + painter.drawText( QtCore.QRectF(5, -20, 35, 10), x_label_value, text_origin ) painter.drawText( QtCore.QRectF(5, -10, 35, 10), y_label_value, text_origin ) - painter.restore() + def _invalidate(self) -> None: + """Force repaint of visible region of graphics scene.""" + self._scene.invalidate(QtCore.QRectF(self.visibleRegion().boundingRect())) + def _update_curves(self) -> None: + """ + Update X-axis samples and rebuild curves from any cached + processor. + """ self._update_x_samples() if self._prev_cpu_proc is not None: self._on_cpu_processor_ready(self._prev_cpu_proc) + def _update_x_samples(self): + """ + Update linear and log X-axis samples from input and sample + parameters. + """ + self._x_lin = np.linspace( + self._input_min, self._input_max, self._sample_count, dtype=np.float32 + ) + + log_min = math.log(max(self.EPSILON, self._input_min)) + log_max = max(log_min + 0.00001, math.log(self._input_max, self._log_base)) + self._x_log = np.logspace( + log_min, log_max, self._sample_count, base=self._log_base, dtype=np.float32 + ) + + self._x_min = self._x_lin.min() + self._x_max = self._x_lin.max() + def _fit(self) -> None: + """Fit and center graph.""" if not self._curve_init: return + # Use font metrics to calculate text padding, based on estimated maximum + # number lengths. font = self.font() font.setPixelSize(self.FONT_HEIGHT) fm = QtGui.QFontMetrics(font) @@ -503,29 +597,15 @@ def _fit(self) -> None: fit_rect = self._curve_rect.adjusted(-pad_l, -pad_t, pad_r, pad_b) + # Fit and center on calculated rectangle self.fitInView(fit_rect, QtCore.Qt.KeepAspectRatio) self.centerOn(fit_rect.center()) self.update() - def _invalidate(self) -> None: - self._scene.invalidate(QtCore.QRectF(self.visibleRegion().boundingRect())) - - def _update_x_samples(self): - self._samples_x_lin = np.linspace( - self._input_min, self._input_max, self._sample_size, dtype=np.float32 - ) - self._samples_x_log = np.logspace( - math.log(max(self.LOG_EPSILON, self._input_min)), - math.log(max(self.LOG_EPSILON, self._input_max)), - self._sample_size, - base=self._log_base, - dtype=np.float32, - ) - @QtCore.Slot(ocio.CPUProcessor) def _on_cpu_processor_ready(self, cpu_proc: ocio.CPUProcessor) -> None: """ - Update curves from OCIO CPU processor. + Update curves from sampled OCIO CPU processor. :param cpu_proc: CPU processor of currently viewed transform """ @@ -535,12 +615,12 @@ def _on_cpu_processor_ready(self, cpu_proc: ocio.CPUProcessor) -> None: # Get input samples if self._sample_type == SampleType.LOG: - samples = self._samples_x_log + x_samples = self._x_log else: # LINEAR - samples = self._samples_x_lin + x_samples = self._x_lin # Interleave samples per channel - rgb_samples = np.repeat(samples, 3) + rgb_samples = np.repeat(x_samples, 3) # Apply processor to samples cpu_proc.applyRGB(rgb_samples) @@ -550,28 +630,21 @@ def _on_cpu_processor_ready(self, cpu_proc: ocio.CPUProcessor) -> None: g_samples = rgb_samples[1::3] b_samples = rgb_samples[2::3] - # Build painter path from sample data - if np.array_equal(r_samples, g_samples) and np.array_equal( - r_samples, b_samples + # Collect sample pairs and min/max Y sample values + if np.allclose(r_samples, g_samples, atol=self.EPSILON) and np.allclose( + r_samples, b_samples, atol=self.EPSILON ): palette = self.palette() - color_name = palette.color(palette.Text).name() + color_name = palette.color(palette.ColorRole.Text).name() - self._samples[color_name] = np.dstack((self._samples_x_lin, r_samples)) - self._sample_y_min = r_samples.min() - self._sample_y_max = r_samples.max() + self._samples[color_name] = np.stack((self._x_lin, r_samples), axis=-1) - curve = QtGui.QPainterPath( - QtCore.QPointF(self._samples_x_lin[0], r_samples[0]) - ) - curve.reserve(samples.size) - for i in range(1, samples.size): - curve.lineTo(QtCore.QPointF(self._samples_x_lin[i], r_samples[i])) - self._curve_paths[color_name] = curve + self._y_min = r_samples.min() + self._y_max = r_samples.max() else: - sample_y_min = None - sample_y_max = None + y_min = None + y_max = None for i, (color, channel_samples) in enumerate( [ @@ -582,43 +655,71 @@ def _on_cpu_processor_ready(self, cpu_proc: ocio.CPUProcessor) -> None: ): color_name = color.name() - self._samples[color_name] = np.dstack( - (self._samples_x_lin, channel_samples) + self._samples[color_name] = np.stack( + (self._x_lin, channel_samples), axis=-1 ) - channel_sample_y_min = channel_samples.min() - if sample_y_min is None or channel_sample_y_min < sample_y_min: - sample_y_min = channel_sample_y_min - channel_sample_y_max = channel_samples.max() - if sample_y_max is None or channel_sample_y_max > sample_y_max: - sample_y_max = channel_sample_y_max + channel_y_min = channel_samples.min() + if y_min is None or channel_y_min < y_min: + y_min = channel_y_min + channel_y_max = channel_samples.max() + if y_max is None or channel_y_max > y_max: + y_max = channel_y_max + + self._y_min = y_min + self._y_max = y_max + + # Transform to scale/translate curve so that it fits in a square and + # has its origin at (0, 0). + curve_tf = QtGui.QTransform() + curve_tf.translate(-self._x_min, -self._y_min) + curve_tf.scale( + self.CURVE_SCALE / (self._x_max - self._x_min), + self.CURVE_SCALE / (self._y_max - self._y_min), + ) + self._curve_tf = curve_tf + self._curve_tf_inv, ok = curve_tf.inverted() + + # Curve square + self._curve_rect = QtCore.QRectF( + self._curve_tf.map(QtCore.QPointF(self._x_min, self._y_min)), + self._curve_tf.map(QtCore.QPointF(self._x_max, self._y_max)), + ) + + # Sample box rect + self._sample_rect = QtCore.QRectF( + self._curve_rect.right() + 5, + self._curve_rect.top(), + 40, + len(self._samples) * 20, + ) - curve = QtGui.QPainterPath( - QtCore.QPointF(self._samples_x_lin[0], channel_samples[0]) + # Build painter paths that fit in square from sample data + for color_name, channel_samples in self._samples.items(): + curve = QtGui.QPainterPath( + self._curve_tf.map( + QtCore.QPointF(channel_samples[0][0], channel_samples[0][1]) ) - curve.reserve(samples.size) - for j in range(1, samples.size): - curve.lineTo( - QtCore.QPointF(self._samples_x_lin[j], channel_samples[j]) + ) + curve.reserve(channel_samples.shape[0]) + for i in range(1, channel_samples.shape[0]): + curve.lineTo( + self._curve_tf.map( + QtCore.QPointF(channel_samples[i][0], channel_samples[i][1]) ) - self._curve_paths[color_name] = curve - - self._sample_y_min = sample_y_min - self._sample_y_max = sample_y_max + ) + self._curve_paths[color_name] = curve # Add curve(s) to scene - self._curve_rect = QtCore.QRectF() for color_name, curve in self._curve_paths.items(): pen = QtGui.QPen() pen.setColor(QtGui.QColor(color_name)) - pen.setWidthF(0) + pen.setWidthF(0.5) curve_item = self._scene.addPath( curve, pen, QtGui.QBrush(QtCore.Qt.NoBrush) ) - curve_item.setScale(self.CURVE_SCALE) self._curve_items[color_name] = curve_item - self._curve_rect = self._curve_rect.united(curve_item.sceneBoundingRect()) self._curve_init = True diff --git a/src/apps/ocioview/ocioview/inspect/log_inspector.py b/src/apps/ocioview/ocioview/inspect/log_inspector.py index ac026899e..b2cf9fa63 100644 --- a/src/apps/ocioview/ocioview/inspect/log_inspector.py +++ b/src/apps/ocioview/ocioview/inspect/log_inspector.py @@ -4,7 +4,7 @@ from typing import Optional import PyOpenColorIO as ocio -from PySide2 import QtCore, QtGui, QtWidgets +from PySide6 import QtCore, QtGui, QtWidgets from ..log_handlers import set_logging_level from ..message_router import MessageRouter diff --git a/src/apps/ocioview/ocioview/inspect_dock.py b/src/apps/ocioview/ocioview/inspect_dock.py index b68b36cd1..39c352cb1 100644 --- a/src/apps/ocioview/ocioview/inspect_dock.py +++ b/src/apps/ocioview/ocioview/inspect_dock.py @@ -3,7 +3,7 @@ from typing import Optional -from PySide2 import QtCore, QtWidgets +from PySide6 import QtCore, QtWidgets from .inspect.curve_inspector import CurveInspector from .inspect import LogInspector, CodeInspector diff --git a/src/apps/ocioview/ocioview/items/active_display_view_edit.py b/src/apps/ocioview/ocioview/items/active_display_view_edit.py index 5d76b0b12..593a40d44 100644 --- a/src/apps/ocioview/ocioview/items/active_display_view_edit.py +++ b/src/apps/ocioview/ocioview/items/active_display_view_edit.py @@ -3,7 +3,7 @@ from typing import Optional -from PySide2 import QtCore, QtGui, QtWidgets +from PySide6 import QtCore, QtGui, QtWidgets from ..widgets import ItemModelListWidget from ..utils import get_glyph_icon diff --git a/src/apps/ocioview/ocioview/items/active_display_view_model.py b/src/apps/ocioview/ocioview/items/active_display_view_model.py index 297a8e575..63f1d5fd3 100644 --- a/src/apps/ocioview/ocioview/items/active_display_view_model.py +++ b/src/apps/ocioview/ocioview/items/active_display_view_model.py @@ -5,7 +5,7 @@ from typing import Any, Callable, Optional, Type import PyOpenColorIO as ocio -from PySide2 import QtCore, QtGui, QtWidgets +from PySide6 import QtCore, QtGui from ..config_cache import ConfigCache from ..undo import ConfigSnapshotUndoCommand @@ -87,7 +87,7 @@ def get_item_names(self) -> list[str]: def _get_undo_command_type( self, column_desc: ColumnDesc - ) -> Type[QtWidgets.QUndoCommand]: + ) -> Type[QtGui.QUndoCommand]: if column_desc == self.ACTIVE: # Changing check state of the ACTIVE column has side effects related to # display/view order, so a config snapshot is needed to revert the change. diff --git a/src/apps/ocioview/ocioview/items/color_space_edit.py b/src/apps/ocioview/ocioview/items/color_space_edit.py index 5c3c9b2fa..4e5d659a3 100644 --- a/src/apps/ocioview/ocioview/items/color_space_edit.py +++ b/src/apps/ocioview/ocioview/items/color_space_edit.py @@ -4,7 +4,7 @@ from functools import partial from typing import Optional -from PySide2 import QtCore, QtWidgets +from PySide6 import QtCore, QtWidgets import PyOpenColorIO as ocio from ..config_cache import ConfigCache diff --git a/src/apps/ocioview/ocioview/items/color_space_model.py b/src/apps/ocioview/ocioview/items/color_space_model.py index 80f34ee74..3b6c12d2c 100644 --- a/src/apps/ocioview/ocioview/items/color_space_model.py +++ b/src/apps/ocioview/ocioview/items/color_space_model.py @@ -5,7 +5,7 @@ from typing import Any, Optional import PyOpenColorIO as ocio -from PySide2 import QtCore, QtGui +from PySide6 import QtCore, QtGui from ..config_cache import ConfigCache from ..ref_space_manager import ReferenceSpaceManager diff --git a/src/apps/ocioview/ocioview/items/config_item_edit.py b/src/apps/ocioview/ocioview/items/config_item_edit.py index 93aca0dc1..a49fbdc65 100644 --- a/src/apps/ocioview/ocioview/items/config_item_edit.py +++ b/src/apps/ocioview/ocioview/items/config_item_edit.py @@ -4,7 +4,7 @@ from functools import partial from typing import Optional -from PySide2 import QtCore, QtGui, QtWidgets +from PySide6 import QtCore, QtGui, QtWidgets from ..constants import MARGIN_WIDTH from ..transform_manager import TransformManager @@ -47,7 +47,7 @@ def __init__(self, parent: Optional[QtWidgets.QWidget] = None): if self.__has_transforms__: self.__has_tabs__ = True - no_tf_color = palette.color(palette.Disabled, palette.Text) + no_tf_color = palette.color(palette.ColorGroup.Disabled, palette.ColorRole.Text) self._from_ref_icon = get_glyph_icon("mdi6.layers-plus") self._no_from_ref_icon = get_glyph_icon( "mdi6.layers-plus", color=no_tf_color diff --git a/src/apps/ocioview/ocioview/items/config_item_model.py b/src/apps/ocioview/ocioview/items/config_item_model.py index e06ac024c..15a11c9e1 100644 --- a/src/apps/ocioview/ocioview/items/config_item_model.py +++ b/src/apps/ocioview/ocioview/items/config_item_model.py @@ -6,7 +6,7 @@ from typing import Any, Optional, Type, Union import PyOpenColorIO as ocio -from PySide2 import QtCore, QtGui, QtWidgets +from PySide6 import QtCore, QtGui, QtWidgets from ..config_cache import ConfigCache from ..transform_manager import TransformManager, TransformAgent @@ -587,7 +587,7 @@ def _get_item_and_column( def _get_undo_command_type( self, column_desc: ColumnDesc - ) -> Type[QtWidgets.QUndoCommand]: + ) -> Type[QtGui.QUndoCommand]: """ Support overriding the undo command type used to track data changes, per column. diff --git a/src/apps/ocioview/ocioview/items/config_properties_edit.py b/src/apps/ocioview/ocioview/items/config_properties_edit.py index 37b695a8c..58dddba8d 100644 --- a/src/apps/ocioview/ocioview/items/config_properties_edit.py +++ b/src/apps/ocioview/ocioview/items/config_properties_edit.py @@ -5,7 +5,7 @@ from typing import Optional import PyOpenColorIO as ocio -from PySide2 import QtWidgets +from PySide6 import QtWidgets from ..constants import RGB from ..widgets import ( diff --git a/src/apps/ocioview/ocioview/items/config_properties_model.py b/src/apps/ocioview/ocioview/items/config_properties_model.py index 895650802..247a7fb42 100644 --- a/src/apps/ocioview/ocioview/items/config_properties_model.py +++ b/src/apps/ocioview/ocioview/items/config_properties_model.py @@ -5,7 +5,7 @@ from pathlib import Path from typing import Any, Optional -from PySide2 import QtCore +from PySide6 import QtCore import PyOpenColorIO as ocio from .config_item_model import ColumnDesc, BaseConfigItemModel diff --git a/src/apps/ocioview/ocioview/items/delegates.py b/src/apps/ocioview/ocioview/items/delegates.py index d11428b7f..75499e651 100644 --- a/src/apps/ocioview/ocioview/items/delegates.py +++ b/src/apps/ocioview/ocioview/items/delegates.py @@ -4,7 +4,7 @@ from typing import Optional import PyOpenColorIO as ocio -from PySide2 import QtCore, QtWidgets +from PySide6 import QtCore, QtWidgets from ..config_cache import ConfigCache from ..widgets import CallbackComboBox @@ -34,7 +34,6 @@ def createEditor( editor = CallbackComboBox( get_items=ConfigCache.get_color_space_names, editable=True, parent=parent ) - editor.setAutoCompletion(True) editor.completer().setCompletionMode(QtWidgets.QCompleter.PopupCompletion) return editor @@ -119,7 +118,6 @@ def createEditor( raise NotImplementedError widget = CallbackComboBox(get_items=get_items, editable=True, parent=parent) - widget.setAutoCompletion(True) widget.completer().setCompletionMode(QtWidgets.QCompleter.PopupCompletion) return widget diff --git a/src/apps/ocioview/ocioview/items/display_model.py b/src/apps/ocioview/ocioview/items/display_model.py index a12f08ff2..1f8c78213 100644 --- a/src/apps/ocioview/ocioview/items/display_model.py +++ b/src/apps/ocioview/ocioview/items/display_model.py @@ -5,7 +5,7 @@ from typing import Any, Optional import PyOpenColorIO as ocio -from PySide2 import QtCore +from PySide6 import QtCore from ..config_cache import ConfigCache from ..utils import next_name diff --git a/src/apps/ocioview/ocioview/items/display_view_edit.py b/src/apps/ocioview/ocioview/items/display_view_edit.py index 0c5497a7f..059da4655 100644 --- a/src/apps/ocioview/ocioview/items/display_view_edit.py +++ b/src/apps/ocioview/ocioview/items/display_view_edit.py @@ -3,7 +3,7 @@ from typing import Optional -from PySide2 import QtCore, QtGui, QtWidgets +from PySide6 import QtCore, QtGui, QtWidgets from ..utils import get_glyph_icon from .active_display_view_edit import ActiveDisplayViewEdit diff --git a/src/apps/ocioview/ocioview/items/file_rule_edit.py b/src/apps/ocioview/ocioview/items/file_rule_edit.py index ce8641bf0..0dd1b8a2f 100644 --- a/src/apps/ocioview/ocioview/items/file_rule_edit.py +++ b/src/apps/ocioview/ocioview/items/file_rule_edit.py @@ -3,7 +3,7 @@ from typing import Optional -from PySide2 import QtCore, QtWidgets +from PySide6 import QtCore, QtWidgets from ..config_cache import ConfigCache from ..utils import get_glyph_icon diff --git a/src/apps/ocioview/ocioview/items/file_rule_model.py b/src/apps/ocioview/ocioview/items/file_rule_model.py index 0be470cd4..d73c76f5a 100644 --- a/src/apps/ocioview/ocioview/items/file_rule_model.py +++ b/src/apps/ocioview/ocioview/items/file_rule_model.py @@ -6,7 +6,7 @@ from typing import Any, Optional, Union import PyOpenColorIO as ocio -from PySide2 import QtCore, QtGui +from PySide6 import QtCore, QtGui from ..config_cache import ConfigCache from ..undo import ConfigSnapshotUndoCommand diff --git a/src/apps/ocioview/ocioview/items/look_edit.py b/src/apps/ocioview/ocioview/items/look_edit.py index 0ae3e9015..630051c9c 100644 --- a/src/apps/ocioview/ocioview/items/look_edit.py +++ b/src/apps/ocioview/ocioview/items/look_edit.py @@ -5,7 +5,7 @@ from typing import Optional import PyOpenColorIO as ocio -from PySide2 import QtWidgets +from PySide6 import QtWidgets from ..config_cache import ConfigCache from ..widgets import CallbackComboBox, TextEdit diff --git a/src/apps/ocioview/ocioview/items/look_model.py b/src/apps/ocioview/ocioview/items/look_model.py index ef4944692..d7d1a309e 100644 --- a/src/apps/ocioview/ocioview/items/look_model.py +++ b/src/apps/ocioview/ocioview/items/look_model.py @@ -5,7 +5,7 @@ from typing import Any, Optional import PyOpenColorIO as ocio -from PySide2 import QtCore, QtGui +from PySide6 import QtCore, QtGui from ..config_cache import ConfigCache from ..ref_space_manager import ReferenceSpaceManager diff --git a/src/apps/ocioview/ocioview/items/named_transform_edit.py b/src/apps/ocioview/ocioview/items/named_transform_edit.py index 7364f0ef9..d2a7bc77a 100644 --- a/src/apps/ocioview/ocioview/items/named_transform_edit.py +++ b/src/apps/ocioview/ocioview/items/named_transform_edit.py @@ -3,7 +3,7 @@ from typing import Optional -from PySide2 import QtWidgets +from PySide6 import QtWidgets from ..config_cache import ConfigCache from ..utils import get_glyph_icon diff --git a/src/apps/ocioview/ocioview/items/named_transform_model.py b/src/apps/ocioview/ocioview/items/named_transform_model.py index 656af6b11..2dc02acad 100644 --- a/src/apps/ocioview/ocioview/items/named_transform_model.py +++ b/src/apps/ocioview/ocioview/items/named_transform_model.py @@ -5,7 +5,7 @@ from typing import Any, Optional, Union import PyOpenColorIO as ocio -from PySide2 import QtCore, QtGui +from PySide6 import QtCore, QtGui from ..config_cache import ConfigCache from ..utils import get_glyph_icon diff --git a/src/apps/ocioview/ocioview/items/role_edit.py b/src/apps/ocioview/ocioview/items/role_edit.py index af9138266..f527ceef1 100644 --- a/src/apps/ocioview/ocioview/items/role_edit.py +++ b/src/apps/ocioview/ocioview/items/role_edit.py @@ -3,7 +3,7 @@ from typing import Optional -from PySide2 import QtGui, QtWidgets +from PySide6 import QtGui, QtWidgets from ..widgets import ItemModelTableWidget from .delegates import RoleDelegate diff --git a/src/apps/ocioview/ocioview/items/role_model.py b/src/apps/ocioview/ocioview/items/role_model.py index 6650c0c0b..fb5f78bbf 100644 --- a/src/apps/ocioview/ocioview/items/role_model.py +++ b/src/apps/ocioview/ocioview/items/role_model.py @@ -5,7 +5,7 @@ from typing import Any import PyOpenColorIO as ocio -from PySide2 import QtCore +from PySide6 import QtCore from ..config_cache import ConfigCache from .config_item_model import ColumnDesc, BaseConfigItemModel diff --git a/src/apps/ocioview/ocioview/items/rule_edit.py b/src/apps/ocioview/ocioview/items/rule_edit.py index caef94723..cb60da1e8 100644 --- a/src/apps/ocioview/ocioview/items/rule_edit.py +++ b/src/apps/ocioview/ocioview/items/rule_edit.py @@ -3,7 +3,7 @@ from typing import Optional -from PySide2 import QtCore, QtGui, QtWidgets +from PySide6 import QtCore, QtGui, QtWidgets from ..utils import get_glyph_icon from .file_rule_edit import FileRuleEdit diff --git a/src/apps/ocioview/ocioview/items/shared_view_edit.py b/src/apps/ocioview/ocioview/items/shared_view_edit.py index 527fffc5c..db474e531 100644 --- a/src/apps/ocioview/ocioview/items/shared_view_edit.py +++ b/src/apps/ocioview/ocioview/items/shared_view_edit.py @@ -5,7 +5,7 @@ from typing import Optional import PyOpenColorIO as ocio -from PySide2 import QtWidgets +from PySide6 import QtWidgets from ..config_cache import ConfigCache from ..widgets import CallbackComboBox, LineEdit diff --git a/src/apps/ocioview/ocioview/items/shared_view_model.py b/src/apps/ocioview/ocioview/items/shared_view_model.py index b89f981fd..776912778 100644 --- a/src/apps/ocioview/ocioview/items/shared_view_model.py +++ b/src/apps/ocioview/ocioview/items/shared_view_model.py @@ -6,7 +6,7 @@ from typing import Any, Optional import PyOpenColorIO as ocio -from PySide2 import QtCore, QtGui +from PySide6 import QtCore, QtGui from ..config_cache import ConfigCache from .config_item_model import ColumnDesc, BaseConfigItemModel diff --git a/src/apps/ocioview/ocioview/items/view_edit.py b/src/apps/ocioview/ocioview/items/view_edit.py index 349d401da..1172dbe75 100644 --- a/src/apps/ocioview/ocioview/items/view_edit.py +++ b/src/apps/ocioview/ocioview/items/view_edit.py @@ -5,7 +5,7 @@ from typing import Optional import PyOpenColorIO as ocio -from PySide2 import QtCore, QtWidgets +from PySide6 import QtCore, QtWidgets from ..config_cache import ConfigCache from ..transform_manager import TransformManager diff --git a/src/apps/ocioview/ocioview/items/view_model.py b/src/apps/ocioview/ocioview/items/view_model.py index 064b46dff..7fd341263 100644 --- a/src/apps/ocioview/ocioview/items/view_model.py +++ b/src/apps/ocioview/ocioview/items/view_model.py @@ -4,7 +4,7 @@ from typing import Any, Optional, Union import PyOpenColorIO as ocio -from PySide2 import QtCore, QtGui +from PySide6 import QtCore, QtGui from ..config_cache import ConfigCache from ..ref_space_manager import ReferenceSpaceManager diff --git a/src/apps/ocioview/ocioview/items/view_transform_edit.py b/src/apps/ocioview/ocioview/items/view_transform_edit.py index 50ffa0e78..c8a087359 100644 --- a/src/apps/ocioview/ocioview/items/view_transform_edit.py +++ b/src/apps/ocioview/ocioview/items/view_transform_edit.py @@ -4,7 +4,7 @@ from functools import partial from typing import Optional -from PySide2 import QtWidgets +from PySide6 import QtWidgets import PyOpenColorIO as ocio from ..config_cache import ConfigCache diff --git a/src/apps/ocioview/ocioview/items/view_transform_model.py b/src/apps/ocioview/ocioview/items/view_transform_model.py index 63a954082..69595ecf6 100644 --- a/src/apps/ocioview/ocioview/items/view_transform_model.py +++ b/src/apps/ocioview/ocioview/items/view_transform_model.py @@ -5,7 +5,7 @@ from typing import Any, Optional, Union import PyOpenColorIO as ocio -from PySide2 import QtCore, QtGui +from PySide6 import QtCore, QtGui from ..config_cache import ConfigCache from ..utils import get_enum_member, get_glyph_icon diff --git a/src/apps/ocioview/ocioview/items/viewing_rule_edit.py b/src/apps/ocioview/ocioview/items/viewing_rule_edit.py index 67c2ef83f..83c90a70b 100644 --- a/src/apps/ocioview/ocioview/items/viewing_rule_edit.py +++ b/src/apps/ocioview/ocioview/items/viewing_rule_edit.py @@ -3,7 +3,7 @@ from typing import Optional -from PySide2 import QtCore, QtWidgets +from PySide6 import QtCore, QtWidgets from ..config_cache import ConfigCache from ..utils import get_glyph_icon diff --git a/src/apps/ocioview/ocioview/items/viewing_rule_model.py b/src/apps/ocioview/ocioview/items/viewing_rule_model.py index 381635ae8..7e04efa3d 100644 --- a/src/apps/ocioview/ocioview/items/viewing_rule_model.py +++ b/src/apps/ocioview/ocioview/items/viewing_rule_model.py @@ -7,7 +7,7 @@ from typing import Any, Optional, Union import PyOpenColorIO as ocio -from PySide2 import QtCore, QtGui +from PySide6 import QtCore, QtGui from ..config_cache import ConfigCache from ..undo import ConfigSnapshotUndoCommand diff --git a/src/apps/ocioview/ocioview/main_window.py b/src/apps/ocioview/ocioview/main_window.py index e68b165f9..69646b266 100644 --- a/src/apps/ocioview/ocioview/main_window.py +++ b/src/apps/ocioview/ocioview/main_window.py @@ -7,7 +7,7 @@ from typing import Optional import PyOpenColorIO as ocio -from PySide2 import QtCore, QtGui, QtWidgets +from PySide6 import QtCore, QtGui, QtWidgets from .config_cache import ConfigCache from .config_dock import ConfigDock diff --git a/src/apps/ocioview/ocioview/message_router.py b/src/apps/ocioview/ocioview/message_router.py index bbc3744a7..668cb219b 100644 --- a/src/apps/ocioview/ocioview/message_router.py +++ b/src/apps/ocioview/ocioview/message_router.py @@ -10,7 +10,7 @@ from queue import Empty, SimpleQueue import PyOpenColorIO as ocio -from PySide2 import QtCore, QtGui, QtWidgets +from PySide6 import QtCore, QtGui, QtWidgets from .utils import config_to_html, processor_to_ctf_html, processor_to_shader_html diff --git a/src/apps/ocioview/ocioview/settings.py b/src/apps/ocioview/ocioview/settings.py index fded91dd5..9f2c5ac7b 100644 --- a/src/apps/ocioview/ocioview/settings.py +++ b/src/apps/ocioview/ocioview/settings.py @@ -1,7 +1,7 @@ # SPDX-License-Identifier: BSD-3-Clause # Copyright Contributors to the OpenColorIO Project. -from PySide2 import QtCore +from PySide6 import QtCore settings = QtCore.QSettings( diff --git a/src/apps/ocioview/ocioview/style.py b/src/apps/ocioview/ocioview/style.py index 6acf1399a..1f35c1564 100644 --- a/src/apps/ocioview/ocioview/style.py +++ b/src/apps/ocioview/ocioview/style.py @@ -3,7 +3,7 @@ from typing import Optional -from PySide2 import QtGui, QtWidgets +from PySide6 import QtGui, QtWidgets from .constants import ( BORDER_COLOR_ROLE, diff --git a/src/apps/ocioview/ocioview/transform_manager.py b/src/apps/ocioview/ocioview/transform_manager.py index 77d9e96c2..65d72a248 100644 --- a/src/apps/ocioview/ocioview/transform_manager.py +++ b/src/apps/ocioview/ocioview/transform_manager.py @@ -7,7 +7,7 @@ from typing import Callable, Optional import PyOpenColorIO as ocio -from PySide2 import QtCore, QtGui +from PySide6 import QtCore, QtGui from .utils import get_glyph_icon @@ -99,12 +99,15 @@ def set_subscription( tf_agent.item_tf_changed.connect(partial(cls._on_item_tf_changed, slot)) cls._tf_subscriptions[slot] = tf_subscription - # Trigger immediate update to subscribers + # Inform menu subscribers of the menu change cls._update_menu_items() - cls._on_item_tf_changed( - slot, - *item_model.get_item_transforms(item_name), - ) + + # Inform init subscribers of the new subscription + for init_callback in cls._tf_subscribers.get(-1, []): + init_callback(slot) + + # Trigger immediate update to subscribers of this slot + cls._on_item_tf_changed(slot, *item_model.get_item_transforms(item_name)) # Repaint views for previous and new model if prev_item_model is not None: @@ -206,10 +209,27 @@ def subscribe_to_transform_menu(cls, menu_callback: Callable) -> None: menu_callback(cls.get_subscription_menu_items()) @classmethod - def subscribe_to_transforms(cls, slot: int, tf_callback: Callable) -> None: + def subscribe_to_transform_subscription_init(cls, init_callback: Callable) -> None: + """ + Subscribe to transform subscription initialization on all slots. + + :param init_callback: Transform subscription initialization + callback, which will be called whenever a new transform + subscription is initialized with the subscription slot + number. + """ + cls._tf_subscribers[-1].append(init_callback) + + # Trigger immediate update to init subscriber if a transform subscription + # exists. + for slot, tf_subscription in cls._tf_subscriptions.items(): + init_callback(slot) + break + + @classmethod + def subscribe_to_transforms_at(cls, slot: int, tf_callback: Callable) -> None: """ - Subscribe to transform and item name updates at the given slot - number. + Subscribe to transform updates at the given slot number. :param slot: Subscription slot number :param tf_callback: Transform callback, which will be called diff --git a/src/apps/ocioview/ocioview/transforms/allocation_edit.py b/src/apps/ocioview/ocioview/transforms/allocation_edit.py index 96885a9a5..2e7bde4a0 100644 --- a/src/apps/ocioview/ocioview/transforms/allocation_edit.py +++ b/src/apps/ocioview/ocioview/transforms/allocation_edit.py @@ -4,7 +4,7 @@ from typing import Optional import PyOpenColorIO as ocio -from PySide2 import QtCore +from PySide6 import QtCore from ..widgets import EnumComboBox, FloatEdit, FloatEditArray from .transform_edit import BaseTransformEdit diff --git a/src/apps/ocioview/ocioview/transforms/builtin_edit.py b/src/apps/ocioview/ocioview/transforms/builtin_edit.py index 0813d33c9..2be865f24 100644 --- a/src/apps/ocioview/ocioview/transforms/builtin_edit.py +++ b/src/apps/ocioview/ocioview/transforms/builtin_edit.py @@ -4,7 +4,7 @@ from typing import Optional import PyOpenColorIO as ocio -from PySide2 import QtCore +from PySide6 import QtCore from ..widgets import ComboBox from .transform_edit import BaseTransformEdit diff --git a/src/apps/ocioview/ocioview/transforms/cdl_edit.py b/src/apps/ocioview/ocioview/transforms/cdl_edit.py index 174b7cace..d411d16ae 100644 --- a/src/apps/ocioview/ocioview/transforms/cdl_edit.py +++ b/src/apps/ocioview/ocioview/transforms/cdl_edit.py @@ -4,7 +4,7 @@ from typing import Optional import PyOpenColorIO as ocio -from PySide2 import QtCore +from PySide6 import QtCore from ..constants import RGB from ..widgets import EnumComboBox, FloatEdit, FloatEditArray diff --git a/src/apps/ocioview/ocioview/transforms/color_space_edit.py b/src/apps/ocioview/ocioview/transforms/color_space_edit.py index f297ded44..db83ec88b 100644 --- a/src/apps/ocioview/ocioview/transforms/color_space_edit.py +++ b/src/apps/ocioview/ocioview/transforms/color_space_edit.py @@ -4,7 +4,7 @@ from typing import Optional import PyOpenColorIO as ocio -from PySide2 import QtCore +from PySide6 import QtCore from ..config_cache import ConfigCache from ..widgets import CheckBox, CallbackComboBox diff --git a/src/apps/ocioview/ocioview/transforms/display_view_edit.py b/src/apps/ocioview/ocioview/transforms/display_view_edit.py index 059dfc8f4..e5cb3bfae 100644 --- a/src/apps/ocioview/ocioview/transforms/display_view_edit.py +++ b/src/apps/ocioview/ocioview/transforms/display_view_edit.py @@ -4,7 +4,7 @@ from typing import Optional import PyOpenColorIO as ocio -from PySide2 import QtCore, QtWidgets +from PySide6 import QtCore, QtWidgets from ..config_cache import ConfigCache from ..utils import SignalsBlocked diff --git a/src/apps/ocioview/ocioview/transforms/exponent_edit.py b/src/apps/ocioview/ocioview/transforms/exponent_edit.py index d7c294a7d..c5ed3dba2 100644 --- a/src/apps/ocioview/ocioview/transforms/exponent_edit.py +++ b/src/apps/ocioview/ocioview/transforms/exponent_edit.py @@ -4,7 +4,7 @@ from typing import Optional import PyOpenColorIO as ocio -from PySide2 import QtCore +from PySide6 import QtCore from ..constants import RGBA from ..widgets import EnumComboBox, FloatEditArray diff --git a/src/apps/ocioview/ocioview/transforms/exponent_with_linear_edit.py b/src/apps/ocioview/ocioview/transforms/exponent_with_linear_edit.py index 2f78a68b6..004bdb02f 100644 --- a/src/apps/ocioview/ocioview/transforms/exponent_with_linear_edit.py +++ b/src/apps/ocioview/ocioview/transforms/exponent_with_linear_edit.py @@ -4,7 +4,7 @@ from typing import Optional import PyOpenColorIO as ocio -from PySide2 import QtCore +from PySide6 import QtCore from ..constants import RGBA from ..widgets import EnumComboBox, FloatEditArray diff --git a/src/apps/ocioview/ocioview/transforms/exposure_contrast_edit.py b/src/apps/ocioview/ocioview/transforms/exposure_contrast_edit.py index eece95377..891eade24 100644 --- a/src/apps/ocioview/ocioview/transforms/exposure_contrast_edit.py +++ b/src/apps/ocioview/ocioview/transforms/exposure_contrast_edit.py @@ -4,7 +4,7 @@ from typing import Optional import PyOpenColorIO as ocio -from PySide2 import QtCore +from PySide6 import QtCore from ..widgets import EnumComboBox, FloatEdit from .transform_edit import BaseTransformEdit diff --git a/src/apps/ocioview/ocioview/transforms/file_edit.py b/src/apps/ocioview/ocioview/transforms/file_edit.py index 68a633477..464eb0ca9 100644 --- a/src/apps/ocioview/ocioview/transforms/file_edit.py +++ b/src/apps/ocioview/ocioview/transforms/file_edit.py @@ -5,7 +5,7 @@ from typing import Optional import PyOpenColorIO as ocio -from PySide2 import QtCore +from PySide6 import QtCore from ..widgets import EnumComboBox, LineEdit from .transform_edit import BaseTransformEdit diff --git a/src/apps/ocioview/ocioview/transforms/fixed_function_edit.py b/src/apps/ocioview/ocioview/transforms/fixed_function_edit.py index 35935fab2..ebf701908 100644 --- a/src/apps/ocioview/ocioview/transforms/fixed_function_edit.py +++ b/src/apps/ocioview/ocioview/transforms/fixed_function_edit.py @@ -4,7 +4,7 @@ from typing import Optional import PyOpenColorIO as ocio -from PySide2 import QtCore, QtWidgets +from PySide6 import QtCore, QtWidgets from ..widgets import EnumComboBox, FloatEditArray, ExpandingStackedWidget from .transform_edit import BaseTransformEdit diff --git a/src/apps/ocioview/ocioview/transforms/log_affine_edit.py b/src/apps/ocioview/ocioview/transforms/log_affine_edit.py index 285f021e2..4190c9691 100644 --- a/src/apps/ocioview/ocioview/transforms/log_affine_edit.py +++ b/src/apps/ocioview/ocioview/transforms/log_affine_edit.py @@ -4,7 +4,7 @@ from typing import Optional import PyOpenColorIO as ocio -from PySide2 import QtCore +from PySide6 import QtCore from ..constants import RGB from ..widgets import FloatEditArray diff --git a/src/apps/ocioview/ocioview/transforms/log_camera_edit.py b/src/apps/ocioview/ocioview/transforms/log_camera_edit.py index 03b4d605c..ddc865000 100644 --- a/src/apps/ocioview/ocioview/transforms/log_camera_edit.py +++ b/src/apps/ocioview/ocioview/transforms/log_camera_edit.py @@ -4,7 +4,7 @@ from typing import Optional import PyOpenColorIO as ocio -from PySide2 import QtCore +from PySide6 import QtCore from ..constants import RGB from ..widgets import FloatEdit, FloatEditArray diff --git a/src/apps/ocioview/ocioview/transforms/log_edit.py b/src/apps/ocioview/ocioview/transforms/log_edit.py index f845571b7..638eb9417 100644 --- a/src/apps/ocioview/ocioview/transforms/log_edit.py +++ b/src/apps/ocioview/ocioview/transforms/log_edit.py @@ -4,7 +4,7 @@ from typing import Optional import PyOpenColorIO as ocio -from PySide2 import QtCore +from PySide6 import QtCore from ..widgets import FloatEdit from .transform_edit import BaseTransformEdit diff --git a/src/apps/ocioview/ocioview/transforms/look_edit.py b/src/apps/ocioview/ocioview/transforms/look_edit.py index 396ee74d3..e6b0005a1 100644 --- a/src/apps/ocioview/ocioview/transforms/look_edit.py +++ b/src/apps/ocioview/ocioview/transforms/look_edit.py @@ -4,7 +4,7 @@ from typing import Optional import PyOpenColorIO as ocio -from PySide2 import QtCore +from PySide6 import QtCore from ..config_cache import ConfigCache from ..widgets import CheckBox, CallbackComboBox, LineEdit diff --git a/src/apps/ocioview/ocioview/transforms/matrix_edit.py b/src/apps/ocioview/ocioview/transforms/matrix_edit.py index dad31c769..5cd48068b 100644 --- a/src/apps/ocioview/ocioview/transforms/matrix_edit.py +++ b/src/apps/ocioview/ocioview/transforms/matrix_edit.py @@ -4,7 +4,7 @@ from typing import Optional import PyOpenColorIO as ocio -from PySide2 import QtCore, QtGui, QtWidgets +from PySide6 import QtCore, QtGui, QtWidgets from ..constants import RGB, RGBA from ..widgets import ( diff --git a/src/apps/ocioview/ocioview/transforms/range_edit.py b/src/apps/ocioview/ocioview/transforms/range_edit.py index f0d6281dc..c6f4d058b 100644 --- a/src/apps/ocioview/ocioview/transforms/range_edit.py +++ b/src/apps/ocioview/ocioview/transforms/range_edit.py @@ -4,7 +4,7 @@ from typing import Optional import PyOpenColorIO as ocio -from PySide2 import QtCore, QtWidgets +from PySide6 import QtCore, QtWidgets from ..widgets import CheckBox, EnumComboBox, FloatEdit from .transform_edit import BaseTransformEdit diff --git a/src/apps/ocioview/ocioview/transforms/transform_edit.py b/src/apps/ocioview/ocioview/transforms/transform_edit.py index 65821f2e1..9c297fa2a 100644 --- a/src/apps/ocioview/ocioview/transforms/transform_edit.py +++ b/src/apps/ocioview/ocioview/transforms/transform_edit.py @@ -7,7 +7,7 @@ from typing import Optional import PyOpenColorIO as ocio -from PySide2 import QtCore, QtGui, QtWidgets +from PySide6 import QtCore, QtGui, QtWidgets from ..constants import ICON_SIZE_ITEM, BORDER_COLOR_ROLE from ..style import apply_top_tool_bar_style, apply_widget_with_top_tool_bar_style diff --git a/src/apps/ocioview/ocioview/transforms/transform_edit_stack.py b/src/apps/ocioview/ocioview/transforms/transform_edit_stack.py index b01b8edfb..1f7eaf10b 100644 --- a/src/apps/ocioview/ocioview/transforms/transform_edit_stack.py +++ b/src/apps/ocioview/ocioview/transforms/transform_edit_stack.py @@ -5,7 +5,7 @@ from typing import Optional import PyOpenColorIO as ocio -from PySide2 import QtCore, QtGui, QtWidgets +from PySide6 import QtCore, QtGui, QtWidgets # Register all transform edit types from .. import transforms @@ -48,7 +48,7 @@ def __init__(self, parent: Optional[QtCore.QObject] = None): self.add_tf_button.setToolButtonStyle(QtCore.Qt.ToolButtonTextBesideIcon) self.add_tf_button.setMenu(self.tf_menu) - self._start_collapsed_action = QtWidgets.QAction("Start Collapsed") + self._start_collapsed_action = QtGui.QAction("Start Collapsed") self._start_collapsed_action.setCheckable(True) self._start_collapsed_action.triggered[bool].connect( self._on_start_collapsed_changed diff --git a/src/apps/ocioview/ocioview/undo.py b/src/apps/ocioview/ocioview/undo.py index aee5ddef6..d8b0d14f9 100644 --- a/src/apps/ocioview/ocioview/undo.py +++ b/src/apps/ocioview/ocioview/undo.py @@ -6,16 +6,16 @@ from typing import Any, Optional import PyOpenColorIO as ocio -from PySide2 import QtCore, QtWidgets +from PySide6 import QtCore, QtGui from .config_cache import ConfigCache -undo_stack = QtWidgets.QUndoStack() +undo_stack = QtGui.QUndoStack() """Global undo stack.""" -class ItemModelUndoCommand(QtWidgets.QUndoCommand): +class ItemModelUndoCommand(QtGui.QUndoCommand): """ Undo command for use in item model ``setData`` implementations. @@ -31,7 +31,7 @@ def __init__( index: QtCore.QPersistentModelIndex, redo_value: Any, undo_value: Any, - parent: Optional[QtWidgets.QUndoCommand] = None, + parent: Optional[QtGui.QUndoCommand] = None, ): """ :param text: Undo/redo command menu text @@ -59,7 +59,7 @@ def undo(self) -> None: model.setData(self._index, self._undo_value) -class ConfigSnapshotUndoCommand(QtWidgets.QUndoCommand): +class ConfigSnapshotUndoCommand(QtGui.QUndoCommand): """ Undo command for complex config changes like item adds, moves, and deletes, to be used as a content manager in which the entry @@ -71,7 +71,7 @@ def __init__( text: str, model: Optional[QtCore.QAbstractItemModel] = None, item_name: Optional[str] = None, - parent: Optional[QtWidgets.QUndoCommand] = None, + parent: Optional[QtGui.QUndoCommand] = None, ): """ :param text: Undo/redo command menu text diff --git a/src/apps/ocioview/ocioview/utils.py b/src/apps/ocioview/ocioview/utils.py index 23a02e088..31091a8fe 100644 --- a/src/apps/ocioview/ocioview/utils.py +++ b/src/apps/ocioview/ocioview/utils.py @@ -12,7 +12,7 @@ from pygments import highlight from pygments.lexers import GLShaderLexer, HLSLShaderLexer, XmlLexer, YamlLexer from pygments.formatters import HtmlFormatter -from PySide2 import QtCore, QtGui, QtWidgets +from PySide6 import QtCore, QtGui, QtWidgets from .constants import ICON_SCALE_FACTOR, ICON_SIZE_BUTTON diff --git a/src/apps/ocioview/ocioview/viewer/image_plane.py b/src/apps/ocioview/ocioview/viewer/image_plane.py index b36c4cc4a..7b06fd4cb 100644 --- a/src/apps/ocioview/ocioview/viewer/image_plane.py +++ b/src/apps/ocioview/ocioview/viewer/image_plane.py @@ -11,13 +11,16 @@ from functools import partial from pathlib import Path from typing import Any, Optional +import sys -import imath import numpy as np from OpenGL import GL -import OpenImageIO as oiio +try: + import OpenImageIO as oiio +except: + import imageio as iio import PyOpenColorIO as ocio -from PySide2 import QtCore, QtGui, QtWidgets, QtOpenGL +from PySide6 import QtCore, QtGui, QtWidgets, QtOpenGLWidgets from ..log_handlers import message_queue from ..ref_space_manager import ReferenceSpaceManager @@ -88,7 +91,7 @@ """ -class ImagePlane(QtOpenGL.QGLWidget): +class ImagePlane(QtOpenGLWidgets.QOpenGLWidget): """ Qt-wrapped OpenGL window for drawing with PyOpenGL. """ @@ -125,8 +128,8 @@ def __init__(self, parent: Optional[QtWidgets.QWidget] = None): self._ocio_uniform_ids = {} # MVP matrix components - self._model_view_mat = imath.M44f() - self._proj_mat = imath.M44f() + self._model_view_mat = np.eye(4) + self._proj_mat = np.eye(4) # Keyboard shortcuts self._shortcuts = [] @@ -136,10 +139,10 @@ def __init__(self, parent: Optional[QtWidgets.QWidget] = None): self._mouse_last_pos = QtCore.QPointF() # Image texture - self._image_buf = None + self._image_array = None self._image_tex = None - self._image_pos = imath.V2f(0.0, 0.0) - self._image_size = imath.V2f(1.0, 1.0) + self._image_pos = np.array([0.0, 0.0]) + self._image_size = np.array([1.0, 1.0]) self._image_scale = 1.0 # Image plane VAO @@ -170,8 +173,8 @@ def initializeGL(self) -> None: GL.GL_TEXTURE_2D, 0, GL.GL_RGBA32F, - self._image_size.x, - self._image_size.y, + self._image_size[0], + self._image_size[1], 0, GL.GL_RGBA, GL.GL_FLOAT, @@ -261,6 +264,31 @@ def initializeGL(self) -> None: self._build_program() + def _orthographicProjMatrix(self, near, far, left, right, top, bottom): + rightPlusLeft = right + left + rightMinusLeft = right - left + + topPlusBottom = top + bottom + topMinusBottom = top - bottom + + farPlusNear = far + near + farMinusNear = far - near + + tx = -rightPlusLeft / rightMinusLeft + ty = -topPlusBottom / topMinusBottom + tz = -farPlusNear / farMinusNear + + A = 2 / rightMinusLeft + B = 2 / topMinusBottom + C = -2 / farMinusNear + + return np.array([ + [A, 0, 0, tx], + [0, B, 0, ty], + [0, 0, C, tz], + [0, 0, 0, 1 ] + ]) + def resizeGL(self, w: int, h: int) -> None: """ Called whenever the widget is resized. @@ -271,18 +299,15 @@ def resizeGL(self, w: int, h: int) -> None: GL.glViewport(0, 0, w, h) # Center image plane - # fmt: off - frustum = imath.Frustumf( + # fmt: on + self._proj_mat = self._orthographicProjMatrix( -1.0, # Near 1.0, # Far -w / 2.0, # Left w / 2.0, # Right h / 2.0, # Top -h / 2.0, # Bottom - True, ) - # fmt: on - self._proj_mat = frustum.projectionMatrix() self._update_model_view_mat() @@ -301,10 +326,10 @@ def paintGL(self) -> None: self._use_ocio_uniforms() # Set uniforms - mvp_mat = self._proj_mat * self._model_view_mat + mvp_mat = self._proj_mat @ self._model_view_mat mvp_mat_loc = GL.glGetUniformLocation(self._shader_program, "mvpMat") GL.glUniformMatrix4fv( - mvp_mat_loc, 1, GL.GL_FALSE, self._m44f_to_ndarray(mvp_mat) + mvp_mat_loc, 1, GL.GL_FALSE, mvp_mat.T ) image_tex_loc = GL.glGetUniformLocation(self._shader_program, "imageTex") @@ -322,6 +347,55 @@ def paintGL(self) -> None: GL.glBindVertexArray(0) + def load_oiio(self, image_path: Path) -> np.ndarray: + image_buf = oiio.ImageBuf(image_path.as_posix()) + spec = image_buf.spec() + + # Convert to RGBA, filling missing color channels with 0.0, and a + # missing alpha with 1.0. + if spec.nchannels < 4: + image_buf = oiio.ImageBufAlgo.channels( + image_buf, + tuple( + list(range(spec.nchannels)) + + ([0.0] * (4 - spec.nchannels - 1)) + + [1.0] + ), + newchannelnames=("R", "G", "B", "A"), + ) + elif spec.nchannels > 4: + image_buf = oiio.ImageBufAlgo.channels( + image_buf, (0, 1, 2, 3), newchannelnames=("R", "G", "B", "A") + ) + + # Get pixels as 32-bit float NumPy array + return image_buf.get_pixels(oiio.FLOAT) + + def load_iio(self, image_path: Path) -> np.ndarray: + data = iio.imread(image_path.as_posix()) + + # Convert to 32-bit float + if not np.issubdtype(data.dtype, np.floating): + data = data.astype(np.float32) / np.iinfo(data.dtype).max + if data.dtype != np.float32: + data = data.astype(np.float32) + + # Convert to RGBA, filling missing color channels with 0.0, and a + # missing alpha with 1.0. + nchannels = 1 + if len(data.shape) == 3: + nchannels = data.shape[-1] + + while nchannels < 3: + data = np.dstack((data, np.zeros(data.shape[:2]))) + nchannels += 1 + if nchannels < 4: + data = np.dstack((data, np.ones(data.shape[:2]))) + if nchannels > 4: + data = data[..., :4] + + return data + def load_image(self, image_path: Path) -> None: """ Load an image into the image plane texture. @@ -342,34 +416,18 @@ def load_image(self, image_path: Path) -> None: color_space_name = ocio.ROLE_DEFAULT self._ocio_input_color_space = color_space_name - self._image_buf = oiio.ImageBuf(image_path.as_posix()) - spec = self._image_buf.spec() - - # Convert to RGBA, filling missing color channels with 0.0, and a - # missing alpha with 1.0. - if spec.nchannels < 4: - self._image_buf = oiio.ImageBufAlgo.channels( - self._image_buf, - tuple( - list(range(spec.nchannels)) - + ([0.0] * (4 - spec.nchannels - 1)) - + [1.0] - ), - newchannelnames=("R", "G", "B", "A"), - ) - elif spec.nchannels > 4: - self._image_buf = oiio.ImageBufAlgo.channels( - self._image_buf, (0, 1, 2, 3), newchannelnames=("R", "G", "B", "A") - ) + if "OpenImageIO" in sys.modules: + self._image_array = self.load_oiio(image_path) + else: + self._image_array = self.load_iio(image_path) - # Get pixels as 32-bit float NumPy array - data = self._image_buf.get_pixels(oiio.FLOAT) + width = self._image_array.shape[1] + height = self._image_array.shape[0] # Stash image size for pan/zoom calculations - self._image_pos.x = spec.x - self._image_pos.y = spec.y - self._image_size.x = spec.width - self._image_size.y = spec.height + + self._image_pos = np.array([0, 1], dtype=np.float64) + self._image_size = np.array([width, height], dtype=np.float64) # Load image data into texture self.makeCurrent() @@ -379,16 +437,16 @@ def load_image(self, image_path: Path) -> None: GL.GL_TEXTURE_2D, 0, GL.GL_RGBA32F, - spec.width, - spec.height, + width, + height, 0, GL.GL_RGBA, GL.GL_FLOAT, - data.ravel(), + self._image_array.ravel(), ) self.image_loaded.emit( - image_path, int(self._image_size.x), int(self._image_size.y) + image_path, int(self._image_size[0]), int(self._image_size[1]) ) self.update_ocio_proc(input_color_space=self._ocio_input_color_space) @@ -628,7 +686,7 @@ def mouseMoveEvent(self, event: QtGui.QMouseEvent) -> None: pos = event.pos() if self._mouse_pressed: - offset = imath.V2f(*(pos - self._mouse_last_pos).toTuple()) + offset = np.array([*(pos - self._mouse_last_pos).toTuple()]) self._mouse_last_pos = pos self.pan(offset, update=True) @@ -638,29 +696,24 @@ def mouseMoveEvent(self, event: QtGui.QMouseEvent) -> None: # Trace mouse position through the inverse MVP matrix to update sampled # pixel. - screen_pos = imath.V3f( + screen_pos = np.array([ pos.x() / widget_w * 2.0 - 1.0, (widget_h - pos.y() - 1) / widget_h * 2.0 - 1.0, 0.0, - ) - model_pos = ( - (self._proj_mat * self._model_view_mat) - .inverse() - .multVecMatrix(screen_pos) - ) - pixel_pos = ( - imath.V2f(model_pos.x + 0.5, model_pos.y + 0.5) * self._image_size - ) + 1.0 + ]) + model_pos = np.linalg.inv(self._proj_mat @ self._model_view_mat) @ screen_pos + pixel_pos = np.array([model_pos[0] + 0.5, model_pos[1] + 0.5]) * self._image_size # Broadcast sample position if ( - self._image_buf is not None - and 0 <= pixel_pos.x <= self._image_size.x - and 0 <= pixel_pos.y <= self._image_size.y + self._image_array is not None + and 0 <= pixel_pos[0] <= self._image_size[0] + and 0 <= pixel_pos[1] <= self._image_size[1] ): - pixel_x = math.floor(pixel_pos.x) - pixel_y = math.floor(pixel_pos.y) - pixel_input = list(self._image_buf.getpixel(pixel_x, pixel_y)) + pixel_x = math.floor(pixel_pos[0]) + pixel_y = math.floor(pixel_pos[1]) + pixel_input = list(self._image_array[pixel_y, pixel_x]) if len(pixel_input) < 3: pixel_input += [0.0] * (3 - len(pixel_input)) elif len(pixel_input) > 3: @@ -685,9 +738,9 @@ def wheelEvent(self, event: QtGui.QWheelEvent) -> None: # Fit image to frame if h > w: - min_scale = w / self._image_size.x + min_scale = w / self._image_size[0] else: - min_scale = h / self._image_size.y + min_scale = h / self._image_size[1] # Fill frame with 1 pixel with 0.5 pixel overscan max_scale = max(w, h) * 1.5 @@ -712,7 +765,7 @@ def wheelEvent(self, event: QtGui.QWheelEvent) -> None: self.zoom(event.pos(), scale, update=True, absolute=True) def pan( - self, offset: imath.V2f, update: bool = True, absolute: bool = False + self, offset: np.ndarray, update: bool = True, absolute: bool = False ) -> None: """ Pan the viewport by the specified offset in screen space. @@ -746,7 +799,7 @@ def zoom( :param absolute: When True, amount will be treated as an absolute scale to set the viewport to. """ - offset = imath.V2f(*(point - self.rect().center()).toTuple()) + offset = np.array([*(point - self.rect().center()).toTuple()]) self.pan(-offset, update=False) @@ -759,7 +812,7 @@ def zoom( self.pan(offset, update=update) - if self._image_buf is not None: + if self._image_array is not None: self.scale_changed.emit(self._image_scale) def fit(self, update: bool = True) -> None: @@ -773,12 +826,12 @@ def fit(self, update: bool = True) -> None: # Fit image to frame if h > w: - scale = w / self._image_size.x + scale = w / self._image_size[0] else: - scale = h / self._image_size.y + scale = h / self._image_size[1] self.zoom(QtCore.QPoint(), scale, update=False, absolute=True) - self.pan(imath.V2f(), update=update, absolute=True) + self.pan(np.array([0.0, 0.0]), update=update, absolute=True) def _install_shortcuts(self) -> None: """ @@ -787,7 +840,7 @@ def _install_shortcuts(self) -> None: # R,G,B,A = view channel # C = view color for i, key in enumerate(("R", "G", "B", "A", "C")): - channel_shortcut = QtWidgets.QShortcut(QtGui.QKeySequence(key), self) + channel_shortcut = QtGui.QShortcut(QtGui.QKeySequence(key), self) channel_shortcut.activated.connect( partial(self.update_ocio_proc, channel=i) ) @@ -795,7 +848,7 @@ def _install_shortcuts(self) -> None: # Number keys = Subscribe to transform @ slot for i in range(10): - subscribe_shortcut = QtWidgets.QShortcut(QtGui.QKeySequence(str(i)), self) + subscribe_shortcut = QtGui.QShortcut(QtGui.QKeySequence(str(i)), self) subscribe_shortcut.activated.connect( lambda slot=i: self.tf_subscription_requested.emit(slot) ) @@ -803,7 +856,7 @@ def _install_shortcuts(self) -> None: # Ctrl + Number keys = Power of 2 scale: 1 = x1, 2 = x2, 3 = x4, ... for i in range(9): - scale_shortcut = QtWidgets.QShortcut( + scale_shortcut = QtGui.QShortcut( QtGui.QKeySequence(f"Ctrl+{i + 1}"), self ) scale_shortcut.activated.connect( @@ -814,7 +867,7 @@ def _install_shortcuts(self) -> None: self._shortcuts.append(scale_shortcut) # F = fit image to viewport - fit_shortcut = QtWidgets.QShortcut(QtGui.QKeySequence("F"), self) + fit_shortcut = QtGui.QShortcut(QtGui.QKeySequence("F"), self) fit_shortcut.activated.connect(self.fit) self._shortcuts.append(fit_shortcut) @@ -912,18 +965,17 @@ def _update_model_view_mat(self, update: bool = True) -> None: :param bool update: Optionally redraw the window """ - size = self._widget_size_to_v2f(self) + size = np.array([self.width(), self.height()]) - self._model_view_mat.makeIdentity() + self._model_view_mat = np.eye(4) # Flip Y to account for different OIIO/OpenGL image origin - self._model_view_mat.scale(imath.V3f(1.0, -1.0, 1.0)) + self._model_view_mat *= [1.0, -1.0, 1.0, 1.0] - self._model_view_mat.scale(imath.V3f(self._image_scale, self._image_scale, 1.0)) - self._model_view_mat.translate( - self._v2f_to_v3f(self._image_pos / size * 2.0, 0.0) - ) - self._model_view_mat.scale(self._v2f_to_v3f(self._image_size, 1.0)) + self._model_view_mat *= [self._image_scale, self._image_scale, 1.0, 1.0] + self._model_view_mat[:2, -1] += self._image_pos / size * 2.0 + + self._model_view_mat *= self._image_size.tolist() + [1.0, 1.0] # Use nearest interpolation when scaling up to see pixels if self._image_scale > 1.0: @@ -1138,43 +1190,3 @@ def _update_ocio_channel_hot(self, channel: int) -> None: else: for i in range(4): self._ocio_channel_hot[i] = 1 - - def _m44f_to_ndarray(self, m44f: imath.M44f) -> np.ndarray: - """ - Convert Imath.M44f matrix to a flat NumPy float32 array, so that it - can be passed to PyOpenGL functions. - - :param m44f: 4x4 matrix - :return: NumPy array - """ - # fmt: off - return np.array( - [ - m44f[0][0], m44f[0][1], m44f[0][2], m44f[0][3], - m44f[1][0], m44f[1][1], m44f[1][2], m44f[1][3], - m44f[2][0], m44f[2][1], m44f[2][2], m44f[2][3], - m44f[3][0], m44f[3][1], m44f[3][2], m44f[3][3], - ], - dtype=np.float32, - ) - # fmt: on - - def _v2f_to_v3f(self, v2f: imath.V2f, z: float) -> imath.V3f: - """ - Extend an Imath.V2f to an Imath.V3f by adding a Z dimension - value. - - :param v2f: 2D float vector to extend - :param z: Z value to extend vector with - :return: 3D float vector - """ - return imath.V3f(v2f.x, v2f.y, z) - - def _widget_size_to_v2f(self, widget: QtWidgets.QWidget) -> imath.V2f: - """ - Get QWidget dimensions as an Imath.V2f. - - :param widget: Widget to get dimensions of - :return: 2D float vector - """ - return imath.V2f(widget.width(), widget.height()) diff --git a/src/apps/ocioview/ocioview/viewer/image_viewer.py b/src/apps/ocioview/ocioview/viewer/image_viewer.py index 25e277c09..11436a06c 100644 --- a/src/apps/ocioview/ocioview/viewer/image_viewer.py +++ b/src/apps/ocioview/ocioview/viewer/image_viewer.py @@ -6,7 +6,7 @@ from typing import Generator, Optional import PyOpenColorIO as ocio -from PySide2 import QtCore, QtGui, QtWidgets +from PySide6 import QtCore, QtGui, QtWidgets from ..transform_manager import TransformManager from ..config_cache import ConfigCache @@ -253,7 +253,7 @@ def __init__(self, parent: Optional[QtWidgets.QWidget] = None): self.image_plane.tf_subscription_requested.connect( self._on_tf_subscription_requested ) - self.input_color_space_box.currentIndexChanged[str].connect( + self.input_color_space_box.currentTextChanged[str].connect( self._on_input_color_space_changed ) self.tf_box.currentIndexChanged[int].connect(self._on_transform_changed) @@ -266,6 +266,9 @@ def __init__(self, parent: Optional[QtWidgets.QWidget] = None): # Initialize TransformManager.subscribe_to_transform_menu(self._on_transform_menu_changed) + TransformManager.subscribe_to_transform_subscription_init( + self._on_transform_subscription_init + ) self.update() self._on_sample_precision_changed(self.sample_precision_box.value()) self._on_sample_changed(-1, -1, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0) @@ -515,6 +518,19 @@ def _on_transform_menu_changed( # Force update transform self._on_transform_changed(0) + def _on_transform_subscription_init(self, slot: int) -> None: + """ + If this viewer is not subscribed to a specific transform + subscription slot, subscribe to the first slot to receive a + transform subscription. + + :param slot: Transform subscription slot + """ + if self._tf_subscription_slot == -1: + index = self.tf_box.findData(slot) + if index != -1: + self.tf_box.setCurrentIndex(index) + def _float_to_uint8(self, value: float) -> int: """ :param value: Float value @@ -529,7 +545,7 @@ def _on_transform_changed(self, index: int) -> None: self.clear_transform() else: self._tf_subscription_slot = self.tf_box.currentData() - TransformManager.subscribe_to_transforms( + TransformManager.subscribe_to_transforms_at( self._tf_subscription_slot, self.set_transform ) diff --git a/src/apps/ocioview/ocioview/viewer_dock.py b/src/apps/ocioview/ocioview/viewer_dock.py index 26cdb075f..b295fc35c 100644 --- a/src/apps/ocioview/ocioview/viewer_dock.py +++ b/src/apps/ocioview/ocioview/viewer_dock.py @@ -5,7 +5,7 @@ from pathlib import Path from typing import Optional -from PySide2 import QtCore, QtWidgets +from PySide6 import QtCore, QtWidgets from .settings import settings from .transform_manager import TransformManager diff --git a/src/apps/ocioview/ocioview/widgets/check_box.py b/src/apps/ocioview/ocioview/widgets/check_box.py index eb2566a74..7dcdbc828 100644 --- a/src/apps/ocioview/ocioview/widgets/check_box.py +++ b/src/apps/ocioview/ocioview/widgets/check_box.py @@ -1,7 +1,7 @@ # SPDX-License-Identifier: BSD-3-Clause # Copyright Contributors to the OpenColorIO Project. -from PySide2 import QtCore, QtWidgets +from PySide6 import QtCore, QtWidgets from ..utils import SignalsBlocked diff --git a/src/apps/ocioview/ocioview/widgets/combo_box.py b/src/apps/ocioview/ocioview/widgets/combo_box.py index 2f1f0f0c6..5ddc2b308 100644 --- a/src/apps/ocioview/ocioview/widgets/combo_box.py +++ b/src/apps/ocioview/ocioview/widgets/combo_box.py @@ -4,7 +4,7 @@ import enum from typing import Callable, Optional -from PySide2 import QtCore, QtGui, QtWidgets +from PySide6 import QtCore, QtGui, QtWidgets from ..utils import SignalsBlocked @@ -12,7 +12,7 @@ class ComboBox(QtWidgets.QComboBox): def __init__(self, parent: Optional[QtCore.QObject] = None): super().__init__(parent=parent) - self.setSizeAdjustPolicy(QtWidgets.QComboBox.AdjustToMinimumContentsLength) + self.setSizeAdjustPolicy(QtWidgets.QComboBox.AdjustToMinimumContentsLengthWithIcon) # DataWidgetMapper user property interface @QtCore.Property(str, user=True) @@ -103,7 +103,6 @@ def __init__( self._item_icon = item_icon self.setEditable(editable) - self.setAutoCompletion(True) self.setInsertPolicy(QtWidgets.QComboBox.NoInsert) completer = self.completer() diff --git a/src/apps/ocioview/ocioview/widgets/item_view.py b/src/apps/ocioview/ocioview/widgets/item_view.py index 2d81b5ec5..44d05fe18 100644 --- a/src/apps/ocioview/ocioview/widgets/item_view.py +++ b/src/apps/ocioview/ocioview/widgets/item_view.py @@ -3,7 +3,7 @@ from typing import Callable, Optional -from PySide2 import QtCore, QtGui, QtWidgets +from PySide6 import QtCore, QtGui, QtWidgets from ..constants import ICON_SIZE_BUTTON, ICON_SIZE_ITEM from ..style import apply_top_tool_bar_style, apply_widget_with_top_tool_bar_style @@ -241,6 +241,6 @@ def _on_preset_menu_requested(self) -> None: else: self.preset_menu.addAction(preset) - def _on_preset_triggered(self, action: QtWidgets.QAction) -> None: + def _on_preset_triggered(self, action: QtGui.QAction) -> None: """Add a new item from the triggered preset.""" self.add_item(action.text()) diff --git a/src/apps/ocioview/ocioview/widgets/layout.py b/src/apps/ocioview/ocioview/widgets/layout.py index e21cb71ea..331b057f2 100644 --- a/src/apps/ocioview/ocioview/widgets/layout.py +++ b/src/apps/ocioview/ocioview/widgets/layout.py @@ -3,7 +3,7 @@ from typing import Optional -from PySide2 import QtCore, QtWidgets +from PySide6 import QtCore, QtWidgets class FormLayout(QtWidgets.QFormLayout): diff --git a/src/apps/ocioview/ocioview/widgets/line_edit.py b/src/apps/ocioview/ocioview/widgets/line_edit.py index 3588bd70e..0562cb549 100644 --- a/src/apps/ocioview/ocioview/widgets/line_edit.py +++ b/src/apps/ocioview/ocioview/widgets/line_edit.py @@ -5,7 +5,7 @@ from pathlib import Path from typing import Any, Optional, Sequence -from PySide2 import QtCore, QtGui, QtWidgets +from PySide6 import QtCore, QtGui, QtWidgets from ..constants import R_COLOR, G_COLOR, B_COLOR from ..utils import SignalsBlocked, get_glyph_icon @@ -50,7 +50,7 @@ class PathEdit(LineEdit): QtWidgets.QFileDialog.AnyFile: "ph.file", QtWidgets.QFileDialog.ExistingFile: "ph.file", QtWidgets.QFileDialog.Directory: "ph.folder", - QtWidgets.QFileDialog.DirectoryOnly: "ph.folder", + QtWidgets.QFileDialog.ShowDirsOnly: "ph.folder", } def __init__( @@ -75,7 +75,7 @@ def __init__( if self._file_mode in self.BROWSE_GLYPHS: self._browse_action = self.addAction( get_glyph_icon(self.BROWSE_GLYPHS[self._file_mode]), - self.TrailingPosition, + self.ActionPosition.TrailingPosition, ) self._browse_action.triggered.connect(self._on_browse_action_triggered) diff --git a/src/apps/ocioview/ocioview/widgets/list_widget.py b/src/apps/ocioview/ocioview/widgets/list_widget.py index 17d806e94..8fc686058 100644 --- a/src/apps/ocioview/ocioview/widgets/list_widget.py +++ b/src/apps/ocioview/ocioview/widgets/list_widget.py @@ -1,14 +1,15 @@ # SPDX-License-Identifier: BSD-3-Clause # Copyright Contributors to the OpenColorIO Project. -from typing import Callable, Optional, Union +from typing import Callable, Optional, TYPE_CHECKING, Union -from PySide2 import QtCore, QtGui, QtWidgets +from PySide6 import QtCore, QtGui, QtWidgets -from ..items.config_item_model import BaseConfigItemModel from ..utils import SignalsBlocked, next_name from .item_view import BaseItemView +if TYPE_CHECKING: + from ..items.config_item_model import BaseConfigItemModel class StringListWidget(BaseItemView): """ @@ -221,7 +222,7 @@ class ItemModelListWidget(BaseItemView): def __init__( self, - model: BaseConfigItemModel, + model: "BaseConfigItemModel", model_column: int, item_flags: QtCore.Qt.ItemFlags = BaseItemView.DEFAULT_ITEM_FLAGS, item_icon: Optional[QtGui.QIcon] = None, diff --git a/src/apps/ocioview/ocioview/widgets/log_view.py b/src/apps/ocioview/ocioview/widgets/log_view.py index 79cfd4ea6..91f79b8f4 100644 --- a/src/apps/ocioview/ocioview/widgets/log_view.py +++ b/src/apps/ocioview/ocioview/widgets/log_view.py @@ -3,7 +3,7 @@ from typing import Any, Optional -from PySide2 import QtCore, QtGui, QtWidgets +from PySide6 import QtCore, QtGui, QtWidgets from ..constants import ICON_SIZE_BUTTON from ..style import ( diff --git a/src/apps/ocioview/ocioview/widgets/structure.py b/src/apps/ocioview/ocioview/widgets/structure.py index b29571756..dc1492a2e 100644 --- a/src/apps/ocioview/ocioview/widgets/structure.py +++ b/src/apps/ocioview/ocioview/widgets/structure.py @@ -4,7 +4,7 @@ from pathlib import Path from typing import Optional, Union -from PySide2 import QtCore, QtGui, QtWidgets +from PySide6 import QtCore, QtGui, QtWidgets from ..constants import ICON_SIZE_BUTTON, BORDER_COLOR_ROLE from ..style import apply_top_tool_bar_style diff --git a/src/apps/ocioview/ocioview/widgets/table_widget.py b/src/apps/ocioview/ocioview/widgets/table_widget.py index c0da4af86..fc6df1d5c 100644 --- a/src/apps/ocioview/ocioview/widgets/table_widget.py +++ b/src/apps/ocioview/ocioview/widgets/table_widget.py @@ -3,7 +3,7 @@ from typing import Callable, Optional -from PySide2 import QtCore, QtGui, QtWidgets +from PySide6 import QtCore, QtGui, QtWidgets from ..constants import ICON_SIZE_ITEM from ..utils import SignalsBlocked, next_name diff --git a/src/apps/ocioview/ocioview/widgets/text_edit.py b/src/apps/ocioview/ocioview/widgets/text_edit.py index b1a18dd12..d654215ec 100644 --- a/src/apps/ocioview/ocioview/widgets/text_edit.py +++ b/src/apps/ocioview/ocioview/widgets/text_edit.py @@ -3,7 +3,7 @@ from typing import Optional -from PySide2 import QtCore, QtWidgets +from PySide6 import QtCore, QtWidgets from ..utils import SignalsBlocked diff --git a/src/apps/ocioview/requirements.txt b/src/apps/ocioview/requirements.txt index 1a453b819..2da54714f 100644 --- a/src/apps/ocioview/requirements.txt +++ b/src/apps/ocioview/requirements.txt @@ -1,7 +1,7 @@ -# imath +imageio numpy -# OpenImageIO pygments PyOpenGL -PySide2 +PySide6 QtAwesome +OpenColorIO \ No newline at end of file diff --git a/src/apps/pyociodisplay/pyociodisplay.py b/src/apps/pyociodisplay/pyociodisplay.py index 1588001a7..3142f9ba9 100644 --- a/src/apps/pyociodisplay/pyociodisplay.py +++ b/src/apps/pyociodisplay/pyociodisplay.py @@ -11,11 +11,10 @@ import logging import sys -from PySide2 import QtCore, QtGui, QtWidgets, QtOpenGL +from PySide6 import QtCore, QtGui, QtWidgets, QtOpenGL from OpenGL import GL import PyOpenColorIO as ocio import OpenImageIO as oiio -import imath import numpy as np diff --git a/src/bindings/python/CMakeLists.txt b/src/bindings/python/CMakeLists.txt index d7da50fd7..94af56302 100644 --- a/src/bindings/python/CMakeLists.txt +++ b/src/bindings/python/CMakeLists.txt @@ -170,7 +170,7 @@ if(UNIX) set_property(TARGET PyOpenColorIO PROPERTY POSITION_INDEPENDENT_CODE ON) endif() -if (UNIX AND NOT CMAKE_SKIP_RPATH AND NOT CMAKE_INSTALL_RPATH) +if (UNIX AND NOT CMAKE_SKIP_RPATH AND CMAKE_INSTALL_RPATH) # Update the default RPATH so the Python binding dynamic library can find the OpenColorIO # dynamic library based on the default installation directory structure. if (APPLE) diff --git a/src/cmake/Config.cmake.in b/src/cmake/Config.cmake.in index c122b013c..4e2367b09 100644 --- a/src/cmake/Config.cmake.in +++ b/src/cmake/Config.cmake.in @@ -34,15 +34,18 @@ if (NOT @BUILD_SHARED_LIBS@) # NOT @BUILD_SHARED_LIBS@ find_dependency(pystring @pystring_VERSION@) endif() - if (NOT TARGET yaml-cpp AND NOT TARGET yaml-cpp::yaml-cpp) + if (NOT TARGET yaml-cpp::yaml-cpp) find_dependency(yaml-cpp @yaml-cpp_VERSION@) + if (TARGET yaml-cpp AND NOT TARGET yaml-cpp::yaml-cpp) + add_library(yaml-cpp::yaml-cpp ALIAS yaml-cpp) + endif() endif() if (NOT TARGET ZLIB::ZLIB) # ZLIB_VERSION is available starting CMake 3.26+. # ZLIB_VERSION_STRING is still available for backward compatibility. # See https://cmake.org/cmake/help/git-stage/module/FindZLIB.html - + if (@ZLIB_VERSION@) # @ZLIB_VERSION@ find_dependency(ZLIB @ZLIB_VERSION@) else() diff --git a/src/utils/StringUtils.h b/src/utils/StringUtils.h index 78721f112..cc1cf4cd4 100644 --- a/src/utils/StringUtils.h +++ b/src/utils/StringUtils.h @@ -106,7 +106,7 @@ inline std::string LeftTrim(std::string str, char c) // Starting from the left, trim all the space characters i.e. space, tabulation, etc. inline std::string LeftTrim(std::string str) { - const auto it = std::find_if(str.begin(), str.end(), [](char ch) { return !std::isspace(ch); }); + const auto it = std::find_if(str.begin(), str.end(), [](char ch) { return !std::isspace(static_cast(ch)); }); str.erase(str.begin(), it); return str; } @@ -123,7 +123,7 @@ inline std::string RightTrim(std::string str, char c) inline std::string RightTrim(std::string str) { const auto it = - std::find_if(str.rbegin(), str.rend(), [](char ch) { return !std::isspace(ch); }); + std::find_if(str.rbegin(), str.rend(), [](char ch) { return !std::isspace(static_cast(ch)); }); str.erase(it.base(), str.end()); return str; } diff --git a/tests/cpu/BitDepthUtils_tests.cpp b/tests/cpu/BitDepthUtils_tests.cpp index 95b1f0487..2c45bc47a 100644 --- a/tests/cpu/BitDepthUtils_tests.cpp +++ b/tests/cpu/BitDepthUtils_tests.cpp @@ -16,9 +16,6 @@ OCIO_ADD_TEST(BitDepthUtils, get_bitdepth_max_value) OCIO_CHECK_EQUAL(OCIO::GetBitDepthMaxValue(OCIO::BIT_DEPTH_F16), 1.0); OCIO_CHECK_EQUAL(OCIO::GetBitDepthMaxValue(OCIO::BIT_DEPTH_F32), 1.0); - - OCIO_CHECK_THROW_WHAT( - OCIO::GetBitDepthMaxValue((OCIO::BitDepth)42), OCIO::Exception, "not supported"); } OCIO_ADD_TEST(BitDepthUtils, is_float_bitdepth) @@ -36,9 +33,6 @@ OCIO_ADD_TEST(BitDepthUtils, is_float_bitdepth) OCIO_CHECK_THROW_WHAT( OCIO::IsFloatBitDepth(OCIO::BIT_DEPTH_UINT32), OCIO::Exception, "not supported"); - - OCIO_CHECK_THROW_WHAT( - OCIO::IsFloatBitDepth((OCIO::BitDepth)42), OCIO::Exception, "not supported"); } OCIO_ADD_TEST(BitDepthUtils, get_channel_size) diff --git a/tests/cpu/ColorSpace_tests.cpp b/tests/cpu/ColorSpace_tests.cpp index 8c37c3797..ac793b81e 100644 --- a/tests/cpu/ColorSpace_tests.cpp +++ b/tests/cpu/ColorSpace_tests.cpp @@ -4,9 +4,10 @@ #include +#include + #include "ColorSpace.cpp" -#include #include "testutils/UnitTest.h" #include "UnitTestUtils.h" @@ -869,14 +870,37 @@ inactive_colorspaces: [display_linear-trans, scene_linear-trans] description: | No encoding. Considered linear since it is equivalent to the reference space. isdata: false + + - ! + name: linear_mtx_from_file + description: This is an identity matrix and therefore linear, but is in an external file. + isdata: false + to_scene_reference: ! + children: + - ! {src: clf/matrix_windows.clf, interpolation: linear} + + - ! + name: linear_lut3d_from_file + description: | + This is a Lut3D which is linear across it's unbounded range but, like all LUTs, clamps + outside it's [0,1] domain. Therefore, when the algorithm inputs [4,4,4], it is no different + than inputing [1,1,1] and so it is not determined to be linear. + isdata: false + to_scene_reference: ! + children: + - ! {src: clf/lut3d_as_matrix.clf, interpolation: linear} )" }; // Load config. std::istringstream is; is.str(TEST_CONFIG); - OCIO::ConstConfigRcPtr config; + OCIO::ConstConfigRcPtr src_config; - OCIO_CHECK_NO_THROW(config = OCIO::Config::CreateFromStream(is)); + OCIO_CHECK_NO_THROW(src_config = OCIO::Config::CreateFromStream(is)); + + OCIO::ConfigRcPtr config = src_config->createEditableCopy(); + config->setSearchPath(OCIO::GetTestFilesDir().c_str()); + OCIO_REQUIRE_ASSERT(config); OCIO_CHECK_NO_THROW(config->validate()); @@ -927,6 +951,8 @@ inactive_colorspaces: [display_linear-trans, scene_linear-trans] testSceneReferred("scene_nonlin-trans", false, __LINE__); testSceneReferred("scene_linear-trans-alias", true, __LINE__); testSceneReferred("scene_ref", true, __LINE__); + testSceneReferred("linear_mtx_from_file", true, __LINE__); + testSceneReferred("linear_lut3d_from_file", false, __LINE__); } { @@ -945,6 +971,8 @@ inactive_colorspaces: [display_linear-trans, scene_linear-trans] testDisplayReferred("scene_nonlin-trans", false, __LINE__); testDisplayReferred("scene_linear-trans-alias", false, __LINE__); testDisplayReferred("scene_ref", false, __LINE__); + testDisplayReferred("linear_mtx_from_file", false, __LINE__); + testDisplayReferred("linear_lut3d_from_file", false, __LINE__); } } @@ -954,7 +982,7 @@ OCIO_ADD_TEST(ConfigUtils, processor_to_known_colorspace) ocio_profile_version: 2 roles: - default: raw + default: raw data scene_linear: ref_cs display_colorspaces: @@ -973,12 +1001,14 @@ ocio_profile_version: 2 # Put a couple of test color space first in the config since the heuristics stop upon success. - ! - name: File color space - description: Verify that that FileTransforms load correctly when running the heuristics. + name: File color space AP0 to linear rec.709 + description: Would be useable by the heuristics except for the clamping and the fact that it's a clf. isdata: false from_scene_reference: ! children: - - ! {src: lut1d_green.ctf} + - ! + children: + - ! {src: clf/lut3d_as_matrix.clf} - ! name: CS Transform color space @@ -989,7 +1019,7 @@ ocio_profile_version: 2 - ! {src: ref_cs, dst: not sRGB} - ! - name: raw + name: raw data description: A data colorspace (should not be used). isdata: true @@ -1004,6 +1034,31 @@ ocio_profile_version: 2 isdata: false to_scene_reference: ! {style: ACEScct_to_ACES2065-1} + - ! + name: sRGB - curve + description: Just the sRGB gamma curve. Note - None of the heuristics should be able to use this. + isdata: false + from_scene_reference: ! + children: + - ! {gamma: 2.4, offset: 0.055, direction: inverse} + + - ! + name: sRGB - curve 2 + description: Another sRGB curve the heuristics shouldn't use. NB - the transform uses a matrix. + isdata: false + to_scene_reference: ! + children: + - ! {src: sRGB_to_linear.spi1d, interpolation: linear} + + - ! + name: pseudo sRGB + description: Ensure that a gamma 2.2 Rec.709 space is not mistaken for an sRGB space. + isdata: false + from_scene_reference: ! + children: + - ! {matrix: [2.52168618674388, -1.13413098823972, -0.387555198504164, 0, -0.276479914229922, 1.37271908766826, -0.096239173438334, 0, -0.0153780649660342, -0.152975335867399, 1.16835340083343, 0, 0, 0, 0, 1]} + - ! {value: [2.2, 2.2, 2.2, 1], direction: inverse} + - ! name: ACES cg description: An ACEScg space with an unusual spelling. @@ -1029,6 +1084,16 @@ ocio_profile_version: 2 - ! {matrix: [1.45143931614567, -0.23651074689374, -0.214928569251925, 0, -0.0765537733960206, 1.17622969983357, -0.0996759264375522, 0, 0.00831614842569772, -0.00603244979102102, 0.997716301365323, 0, 0, 0, 0, 1]} - ! {gamma: 2.4, offset: 0.055, direction: inverse} + - ! + name: OCIO v1 -- sRGB + description: The sRGB texture space from the legacy v1 ACES config. Usable by the heuristics despite the file. + isdata: false + from_reference: ! + children: + - ! {matrix: [0.952552, 0, 9.36786e-05, 0, 0.343966, 0.728166, -0.0721325, 0, 0, 0, 1.00883, 0, 0, 0, 0, 1]} + - ! {matrix: [3.2096, -1.55743, -0.495805, 0, -0.970989, 1.88517, 0.0394894, 0, 0.0597193, -0.210104, 1.14312, 0, 0, 0, 0, 1]} + - ! {src: sRGB_to_linear.spi1d, interpolation: linear, direction: inverse} + - ! name: Texture -- sRGB description: An sRGB Texture space, spelled differently than in the built-in config. @@ -1053,18 +1118,20 @@ ocio_profile_version: 2 // Make all color spaces suitable for the heuristics inactive. // The heuristics don't look at inactive color spaces. - editableCfg->setInactiveColorSpaces("ACES cg, Linear ITU-R BT.709, Texture -- sRGB"); + editableCfg->setInactiveColorSpaces("ACES cg, Linear ITU-R BT.709, Texture -- sRGB, OCIO v1 -- sRGB"); std::string srcColorSpaceName = "not sRGB"; std::string builtinColorSpaceName = "Gamma 2.2 AP1 - Texture"; // Test throw if no suitable spaces are present. { - OCIO_CHECK_THROW( + OCIO_CHECK_THROW_WHAT( auto proc = OCIO::Config::GetProcessorToBuiltinColorSpace(editableCfg, srcColorSpaceName.c_str(), builtinColorSpaceName.c_str()), - OCIO::Exception + OCIO::Exception, + "Heuristics were not able to find a known color space in the provided config. " + "Please set the interchange roles." ); } @@ -1080,7 +1147,7 @@ ocio_profile_version: 2 // Now make various spaces active and test that they enable the heuristics to find // the appropriate interchange spaces. - editableCfg->setInactiveColorSpaces("ACES cg, Linear ITU-R BT.709"); + editableCfg->setInactiveColorSpaces("ACES cg, Linear ITU-R BT.709, OCIO v1 -- sRGB"); { // Uses "sRGB Texture" to find the reference. auto proc = OCIO::Config::GetProcessorToBuiltinColorSpace(editableCfg, @@ -1090,7 +1157,7 @@ ocio_profile_version: 2 } // Test using linear color space from_ref direction. - editableCfg->setInactiveColorSpaces("ACES cg, Texture -- sRGB"); + editableCfg->setInactiveColorSpaces("ACES cg, Texture -- sRGB, OCIO v1 -- sRGB"); { // Uses "Linear Rec.709 (sRGB)" to find the reference. auto proc = OCIO::Config::GetProcessorToBuiltinColorSpace(editableCfg, @@ -1100,7 +1167,7 @@ ocio_profile_version: 2 } // Test linear color space to_ref direction. - editableCfg->setInactiveColorSpaces("Linear ITU-R BT.709, Texture -- sRGB"); + editableCfg->setInactiveColorSpaces("Linear ITU-R BT.709, Texture -- sRGB, OCIO v1 -- sRGB"); { // Uses "ACEScg" to find the reference. auto proc = OCIO::Config::GetProcessorToBuiltinColorSpace(editableCfg, @@ -1119,7 +1186,7 @@ ocio_profile_version: 2 "ref_cs"); // Make the reference space inactive too. - editableCfg->setInactiveColorSpaces("ACES cg, Linear ITU-R BT.709, ref_cs"); + editableCfg->setInactiveColorSpaces("ACES cg, Linear ITU-R BT.709, ref_cs, OCIO v1 -- sRGB"); { // Uses "sRGB Texture" to find the reference. auto proc = OCIO::Config::GetProcessorFromBuiltinColorSpace(builtinColorSpaceName.c_str(), @@ -1129,7 +1196,7 @@ ocio_profile_version: 2 } // Test using linear color space from_ref direction. - editableCfg->setInactiveColorSpaces("ACES cg, Texture -- sRGB, ref_cs"); + editableCfg->setInactiveColorSpaces("ACES cg, Texture -- sRGB, ref_cs, OCIO v1 -- sRGB"); { // Uses "Linear Rec.709 (sRGB)" to find the reference. auto proc = OCIO::Config::GetProcessorFromBuiltinColorSpace(builtinColorSpaceName.c_str(), @@ -1139,7 +1206,7 @@ ocio_profile_version: 2 } // Test linear color space to_ref direction. - editableCfg->setInactiveColorSpaces("Linear ITU-R BT.709, Texture -- sRGB, ref_cs"); + editableCfg->setInactiveColorSpaces("Linear ITU-R BT.709, Texture -- sRGB, ref_cs, OCIO v1 -- sRGB"); { // Uses "ACEScg" to find the reference. auto proc = OCIO::Config::GetProcessorFromBuiltinColorSpace(builtinColorSpaceName.c_str(), @@ -1163,6 +1230,30 @@ ocio_profile_version: 2 OCIO_CHECK_EQUAL(std::string(builtinInterchange), std::string("ACES2065-1")); } + editableCfg->setInactiveColorSpaces("ACES cg, Linear ITU-R BT.709, OCIO v1 -- sRGB"); + { + // Uses "sRGB Texture" to find the reference. + const char * srcInterchange = nullptr; + const char * builtinInterchange = nullptr; + OCIO::Config::IdentifyInterchangeSpace(&srcInterchange, &builtinInterchange, + editableCfg, "Linear ITU-R BT.709", + builtinConfig, "lin_rec709_srgb"); + OCIO_CHECK_EQUAL(std::string(srcInterchange), std::string("ref_cs")); + OCIO_CHECK_EQUAL(std::string(builtinInterchange), std::string("ACES2065-1")); + } + + editableCfg->setInactiveColorSpaces("ACES cg, Linear ITU-R BT.709, Texture -- sRGB"); + { + // Uses "OCIO v1 -- sRGB" to find the reference. + const char * srcInterchange = nullptr; + const char * builtinInterchange = nullptr; + OCIO::Config::IdentifyInterchangeSpace(&srcInterchange, &builtinInterchange, + editableCfg, "Linear ITU-R BT.709", + builtinConfig, "lin_rec709_srgb"); + OCIO_CHECK_EQUAL(std::string(srcInterchange), std::string("ref_cs")); + OCIO_CHECK_EQUAL(std::string(builtinInterchange), std::string("ACES2065-1")); + } + // Set the interchange role. In order to prove that it is being used rather than // the heuristics, set it to something wrong and check that it gets returned anyway. editableCfg->setRole("aces_interchange", "Texture -- sRGB"); @@ -1180,19 +1271,21 @@ ocio_profile_version: 2 editableCfg->setRole("aces_interchange", ""); // Check what happens if a totally bogus config is passed for the built-in config. - // (It fails in the first heuristic that tries to use one of the known built-in spaces.) OCIO::ConstConfigRcPtr rawCfg = OCIO::Config::CreateRaw(); { const char * srcInterchange = nullptr; const char * builtinInterchange = nullptr; OCIO_CHECK_THROW_WHAT( OCIO::Config::IdentifyInterchangeSpace(&srcInterchange, &builtinInterchange, - editableCfg, "Raw", + editableCfg, "raw data", rawCfg, "raw"), OCIO::Exception, + // (It fails in the first heuristic that tries to use one of the known + // built-in spaces, hence the surprising error message.) "Could not find destination color space 'sRGB - Texture'" ); } + // Check what happens if the source color space doesn't exist. { const char * srcInterchange = nullptr; @@ -1205,6 +1298,7 @@ ocio_profile_version: 2 "Could not find source color space 'Foo'." ); } + // Check what happens if the destination color space doesn't exist. { const char * srcInterchange = nullptr; @@ -1222,7 +1316,7 @@ ocio_profile_version: 2 // Test IdentifyBuiltinColorSpace. // - editableCfg->setInactiveColorSpaces(""); + editableCfg->setInactiveColorSpaces("OCIO v1 -- sRGB"); { // Uses "Texture -- sRGB" to find the reference. @@ -1242,7 +1336,15 @@ ocio_profile_version: 2 OCIO_CHECK_EQUAL(std::string(csname), std::string("ref_cs")); } - editableCfg->setInactiveColorSpaces("Texture -- sRGB, ref_cs"); + editableCfg->setInactiveColorSpaces("Texture -- sRGB"); + + { + // Uses "OCIO v1 -- sRGB" to find the reference. + const char * csname = OCIO::Config::IdentifyBuiltinColorSpace(editableCfg, builtinConfig, "sRGB - Texture"); + OCIO_CHECK_EQUAL(std::string(csname), std::string("OCIO v1 -- sRGB")); + } + + editableCfg->setInactiveColorSpaces("Texture -- sRGB, ref_cs, OCIO v1 -- sRGB"); { // Uses "ACEScg" to find the reference. @@ -1252,8 +1354,9 @@ ocio_profile_version: 2 { // Uses "ACEScg" to find the reference. + // If ColorSpaceTransforms were allowed, this would find "CS Transform color space" instead. const char * csname = OCIO::Config::IdentifyBuiltinColorSpace(editableCfg, builtinConfig, "ACEScct"); - OCIO_CHECK_EQUAL(std::string(csname), std::string("CS Transform color space")); + OCIO_CHECK_EQUAL(std::string(csname), std::string("not sRGB")); } // Aliases for built-in color spaces must work. @@ -1263,6 +1366,12 @@ ocio_profile_version: 2 OCIO_CHECK_EQUAL(std::string(csname), std::string("ACES cg")); } + // Test that a data space is handled properly. + { + const char * csname = OCIO::Config::IdentifyBuiltinColorSpace(editableCfg, builtinConfig, "Raw"); + OCIO_CHECK_EQUAL(std::string(csname), std::string("raw data")); + } + // Display-referred spaces are not supported unless the display-referred interchange // role is present. { diff --git a/tests/cpu/Config_tests.cpp b/tests/cpu/Config_tests.cpp index 5d30fda5f..abb003bdd 100644 --- a/tests/cpu/Config_tests.cpp +++ b/tests/cpu/Config_tests.cpp @@ -4,10 +4,10 @@ #include +#include + #include "Config.cpp" -#include "utils/StringUtils.h" -#include #include "testutils/UnitTest.h" #include "UnitTestLogUtils.h" #include "UnitTestUtils.h" @@ -768,6 +768,55 @@ OCIO_ADD_TEST(Config, serialize_searchpath) } } +OCIO_ADD_TEST(Config, serialize_environment) +{ + { + OCIO::ConfigRcPtr config = OCIO::Config::Create(); + config->setMajorVersion(1); + config->setMinorVersion(0); + + std::ostringstream os; + config->serialize(os); + StringUtils::StringVec osvec = StringUtils::SplitByLines(os.str()); + + // A v1 config does not write the environment section if it's empty. + const std::string expected{ "search_path: \"\"" }; + OCIO_CHECK_EQUAL(osvec[2], expected); + } + { + OCIO::ConfigRcPtr config = OCIO::Config::Create(); + config->setMajorVersion(2); + config->setMinorVersion(0); + + std::ostringstream os; + config->serialize(os); + StringUtils::StringVec osvec = StringUtils::SplitByLines(os.str()); + + // A v2 config does write the environment section, even if it's empty. + const std::string expected1{ "environment:" }; + const std::string expected2{ " {}" }; + OCIO_CHECK_EQUAL(osvec[2], expected1); + OCIO_CHECK_EQUAL(osvec[3], expected2); + } + { + OCIO::ConfigRcPtr config = OCIO::Config::Create(); + config->setMajorVersion(1); + config->setMinorVersion(0); + + config->addEnvironmentVar("SHOT", "0001"); + + std::ostringstream os; + config->serialize(os); + StringUtils::StringVec osvec = StringUtils::SplitByLines(os.str()); + + // A v1 config does write the environment section if it's not empty. + const std::string expected1{ "environment:" }; + const std::string expected2{ " SHOT: 0001" }; + OCIO_CHECK_EQUAL(osvec[2], expected1); + OCIO_CHECK_EQUAL(osvec[3], expected2); + } +} + OCIO_ADD_TEST(Config, validation) { { @@ -1452,7 +1501,7 @@ OCIO_ADD_TEST(Config, context_variable_with_colorspacename) // Set $VAR3 and check again. - OCIO_CHECK_NO_THROW(cfg->addEnvironmentVar("VAR3", "cs1")); + OCIO_CHECK_NO_THROW(cfg->addEnvironmentVar("VAR3", "file.clf")); OCIO_CHECK_NO_THROW(cfg->validate()); } @@ -1537,6 +1586,31 @@ OCIO_ADD_TEST(Config, context_variable_with_colorspacename) OCIO::Exception, "Color space '$VAR3' could not be found."); } + + // Repeat the test using a NamedTransform for one of the color spaces. + + { + std::string configStr + = std::string(CONFIG) + + " from_scene_reference: ! {src: $VAR3, dst: cs1}\n" + + "named_transforms:\n" + + " - !\n" + + " name: nt1\n" + + " transform: ! {min_in_value: 0, min_out_value: 0}\n"; + + std::istringstream iss; + iss.str(configStr); + + OCIO::ConfigRcPtr cfg; + OCIO_CHECK_NO_THROW(cfg = OCIO::Config::CreateFromStream(iss)->createEditableCopy()); + + OCIO_CHECK_NO_THROW(cfg->addEnvironmentVar("VAR3", "nt1")); + OCIO_CHECK_NO_THROW(cfg->validate()); + + OCIO::ContextRcPtr ctx; + OCIO_CHECK_NO_THROW(ctx = cfg->getCurrentContext()->createEditableCopy()); + OCIO_CHECK_NO_THROW(cfg->getProcessor(ctx, "cs1", "cs2")); + } } OCIO_ADD_TEST(Config, context_variable_with_role) @@ -5924,6 +5998,7 @@ ocio_profile_version: 2 displayname: - ! {name: view1, colorspace: displaytest1} - ! {name: view2, view_transform: vt1, display_colorspace: display2} + - ! {name: view3, colorspace: data_space} view_transforms: - ! @@ -6166,6 +6241,13 @@ ocio_profile_version: 2 auto ff4 = OCIO_DYNAMIC_POINTER_CAST(t4); OCIO_CHECK_ASSERT(ff4); + // If one of the spaces is a data space, the whole result must be a no-op. + OCIO_CHECK_NO_THROW(p = OCIO::Config::GetProcessorFromConfigs( + config2, "test2", config1, "displayname", "view3", OCIO::TRANSFORM_DIR_FORWARD)); + OCIO_REQUIRE_ASSERT(p); + group = p->createGroupTransform(); + OCIO_REQUIRE_EQUAL(group->getNumTransforms(), 0); + constexpr const char * SIMPLE_CONFIG3{ R"( ocio_profile_version: 2 diff --git a/tests/cpu/Context_tests.cpp b/tests/cpu/Context_tests.cpp index f78bad8d9..c8d4052ff 100644 --- a/tests/cpu/Context_tests.cpp +++ b/tests/cpu/Context_tests.cpp @@ -4,11 +4,12 @@ #include +#include + #include "Context.cpp" #include "PathUtils.h" #include "Platform.h" -#include "pystring/pystring.h" #include "testutils/UnitTest.h" namespace OCIO = OCIO_NAMESPACE; @@ -171,5 +172,3 @@ OCIO_ADD_TEST(Context, string_vars) OCIO_CHECK_EQUAL(std::string("var3"), ctx1->getStringVarNameByIndex(2)); OCIO_CHECK_EQUAL(std::string("val3"), ctx1->getStringVarByIndex(2)); } - - diff --git a/tests/cpu/UnitTestUtils.h b/tests/cpu/UnitTestUtils.h index 4c65b0f52..bbdac3554 100644 --- a/tests/cpu/UnitTestUtils.h +++ b/tests/cpu/UnitTestUtils.h @@ -13,12 +13,13 @@ # endif #endif +#include + #include #include "MathUtils.h" #include "Op.h" #include "Platform.h" -#include "pystring/pystring.h" #include "CPUInfoConfig.h" namespace OCIO_NAMESPACE diff --git a/tests/cpu/ops/exposurecontrast/ExposureContrastOpData_tests.cpp b/tests/cpu/ops/exposurecontrast/ExposureContrastOpData_tests.cpp index f330c3c84..177e08f72 100644 --- a/tests/cpu/ops/exposurecontrast/ExposureContrastOpData_tests.cpp +++ b/tests/cpu/ops/exposurecontrast/ExposureContrastOpData_tests.cpp @@ -63,10 +63,6 @@ OCIO_ADD_TEST(ExposureContrastOpData, style) OCIO_CHECK_NO_THROW(styleName = ConvertToString(OCIO::ExposureContrastOpData::STYLE_LOGARITHMIC_REV)); OCIO_CHECK_EQUAL(styleName, OCIO::EC_STYLE_LOGARITHMIC_REV); - - OCIO_CHECK_THROW_WHAT( - ConvertToString((OCIO::ExposureContrastOpData::Style)-1), - OCIO::Exception, "Unknown exposure contrast style"); } OCIO_ADD_TEST(ExposureContrastOpData, accessors) @@ -336,4 +332,3 @@ OCIO_ADD_TEST(ExposureContrastOpData, replace_dynamic_property) OCIO::Exception); OCIO_CHECK_THROW(ec1.getDynamicProperty(OCIO::DYNAMIC_PROPERTY_CONTRAST), OCIO::Exception); } - diff --git a/tests/cpu/ops/lut3d/Lut3DOp_tests.cpp b/tests/cpu/ops/lut3d/Lut3DOp_tests.cpp index 5a1c67778..6377c7778 100644 --- a/tests/cpu/ops/lut3d/Lut3DOp_tests.cpp +++ b/tests/cpu/ops/lut3d/Lut3DOp_tests.cpp @@ -126,11 +126,6 @@ OCIO_ADD_TEST(GenerateIdentityLut3D, throw_lut) lut.data(), lutSize, 2, OCIO::LUT3DORDER_FAST_RED), OCIO::Exception, "less than 3 channels"); - // GenerateIdentityLut3D with unknown order. - OCIO_CHECK_THROW_WHAT(GenerateIdentityLut3D( - lut.data(), lutSize, 3, (OCIO::Lut3DOrder)42), - OCIO::Exception, "Unknown Lut3DOrder"); - // Get3DLutEdgeLenFromNumPixels with not cubic size. OCIO_CHECK_THROW_WHAT(OCIO::Get3DLutEdgeLenFromNumPixels(10), OCIO::Exception, "Cannot infer 3D LUT size"); @@ -674,4 +669,3 @@ OCIO_ADD_TEST(Lut3DTransform, build_op) // TODO: Port syncolor test: renderer\test\CPURenderer_cases.cpp_inc - CPURendererLUT3D_Green // TODO: Port syncolor test: renderer\test\CPURenderer_cases.cpp_inc - CPURendererLUT3D_Red // TODO: Port syncolor test: renderer\test\CPURenderer_cases.cpp_inc - CPURendererLUT3D_Example - diff --git a/tests/cpu/transforms/ColorSpaceTransform_tests.cpp b/tests/cpu/transforms/ColorSpaceTransform_tests.cpp index 6a78d9d7a..82b39e696 100644 --- a/tests/cpu/transforms/ColorSpaceTransform_tests.cpp +++ b/tests/cpu/transforms/ColorSpaceTransform_tests.cpp @@ -834,6 +834,24 @@ OCIO_ADD_TEST(ColorSpaceTransform, context_variables) OCIO_CHECK_EQUAL(std::string("ENV1"), usedContextVars->getStringVarNameByIndex(0)); OCIO_CHECK_EQUAL(std::string("exposure_contrast_linear.ctf"), usedContextVars->getStringVarByIndex(0)); + + // Case 5 - Context variable indirectly used via a NamedTransform. + + OCIO::NamedTransformRcPtr namedTransform = OCIO::NamedTransform::Create(); + namedTransform->setName("nt"); + OCIO::FileTransformRcPtr file2 = OCIO::FileTransform::Create(); + file2->setSrc("$ENV1"); + namedTransform->setTransform(file2, OCIO::TRANSFORM_DIR_FORWARD); + OCIO_CHECK_NO_THROW(cfg->addNamedTransform(namedTransform)); + + // 'cst' now uses 'nt' which is a NamedTransform whose transform uses a context variable. + cst->setSrc("nt"); + usedContextVars = OCIO::Context::Create(); // New & empty instance. + OCIO_CHECK_ASSERT(OCIO::CollectContextVariables(*cfg, *ctx, *cst, usedContextVars)); + OCIO_CHECK_EQUAL(1, usedContextVars->getNumStringVars()); + OCIO_CHECK_EQUAL(std::string("ENV1"), usedContextVars->getStringVarNameByIndex(0)); + OCIO_CHECK_EQUAL(std::string("exposure_contrast_linear.ctf"), + usedContextVars->getStringVarByIndex(0)); } // Please see (Config, named_transform_processor) in NamedTransform_tests.cpp for coverage of diff --git a/tests/cpu/transforms/FileTransform_tests.cpp b/tests/cpu/transforms/FileTransform_tests.cpp index 55315822c..0301ac02d 100644 --- a/tests/cpu/transforms/FileTransform_tests.cpp +++ b/tests/cpu/transforms/FileTransform_tests.cpp @@ -484,3 +484,74 @@ OCIO_ADD_TEST(FileTransform, context_variables) OCIO_CHECK_EQUAL(std::string("01"), usedContextVars->getStringVarByIndex(1)); } } + +OCIO_ADD_TEST(FileTransform, cc_file_with_different_file_extension) +{ + static const std::string BASE_CONFIG = + "ocio_profile_version: 1\n" + "description: Minimal\n" + "search_path: " + OCIO::GetTestFilesDir() + "\n" + "\n" + "roles:\n" + " default: basic\n" + " scene_linear: basic\n" + " data: basic\n" + " reference: basic\n" + " compositing_log: basic\n" + " color_timing: basic\n" + " color_picking: basic\n" + " texture_paint: basic\n" + " matte_paint: basic\n" + " rendering: basic\n" + " aces_interchange: basic\n" + " cie_xyz_d65_interchange: basic\n" + "\n" + "displays:\n" + " display:\n" + " - ! {name: basic, colorspace: basic }\n" + " - ! {name: cdl, colorspace: basic_cdl }\n" + "\n" + "colorspaces:\n" + " - !\n" + " name: basic\n" + "\n" + " - !\n" + " name: basic_cdl\n"; + + { + static const std::string CONFIG = BASE_CONFIG + + " from_reference: ! { src: cdl_test_cc_file_with_extension.cdl }\n"; + std::istringstream iss; + iss.str(CONFIG); + + OCIO::ConstConfigRcPtr cfg; + OCIO_CHECK_NO_THROW(cfg = OCIO::Config::CreateFromStream(iss)); + OCIO_CHECK_NO_THROW(cfg->validate()); + + + OCIO::ConstTransformRcPtr tr1 = cfg->getColorSpace("basic_cdl")->getTransform( + OCIO::COLORSPACE_DIR_FROM_REFERENCE + ); + OCIO::ConstFileTransformRcPtr fTr1 = OCIO::DynamicPtrCast(tr1); + OCIO_CHECK_ASSERT(fTr1); + OCIO_CHECK_NO_THROW(cfg->getProcessor(tr1)); + } + { + static const std::string CONFIG = BASE_CONFIG + + " from_reference: ! { src: cdl_test_cc_file_with_extension.ccc }\n"; + std::istringstream iss; + iss.str(CONFIG); + + OCIO::ConstConfigRcPtr cfg; + OCIO_CHECK_NO_THROW(cfg = OCIO::Config::CreateFromStream(iss)); + OCIO_CHECK_NO_THROW(cfg->validate()); + + + OCIO::ConstTransformRcPtr tr2 = cfg->getColorSpace("basic_cdl")->getTransform( + OCIO::COLORSPACE_DIR_FROM_REFERENCE + ); + OCIO::ConstFileTransformRcPtr fTr2 = OCIO::DynamicPtrCast(tr2); + OCIO_CHECK_ASSERT(fTr2); + OCIO_CHECK_NO_THROW(cfg->getProcessor(tr2)); + } +} diff --git a/tests/data/files/cdl_test_cc_file_with_extension.ccc b/tests/data/files/cdl_test_cc_file_with_extension.ccc new file mode 100644 index 000000000..70ad93687 --- /dev/null +++ b/tests/data/files/cdl_test_cc_file_with_extension.ccc @@ -0,0 +1,12 @@ + + + + No Op CDL + 1.00000 1.00000 1.00000 + 0.00000 0.00000 0.00000 + 1.00000 1.00000 1.00000 + + + 1.00000 + + diff --git a/tests/data/files/cdl_test_cc_file_with_extension.cdl b/tests/data/files/cdl_test_cc_file_with_extension.cdl new file mode 100644 index 000000000..70ad93687 --- /dev/null +++ b/tests/data/files/cdl_test_cc_file_with_extension.cdl @@ -0,0 +1,12 @@ + + + + No Op CDL + 1.00000 1.00000 1.00000 + 0.00000 0.00000 0.00000 + 1.00000 1.00000 1.00000 + + + 1.00000 + + diff --git a/tests/data/files/clf/lut3d_as_matrix.clf b/tests/data/files/clf/lut3d_as_matrix.clf new file mode 100644 index 000000000..f772f7216 --- /dev/null +++ b/tests/data/files/clf/lut3d_as_matrix.clf @@ -0,0 +1,18 @@ + + + This Lut3D is equivalent (on its limited domain) to a matrix multiply from linear ap0 to linear rec 709 + ap0 + rec 709 + + + 0 0 0 +-396.469 -98.4527 1195.23 +-1160.22 1404.29 -156.494 +-1556.68 1305.84 1038.73 + 2579.68 -282.839 -15.7318 + 2183.22 -381.292 1179.49 + 1419.47 1121.45 -172.226 + 1023 1023 1023 + + + diff --git a/tests/data/files/sRGB_to_linear.spi1d b/tests/data/files/sRGB_to_linear.spi1d new file mode 100644 index 000000000..f1fb422a7 --- /dev/null +++ b/tests/data/files/sRGB_to_linear.spi1d @@ -0,0 +1,4107 @@ +Version 1 +From -0.125 1.125 +Length 4101 +Components 1 +{ + -0.00967492260062 + -0.00965132522842 + -0.00962772785623 + -0.00960413048403 + -0.00958053311183 + -0.00955693573964 + -0.00953333836744 + -0.00950974099524 + -0.00948614362305 + -0.00946254625085 + -0.00943894887865 + -0.00941535150646 + -0.00939175413426 + -0.00936815676206 + -0.00934455938987 + -0.00932096201767 + -0.00929736464547 + -0.00927376727328 + -0.00925016990108 + -0.00922657252888 + -0.00920297515669 + -0.00917937778449 + -0.00915578041229 + -0.0091321830401 + -0.0091085856679 + -0.0090849882957 + -0.00906139092351 + -0.00903779355131 + -0.00901419617911 + -0.00899059880692 + -0.00896700143472 + -0.00894340406252 + -0.00891980669033 + -0.00889620931813 + -0.00887261194593 + -0.00884901457374 + -0.00882541720154 + -0.00880181982934 + -0.00877822245715 + -0.00875462508495 + -0.00873102771275 + -0.00870743034056 + -0.00868383296836 + -0.00866023559616 + -0.00863663822397 + -0.00861304085177 + -0.00858944347957 + -0.00856584610738 + -0.00854224873518 + -0.00851865136298 + -0.00849505399079 + -0.00847145661859 + -0.00844785924639 + -0.0084242618742 + -0.008400664502 + -0.0083770671298 + -0.00835346975761 + -0.00832987238541 + -0.00830627501321 + -0.00828267764102 + -0.00825908026882 + -0.00823548289662 + -0.00821188552443 + -0.00818828815223 + -0.00816469078003 + -0.00814109340784 + -0.00811749603564 + -0.00809389866344 + -0.00807030129125 + -0.00804670391905 + -0.00802310654685 + -0.00799950917466 + -0.00797591180246 + -0.00795231443027 + -0.00792871705807 + -0.00790511968587 + -0.00788152231368 + -0.00785792494148 + -0.00783432756928 + -0.00781073019709 + -0.00778713282489 + -0.00776353545269 + -0.0077399380805 + -0.0077163407083 + -0.0076927433361 + -0.00766914596391 + -0.00764554859171 + -0.00762195121951 + -0.00759835384732 + -0.00757475647512 + -0.00755115910292 + -0.00752756173073 + -0.00750396435853 + -0.00748036698633 + -0.00745676961414 + -0.00743317224194 + -0.00740957486974 + -0.00738597749755 + -0.00736238012535 + -0.00733878275315 + -0.00731518538096 + -0.00729158800876 + -0.00726799063656 + -0.00724439326437 + -0.00722079589217 + -0.00719719851997 + -0.00717360114778 + -0.00715000377558 + -0.00712640640338 + -0.00710280903119 + -0.00707921165899 + -0.00705561428679 + -0.0070320169146 + -0.0070084195424 + -0.0069848221702 + -0.00696122479801 + -0.00693762742581 + -0.00691403005361 + -0.00689043268142 + -0.00686683530922 + -0.00684323793702 + -0.00681964056483 + -0.00679604319263 + -0.00677244582043 + -0.00674884844824 + -0.00672525107604 + -0.00670165370384 + -0.00667805633165 + -0.00665445895945 + -0.00663086158725 + -0.00660726421506 + -0.00658366684286 + -0.00656006947066 + -0.00653647209847 + -0.00651287472627 + -0.00648927735407 + -0.00646567998188 + -0.00644208260968 + -0.00641848523748 + -0.00639488786529 + -0.00637129049309 + -0.00634769312089 + -0.0063240957487 + -0.0063004983765 + -0.0062769010043 + -0.00625330363211 + -0.00622970625991 + -0.00620610888771 + -0.00618251151552 + -0.00615891414332 + -0.00613531677112 + -0.00611171939893 + -0.00608812202673 + -0.00606452465453 + -0.00604092728234 + -0.00601732991014 + -0.00599373253794 + -0.00597013516575 + -0.00594653779355 + -0.00592294042135 + -0.00589934304916 + -0.00587574567696 + -0.00585214830476 + -0.00582855093257 + -0.00580495356037 + -0.00578135618817 + -0.00575775881598 + -0.00573416144378 + -0.00571056407158 + -0.00568696669939 + -0.00566336932719 + -0.005639771955 + -0.0056161745828 + -0.0055925772106 + -0.00556897983841 + -0.00554538246621 + -0.00552178509401 + -0.00549818772182 + -0.00547459034962 + -0.00545099297742 + -0.00542739560523 + -0.00540379823303 + -0.00538020086083 + -0.00535660348864 + -0.00533300611644 + -0.00530940874424 + -0.00528581137205 + -0.00526221399985 + -0.00523861662765 + -0.00521501925546 + -0.00519142188326 + -0.00516782451106 + -0.00514422713887 + -0.00512062976667 + -0.00509703239447 + -0.00507343502228 + -0.00504983765008 + -0.00502624027788 + -0.00500264290569 + -0.00497904553349 + -0.00495544816129 + -0.0049318507891 + -0.0049082534169 + -0.0048846560447 + -0.00486105867251 + -0.00483746130031 + -0.00481386392811 + -0.00479026655592 + -0.00476666918372 + -0.00474307181152 + -0.00471947443933 + -0.00469587706713 + -0.00467227969493 + -0.00464868232274 + -0.00462508495054 + -0.00460148757834 + -0.00457789020615 + -0.00455429283395 + -0.00453069546175 + -0.00450709808956 + -0.00448350071736 + -0.00445990334516 + -0.00443630597297 + -0.00441270860077 + -0.00438911122857 + -0.00436551385638 + -0.00434191648418 + -0.00431831911198 + -0.00429472173979 + -0.00427112436759 + -0.00424752699539 + -0.0042239296232 + -0.004200332251 + -0.0041767348788 + -0.00415313750661 + -0.00412954013441 + -0.00410594276221 + -0.00408234539002 + -0.00405874801782 + -0.00403515064562 + -0.00401155327343 + -0.00398795590123 + -0.00396435852903 + -0.00394076115684 + -0.00391716378464 + -0.00389356641244 + -0.00386996904025 + -0.00384637166805 + -0.00382277429585 + -0.00379917692366 + -0.00377557955146 + -0.00375198217926 + -0.00372838480707 + -0.00370478743487 + -0.00368119006267 + -0.00365759269048 + -0.00363399531828 + -0.00361039794608 + -0.00358680057389 + -0.00356320320169 + -0.00353960582949 + -0.0035160084573 + -0.0034924110851 + -0.0034688137129 + -0.00344521634071 + -0.00342161896851 + -0.00339802159632 + -0.00337442422412 + -0.00335082685192 + -0.00332722947973 + -0.00330363210753 + -0.00328003473533 + -0.00325643736314 + -0.00323283999094 + -0.00320924261874 + -0.00318564524655 + -0.00316204787435 + -0.00313845050215 + -0.00311485312996 + -0.00309125575776 + -0.00306765838556 + -0.00304406101337 + -0.00302046364117 + -0.00299686626897 + -0.00297326889678 + -0.00294967152458 + -0.00292607415238 + -0.00290247678019 + -0.00287887940799 + -0.00285528203579 + -0.0028316846636 + -0.0028080872914 + -0.0027844899192 + -0.00276089254701 + -0.00273729517481 + -0.00271369780261 + -0.00269010043042 + -0.00266650305822 + -0.00264290568602 + -0.00261930831383 + -0.00259571094163 + -0.00257211356943 + -0.00254851619724 + -0.00252491882504 + -0.00250132145284 + -0.00247772408065 + -0.00245412670845 + -0.00243052933625 + -0.00240693196406 + -0.00238333459186 + -0.00235973721966 + -0.00233613984747 + -0.00231254247527 + -0.00228894510307 + -0.00226534773088 + -0.00224175035868 + -0.00221815298648 + -0.00219455561429 + -0.00217095824209 + -0.00214736086989 + -0.0021237634977 + -0.0021001661255 + -0.0020765687533 + -0.00205297138111 + -0.00202937400891 + -0.00200577663671 + -0.00198217926452 + -0.00195858189232 + -0.00193498452012 + -0.00191138714793 + -0.00188778977573 + -0.00186419240353 + -0.00184059503134 + -0.00181699765914 + -0.00179340028694 + -0.00176980291475 + -0.00174620554255 + -0.00172260817035 + -0.00169901079816 + -0.00167541342596 + -0.00165181605376 + -0.00162821868157 + -0.00160462130937 + -0.00158102393717 + -0.00155742656498 + -0.00153382919278 + -0.00151023182058 + -0.00148663444839 + -0.00146303707619 + -0.00143943970399 + -0.0014158423318 + -0.0013922449596 + -0.0013686475874 + -0.00134505021521 + -0.00132145284301 + -0.00129785547081 + -0.00127425809862 + -0.00125066072642 + -0.00122706335422 + -0.00120346598203 + -0.00117986860983 + -0.00115627123763 + -0.00113267386544 + -0.00110907649324 + -0.00108547912105 + -0.00106188174885 + -0.00103828437665 + -0.00101468700446 + -0.000991089632259 + -0.000967492260062 + -0.000943894887865 + -0.000920297515669 + -0.000896700143472 + -0.000873102771275 + -0.000849505399079 + -0.000825908026882 + -0.000802310654685 + -0.000778713282489 + -0.000755115910292 + -0.000731518538096 + -0.000707921165899 + -0.000684323793702 + -0.000660726421506 + -0.000637129049309 + -0.000613531677112 + -0.000589934304916 + -0.000566336932719 + -0.000542739560523 + -0.000519142188326 + -0.000495544816129 + -0.000471947443933 + -0.000448350071736 + -0.000424752699539 + -0.000401155327343 + -0.000377557955146 + -0.000353960582949 + -0.000330363210753 + -0.000306765838556 + -0.00028316846636 + -0.000259571094163 + -0.000235973721966 + -0.00021237634977 + -0.000188778977573 + -0.000165181605376 + -0.00014158423318 + -0.000117986860983 + -9.43894887865e-05 + -7.07921165899e-05 + -4.71947443933e-05 + -2.35973721966e-05 + 0.0 + 2.35973721966e-05 + 4.71947443933e-05 + 7.07921165899e-05 + 9.43894887865e-05 + 0.000117986860983 + 0.00014158423318 + 0.000165181605376 + 0.000188778977573 + 0.00021237634977 + 0.000235973721966 + 0.000259571094163 + 0.00028316846636 + 0.000306765838556 + 0.000330363210753 + 0.000353960582949 + 0.000377557955146 + 0.000401155327343 + 0.000424752699539 + 0.000448350071736 + 0.000471947443933 + 0.000495544816129 + 0.000519142188326 + 0.000542739560523 + 0.000566336932719 + 0.000589934304916 + 0.000613531677112 + 0.000637129049309 + 0.000660726421506 + 0.000684323793702 + 0.000707921165899 + 0.000731518538096 + 0.000755115910292 + 0.000778713282489 + 0.000802310654685 + 0.000825908026882 + 0.000849505399079 + 0.000873102771275 + 0.000896700143472 + 0.000920297515669 + 0.000943894887865 + 0.000967492260062 + 0.000991089632259 + 0.00101468700446 + 0.00103828437665 + 0.00106188174885 + 0.00108547912105 + 0.00110907649324 + 0.00113267386544 + 0.00115627123763 + 0.00117986860983 + 0.00120346598203 + 0.00122706335422 + 0.00125066072642 + 0.00127425809862 + 0.00129785547081 + 0.00132145284301 + 0.00134505021521 + 0.0013686475874 + 0.0013922449596 + 0.0014158423318 + 0.00143943970399 + 0.00146303707619 + 0.00148663444839 + 0.00151023182058 + 0.00153382919278 + 0.00155742656498 + 0.00158102393717 + 0.00160462130937 + 0.00162821868157 + 0.00165181605376 + 0.00167541342596 + 0.00169901079816 + 0.00172260817035 + 0.00174620554255 + 0.00176980291475 + 0.00179340028694 + 0.00181699765914 + 0.00184059503134 + 0.00186419240353 + 0.00188778977573 + 0.00191138714793 + 0.00193498452012 + 0.00195858189232 + 0.00198217926452 + 0.00200577663671 + 0.00202937400891 + 0.00205297138111 + 0.0020765687533 + 0.0021001661255 + 0.0021237634977 + 0.00214736086989 + 0.00217095824209 + 0.00219455561429 + 0.00221815298648 + 0.00224175035868 + 0.00226534773088 + 0.00228894510307 + 0.00231254247527 + 0.00233613984747 + 0.00235973721966 + 0.00238333459186 + 0.00240693196406 + 0.00243052933625 + 0.00245412670845 + 0.00247772408065 + 0.00250132145284 + 0.00252491882504 + 0.00254851619724 + 0.00257211356943 + 0.00259571094163 + 0.00261930831383 + 0.00264290568602 + 0.00266650305822 + 0.00269010043042 + 0.00271369780261 + 0.00273729517481 + 0.00276089254701 + 0.0027844899192 + 0.0028080872914 + 0.0028316846636 + 0.00285528203579 + 0.00287887940799 + 0.00290247678019 + 0.00292607415238 + 0.00294967152458 + 0.00297326889678 + 0.00299686626897 + 0.00302046364117 + 0.00304406101337 + 0.00306765838556 + 0.00309125575776 + 0.00311485312996 + 0.0031385890345 + 0.00316267788401 + 0.00318687423862 + 0.00321117823498 + 0.00323559000948 + 0.00326010969823 + 0.00328473743711 + 0.00330947336173 + 0.00333431760746 + 0.00335927030939 + 0.00338433160238 + 0.00340950162104 + 0.00343478049972 + 0.00346016837252 + 0.0034856653733 + 0.00351127163568 + 0.00353698729303 + 0.00356281247846 + 0.00358874732486 + 0.00361479196487 + 0.0036409465309 + 0.00366721115511 + 0.00369358596941 + 0.0037200711055 + 0.00374666669484 + 0.00377337286863 + 0.00380018975787 + 0.00382711749331 + 0.00385415620548 + 0.00388130602466 + 0.00390856708091 + 0.00393593950409 + 0.00396342342379 + 0.0039910189694 + 0.00401872627008 + 0.00404654545476 + 0.00407447665216 + 0.00410251999077 + 0.00413067559886 + 0.00415894360449 + 0.00418732413548 + 0.00421581731946 + 0.00424442328382 + 0.00427314215575 + 0.00430197406221 + 0.00433091912997 + 0.00435997748556 + 0.00438914925532 + 0.00441843456538 + 0.00444783354163 + 0.00447734630978 + 0.00450697299533 + 0.00453671372356 + 0.00456656861956 + 0.00459653780821 + 0.00462662141416 + 0.0046568195619 + 0.00468713237568 + 0.00471755997958 + 0.00474810249745 + 0.00477876005296 + 0.00480953276957 + 0.00484042077055 + 0.00487142417896 + 0.00490254311768 + 0.00493377770939 + 0.00496512807657 + 0.0049965943415 + 0.00502817662628 + 0.0050598750528 + 0.00509168974279 + 0.00512362081776 + 0.00515566839903 + 0.00518783260776 + 0.00522011356488 + 0.00525251139117 + 0.0052850262072 + 0.00531765813336 + 0.00535040728986 + 0.00538327379672 + 0.00541625777377 + 0.00544935934067 + 0.0054825786169 + 0.00551591572174 + 0.00554937077429 + 0.0055829438935 + 0.00561663519811 + 0.00565044480668 + 0.00568437283762 + 0.00571841940914 + 0.00575258463927 + 0.00578686864589 + 0.00582127154667 + 0.00585579345914 + 0.00589043450063 + 0.00592519478831 + 0.00596007443917 + 0.00599507357003 + 0.00603019229756 + 0.00606543073822 + 0.00610078900832 + 0.00613626722402 + 0.00617186550129 + 0.00620758395592 + 0.00624342270356 + 0.00627938185968 + 0.00631546153959 + 0.00635166185843 + 0.00638798293118 + 0.00642442487264 + 0.00646098779746 + 0.00649767182014 + 0.006534477055 + 0.0065714036162 + 0.00660845161773 + 0.00664562117345 + 0.00668291239704 + 0.00672032540201 + 0.00675786030174 + 0.00679551720942 + 0.00683329623811 + 0.0068711975007 + 0.00690922110993 + 0.00694736717837 + 0.00698563581845 + 0.00702402714244 + 0.00706254126247 + 0.00710117829048 + 0.0071399383383 + 0.00717882151758 + 0.00721782793983 + 0.0072569577164 + 0.00729621095851 + 0.0073355877772 + 0.00737508828338 + 0.00741471258781 + 0.00745446080109 + 0.00749433303369 + 0.00753432939592 + 0.00757444999795 + 0.00761469494979 + 0.00765506436132 + 0.00769555834227 + 0.00773617700222 + 0.00777692045062 + 0.00781778879675 + 0.00785878214977 + 0.00789990061869 + 0.00794114431238 + 0.00798251333957 + 0.00802400780883 + 0.00806562782861 + 0.00810737350722 + 0.00814924495281 + 0.00819124227342 + 0.00823336557692 + 0.00827561497107 + 0.00831799056348 + 0.0083604924616 + 0.00840312077279 + 0.00844587560423 + 0.008488757063 + 0.00853176525601 + 0.00857490029005 + 0.00861816227179 + 0.00866155130774 + 0.0087050675043 + 0.00874871096771 + 0.0087924818041 + 0.00883638011947 + 0.00888040601966 + 0.00892455961041 + 0.0089688409973 + 0.00901325028581 + 0.00905778758127 + 0.00910245298887 + 0.0091472466137 + 0.00919216856071 + 0.0092372189347 + 0.00928239784036 + 0.00932770538227 + 0.00937314166485 + 0.0094187067924 + 0.00946440086912 + 0.00951022399905 + 0.00955617628612 + 0.00960225783414 + 0.00964846874678 + 0.0096948091276 + 0.00974127908002 + 0.00978787870736 + 0.0098346081128 + 0.00988146739939 + 0.00992845667008 + 0.00997557602767 + 0.0100228255749 + 0.0100702054142 + 0.0101177156482 + 0.0101653563792 + 0.0102131277093 + 0.0102610297407 + 0.0103090625753 + 0.010357226315 + 0.0104055210615 + 0.0104539469164 + 0.0105025039813 + 0.0105511923574 + 0.0106000121461 + 0.0106489634486 + 0.0106980463657 + 0.0107472609986 + 0.0107966074478 + 0.0108460858142 + 0.0108956961983 + 0.0109454387006 + 0.0109953134213 + 0.0110453204607 + 0.011095459919 + 0.011145731896 + 0.0111961364918 + 0.011246673806 + 0.0112973439383 + 0.0113481469883 + 0.0113990830554 + 0.0114501522389 + 0.011501354638 + 0.0115526903519 + 0.0116041594794 + 0.0116557621196 + 0.0117074983712 + 0.0117593683328 + 0.0118113721029 + 0.0118635097801 + 0.0119157814627 + 0.0119681872489 + 0.0120207272367 + 0.0120734015243 + 0.0121262102095 + 0.0121791533901 + 0.0122322311638 + 0.0122854436282 + 0.0123387908808 + 0.0123922730189 + 0.0124458901399 + 0.0124996423408 + 0.0125535297187 + 0.0126075523707 + 0.0126617103934 + 0.0127160038838 + 0.0127704329384 + 0.0128249976537 + 0.0128796981262 + 0.0129345344523 + 0.0129895067281 + 0.0130446150498 + 0.0130998595134 + 0.0131552402149 + 0.01321075725 + 0.0132664107145 + 0.013322200704 + 0.0133781273141 + 0.0134341906401 + 0.0134903907774 + 0.0135467278211 + 0.0136032018666 + 0.0136598130086 + 0.0137165613422 + 0.0137734469623 + 0.0138304699634 + 0.0138876304403 + 0.0139449284876 + 0.0140023641995 + 0.0140599376705 + 0.0141176489949 + 0.0141754982666 + 0.0142334855799 + 0.0142916110287 + 0.0143498747068 + 0.014408276708 + 0.0144668171259 + 0.0145254960541 + 0.0145843135862 + 0.0146432698154 + 0.0147023648351 + 0.0147615987385 + 0.0148209716187 + 0.0148804835686 + 0.0149401346812 + 0.0149999250493 + 0.0150598547657 + 0.0151199239229 + 0.0151801326136 + 0.0152404809302 + 0.0153009689649 + 0.0153615968102 + 0.0154223645582 + 0.0154832723009 + 0.0155443201304 + 0.0156055081386 + 0.0156668364173 + 0.0157283050581 + 0.0157899141528 + 0.015851663793 + 0.0159135540699 + 0.0159755850751 + 0.0160377568997 + 0.0161000696351 + 0.0161625233722 + 0.0162251182021 + 0.0162878542158 + 0.016350731504 + 0.0164137501575 + 0.016476910267 + 0.016540211923 + 0.0166036552161 + 0.0166672402366 + 0.0167309670748 + 0.0167948358211 + 0.0168588465654 + 0.016922999398 + 0.0169872944087 + 0.0170517316874 + 0.017116311324 + 0.0171810334081 + 0.0172458980295 + 0.0173109052775 + 0.0173760552418 + 0.0174413480117 + 0.0175067836764 + 0.0175723623252 + 0.0176380840473 + 0.0177039489316 + 0.0177699570671 + 0.0178361085427 + 0.0179024034472 + 0.0179688418694 + 0.0180354238978 + 0.0181021496209 + 0.0181690191274 + 0.0182360325055 + 0.0183031898435 + 0.0183704912298 + 0.0184379367523 + 0.0185055264993 + 0.0185732605586 + 0.0186411390182 + 0.0187091619659 + 0.0187773294894 + 0.0188456416764 + 0.0189140986145 + 0.0189827003912 + 0.0190514470939 + 0.0191203388099 + 0.0191893756265 + 0.019258557631 + 0.0193278849103 + 0.0193973575516 + 0.0194669756418 + 0.0195367392677 + 0.0196066485162 + 0.019676703474 + 0.0197469042277 + 0.0198172508639 + 0.0198877434691 + 0.0199583821296 + 0.0200291669319 + 0.0201000979621 + 0.0201711753065 + 0.0202423990512 + 0.0203137692821 + 0.0203852860853 + 0.0204569495466 + 0.0205287597519 + 0.0206007167868 + 0.020672820737 + 0.020745071688 + 0.0208174697255 + 0.0208900149348 + 0.0209627074012 + 0.0210355472101 + 0.0211085344467 + 0.021181669196 + 0.0212549515432 + 0.0213283815732 + 0.021401959371 + 0.0214756850213 + 0.021549558609 + 0.0216235802187 + 0.0216977499351 + 0.0217720678426 + 0.0218465340259 + 0.0219211485693 + 0.021995911557 + 0.0220708230735 + 0.0221458832028 + 0.0222210920291 + 0.0222964496364 + 0.0223719561088 + 0.02244761153 + 0.0225234159839 + 0.0225993695544 + 0.0226754723251 + 0.0227517243795 + 0.0228281258014 + 0.022904676674 + 0.0229813770809 + 0.0230582271055 + 0.0231352268308 + 0.0232123763403 + 0.0232896757169 + 0.0233671250438 + 0.023444724404 + 0.0235224738803 + 0.0236003735557 + 0.0236784235129 + 0.0237566238346 + 0.0238349746036 + 0.0239134759023 + 0.0239921278133 + 0.0240709304191 + 0.024149883802 + 0.0242289880444 + 0.0243082432284 + 0.0243876494363 + 0.0244672067502 + 0.0245469152521 + 0.024626775024 + 0.0247067861478 + 0.0247869487054 + 0.0248672627785 + 0.0249477284489 + 0.0250283457981 + 0.0251091149078 + 0.0251900358596 + 0.0252711087347 + 0.0253523336147 + 0.0254337105808 + 0.0255152397143 + 0.0255969210965 + 0.0256787548083 + 0.0257607409309 + 0.0258428795452 + 0.0259251707322 + 0.0260076145728 + 0.0260902111478 + 0.0261729605378 + 0.0262558628236 + 0.0263389180858 + 0.0264221264049 + 0.0265054878614 + 0.0265890025358 + 0.0266726705083 + 0.0267564918593 + 0.026840466669 + 0.0269245950175 + 0.0270088769851 + 0.0270933126516 + 0.0271779020971 + 0.0272626454016 + 0.0273475426448 + 0.0274325939065 + 0.0275177992666 + 0.0276031588046 + 0.0276886726001 + 0.0277743407328 + 0.027860163282 + 0.0279461403273 + 0.0280322719479 + 0.0281185582232 + 0.0282049992324 + 0.0282915950547 + 0.0283783457692 + 0.0284652514549 + 0.0285523121909 + 0.0286395280562 + 0.0287268991294 + 0.0288144254896 + 0.0289021072154 + 0.0289899443855 + 0.0290779370787 + 0.0291660853733 + 0.0292543893481 + 0.0293428490813 + 0.0294314646515 + 0.029520236137 + 0.029609163616 + 0.0296982471668 + 0.0297874868674 + 0.0298768827962 + 0.029966435031 + 0.0300561436499 + 0.0301460087307 + 0.0302360303515 + 0.0303262085899 + 0.0304165435238 + 0.0305070352307 + 0.0305976837885 + 0.0306884892746 + 0.0307794517666 + 0.0308705713419 + 0.030961848078 + 0.0310532820522 + 0.0311448733418 + 0.031236622024 + 0.0313285281761 + 0.0314205918751 + 0.0315128131981 + 0.0316051922222 + 0.0316977290242 + 0.0317904236811 + 0.0318832762697 + 0.0319762868669 + 0.0320694555492 + 0.0321627823935 + 0.0322562674764 + 0.0323499108743 + 0.0324437126639 + 0.0325376729215 + 0.0326317917235 + 0.0327260691464 + 0.0328205052664 + 0.0329151001597 + 0.0330098539025 + 0.0331047665709 + 0.033199838241 + 0.0332950689888 + 0.0333904588903 + 0.0334860080213 + 0.0335817164578 + 0.0336775842755 + 0.0337736115501 + 0.0338697983574 + 0.033966144773 + 0.0340626508725 + 0.0341593167313 + 0.034256142425 + 0.034353128029 + 0.0344502736186 + 0.0345475792692 + 0.0346450450561 + 0.0347426710544 + 0.0348404573392 + 0.0349384039858 + 0.0350365110691 + 0.0351347786641 + 0.0352332068458 + 0.0353317956891 + 0.0354305452689 + 0.0355294556598 + 0.0356285269366 + 0.0357277591741 + 0.0358271524468 + 0.0359267068294 + 0.0360264223963 + 0.0361262992221 + 0.0362263373811 + 0.0363265369477 + 0.0364268979963 + 0.0365274206011 + 0.0366281048364 + 0.0367289507762 + 0.0368299584948 + 0.0369311280662 + 0.0370324595644 + 0.0371339530634 + 0.037235608637 + 0.0373374263592 + 0.0374394063038 + 0.0375415485445 + 0.037643853155 + 0.037746320209 + 0.0378489497802 + 0.037951741942 + 0.038054696768 + 0.0381578143316 + 0.0382610947063 + 0.0383645379655 + 0.0384681441823 + 0.0385719134302 + 0.0386758457823 + 0.0387799413118 + 0.0388842000918 + 0.0389886221953 + 0.0390932076954 + 0.0391979566651 + 0.0393028691773 + 0.0394079453048 + 0.0395131851204 + 0.039618588697 + 0.0397241561073 + 0.039829887424 + 0.0399357827196 + 0.0400418420667 + 0.040148065538 + 0.0402544532058 + 0.0403610051427 + 0.0404677214209 + 0.0405746021129 + 0.040681647291 + 0.0407888570273 + 0.0408962313941 + 0.0410037704635 + 0.0411114743077 + 0.0412193429986 + 0.0413273766084 + 0.0414355752089 + 0.0415439388721 + 0.0416524676699 + 0.041761161674 + 0.0418700209563 + 0.0419790455884 + 0.0420882356421 + 0.0421975911889 + 0.0423071123006 + 0.0424167990485 + 0.0425266515043 + 0.0426366697393 + 0.042746853825 + 0.0428572038327 + 0.0429677198337 + 0.0430784018994 + 0.0431892501008 + 0.0433002645092 + 0.0434114451957 + 0.0435227922315 + 0.0436343056874 + 0.0437459856346 + 0.0438578321439 + 0.0439698452863 + 0.0440820251327 + 0.0441943717537 + 0.0443068852203 + 0.0444195656031 + 0.0445324129727 + 0.0446454273999 + 0.0447586089552 + 0.0448719577092 + 0.0449854737323 + 0.0450991570949 + 0.0452130078676 + 0.0453270261206 + 0.0454412119244 + 0.0455555653491 + 0.0456700864649 + 0.0457847753421 + 0.0458996320509 + 0.0460146566613 + 0.0461298492433 + 0.046245209867 + 0.0463607386023 + 0.0464764355192 + 0.0465923006876 + 0.0467083341772 + 0.0468245360579 + 0.0469409063994 + 0.0470574452714 + 0.0471741527436 + 0.0472910288857 + 0.0474080737671 + 0.0475252874574 + 0.0476426700262 + 0.0477602215427 + 0.0478779420766 + 0.0479958316971 + 0.0481138904735 + 0.0482321184751 + 0.0483505157711 + 0.0484690824308 + 0.0485878185233 + 0.0487067241177 + 0.0488257992831 + 0.0489450440884 + 0.0490644586027 + 0.0491840428949 + 0.0493037970339 + 0.0494237210886 + 0.0495438151277 + 0.0496640792201 + 0.0497845134344 + 0.0499051178394 + 0.0500258925036 + 0.0501468374958 + 0.0502679528845 + 0.0503892387381 + 0.0505106951252 + 0.0506323221142 + 0.0507541197736 + 0.0508760881716 + 0.0509982273765 + 0.0511205374567 + 0.0512430184805 + 0.0513656705159 + 0.0514884936311 + 0.0516114878943 + 0.0517346533735 + 0.0518579901367 + 0.051981498252 + 0.0521051777872 + 0.0522290288104 + 0.0523530513893 + 0.0524772455919 + 0.0526016114858 + 0.0527261491388 + 0.0528508586187 + 0.0529757399931 + 0.0531007933296 + 0.0532260186959 + 0.0533514161594 + 0.0534769857877 + 0.0536027276483 + 0.0537286418086 + 0.0538547283359 + 0.0539809872976 + 0.0541074187611 + 0.0542340227936 + 0.0543607994623 + 0.0544877488345 + 0.0546148709772 + 0.0547421659577 + 0.0548696338429 + 0.0549972746999 + 0.0551250885958 + 0.0552530755974 + 0.0553812357717 + 0.0555095691856 + 0.0556380759059 + 0.0557667559994 + 0.0558956095329 + 0.0560246365731 + 0.0561538371867 + 0.0562832114404 + 0.0564127594007 + 0.0565424811343 + 0.0566723767076 + 0.0568024461872 + 0.0569326896395 + 0.057063107131 + 0.057193698728 + 0.0573244644969 + 0.0574554045039 + 0.0575865188155 + 0.0577178074976 + 0.0578492706167 + 0.0579809082388 + 0.0581127204301 + 0.0582447072565 + 0.0583768687843 + 0.0585092050793 + 0.0586417162076 + 0.0587744022351 + 0.0589072632276 + 0.0590402992511 + 0.0591735103713 + 0.0593068966541 + 0.0594404581651 + 0.0595741949702 + 0.0597081071348 + 0.0598421947248 + 0.0599764578057 + 0.060110896443 + 0.0602455107023 + 0.0603803006491 + 0.0605152663488 + 0.0606504078668 + 0.0607857252685 + 0.0609212186193 + 0.0610568879844 + 0.0611927334292 + 0.0613287550188 + 0.0614649528185 + 0.0616013268934 + 0.0617378773086 + 0.0618746041293 + 0.0620115074204 + 0.062148587247 + 0.0622858436742 + 0.0624232767667 + 0.0625608865896 + 0.0626986732076 + 0.0628366366858 + 0.0629747770887 + 0.0631130944813 + 0.0632515889282 + 0.0633902604941 + 0.0635291092438 + 0.0636681352417 + 0.0638073385526 + 0.0639467192409 + 0.0640862773712 + 0.064226013008 + 0.0643659262156 + 0.0645060170586 + 0.0646462856013 + 0.0647867319081 + 0.0649273560432 + 0.0650681580709 + 0.0652091380555 + 0.0653502960612 + 0.0654916321521 + 0.0656331463924 + 0.0657748388461 + 0.0659167095774 + 0.0660587586503 + 0.0662009861287 + 0.0663433920767 + 0.0664859765581 + 0.0666287396369 + 0.0667716813768 + 0.0669148018418 + 0.0670581010956 + 0.067201579202 + 0.0673452362247 + 0.0674890722274 + 0.0676330872737 + 0.0677772814273 + 0.0679216547518 + 0.0680662073106 + 0.0682109391674 + 0.0683558503856 + 0.0685009410286 + 0.0686462111599 + 0.0687916608429 + 0.0689372901409 + 0.0690830991172 + 0.0692290878351 + 0.0693752563579 + 0.0695216047488 + 0.0696681330709 + 0.0698148413874 + 0.0699617297615 + 0.0701087982562 + 0.0702560469345 + 0.0704034758596 + 0.0705510850943 + 0.0706988747016 + 0.0708468447445 + 0.0709949952858 + 0.0711433263883 + 0.071291838115 + 0.0714405305285 + 0.0715894036917 + 0.0717384576673 + 0.0718876925179 + 0.0720371083062 + 0.0721867050949 + 0.0723364829464 + 0.0724864419235 + 0.0726365820886 + 0.0727869035042 + 0.0729374062327 + 0.0730880903367 + 0.0732389558784 + 0.0733900029203 + 0.0735412315247 + 0.0736926417539 + 0.0738442336702 + 0.0739960073358 + 0.0741479628129 + 0.0743001001637 + 0.0744524194503 + 0.0746049207348 + 0.0747576040794 + 0.074910469546 + 0.0750635171967 + 0.0752167470935 + 0.0753701592983 + 0.075523753873 + 0.0756775308795 + 0.0758314903797 + 0.0759856324354 + 0.0761399571085 + 0.0762944644605 + 0.0764491545534 + 0.0766040274487 + 0.0767590832083 + 0.0769143218936 + 0.0770697435663 + 0.077225348288 + 0.0773811361202 + 0.0775371071245 + 0.0776932613623 + 0.077849598895 + 0.0780061197842 + 0.0781628240911 + 0.0783197118772 + 0.0784767832038 + 0.0786340381321 + 0.0787914767235 + 0.0789490990391 + 0.0791069051402 + 0.079264895088 + 0.0794230689436 + 0.0795814267681 + 0.0797399686225 + 0.0798986945681 + 0.0800576046657 + 0.0802166989763 + 0.080375977561 + 0.0805354404807 + 0.0806950877962 + 0.0808549195685 + 0.0810149358583 + 0.0811751367265 + 0.0813355222339 + 0.0814960924412 + 0.0816568474092 + 0.0818177871984 + 0.0819789118697 + 0.0821402214836 + 0.0823017161008 + 0.0824633957817 + 0.082625260587 + 0.0827873105771 + 0.0829495458126 + 0.0831119663539 + 0.0832745722613 + 0.0834373635955 + 0.0836003404165 + 0.0837635027849 + 0.083926850761 + 0.0840903844049 + 0.0842541037769 + 0.0844180089374 + 0.0845820999463 + 0.084746376864 + 0.0849108397506 + 0.085075488666 + 0.0852403236705 + 0.0854053448241 + 0.0855705521867 + 0.0857359458184 + 0.0859015257791 + 0.0860672921287 + 0.0862332449272 + 0.0863993842344 + 0.0865657101102 + 0.0867322226143 + 0.0868989218065 + 0.0870658077467 + 0.0872328804945 + 0.0874001401096 + 0.0875675866517 + 0.0877352201804 + 0.0879030407554 + 0.0880710484362 + 0.0882392432824 + 0.0884076253535 + 0.088576194709 + 0.0887449514084 + 0.0889138955111 + 0.0890830270765 + 0.0892523461641 + 0.0894218528331 + 0.0895915471429 + 0.0897614291528 + 0.0899314989221 + 0.09010175651 + 0.0902722019757 + 0.0904428353785 + 0.0906136567774 + 0.0907846662317 + 0.0909558638004 + 0.0911272495425 + 0.0912988235173 + 0.0914705857835 + 0.0916425364004 + 0.0918146754267 + 0.0919870029215 + 0.0921595189437 + 0.0923322235521 + 0.0925051168057 + 0.0926781987632 + 0.0928514694834 + 0.0930249290251 + 0.0931985774471 + 0.093372414808 + 0.0935464411666 + 0.0937206565816 + 0.0938950611115 + 0.0940696548149 + 0.0942444377505 + 0.0944194099769 + 0.0945945715524 + 0.0947699225357 + 0.0949454629852 + 0.0951211929593 + 0.0952971125165 + 0.0954732217152 + 0.0956495206136 + 0.0958260092703 + 0.0960026877434 + 0.0961795560913 + 0.0963566143722 + 0.0965338626443 + 0.0967113009659 + 0.0968889293951 + 0.0970667479901 + 0.097244756809 + 0.0974229559098 + 0.0976013453508 + 0.0977799251898 + 0.097958695485 + 0.0981376562943 + 0.0983168076757 + 0.0984961496871 + 0.0986756823864 + 0.0988554058316 + 0.0990353200804 + 0.0992154251908 + 0.0993957212204 + 0.0995762082271 + 0.0997568862687 + 0.0999377554029 + 0.100118815687 + 0.10030006718 + 0.100481509938 + 0.100663144019 + 0.100844969481 + 0.101026986381 + 0.101209194778 + 0.101391594728 + 0.101574186288 + 0.101756969518 + 0.101939944473 + 0.102123111211 + 0.10230646979 + 0.102490020267 + 0.102673762699 + 0.102857697144 + 0.10304182366 + 0.103226142302 + 0.103410653129 + 0.103595356197 + 0.103780251564 + 0.103965339288 + 0.104150619424 + 0.104336092031 + 0.104521757165 + 0.104707614883 + 0.104893665243 + 0.105079908301 + 0.105266344115 + 0.105452972741 + 0.105639794237 + 0.105826808659 + 0.106014016063 + 0.106201416508 + 0.10638901005 + 0.106576796745 + 0.10676477665 + 0.106952949823 + 0.10714131632 + 0.107329876197 + 0.107518629511 + 0.107707576319 + 0.107896716678 + 0.108086050644 + 0.108275578273 + 0.108465299623 + 0.10865521475 + 0.10884532371 + 0.10903562656 + 0.109226123356 + 0.109416814155 + 0.109607699013 + 0.109798777986 + 0.109990051131 + 0.110181518505 + 0.110373180162 + 0.110565036161 + 0.110757086557 + 0.110949331406 + 0.111141770764 + 0.111334404688 + 0.111527233234 + 0.111720256458 + 0.111913474416 + 0.112106887164 + 0.112300494759 + 0.112494297255 + 0.11268829471 + 0.11288248718 + 0.113076874719 + 0.113271457385 + 0.113466235233 + 0.113661208319 + 0.113856376699 + 0.114051740429 + 0.114247299564 + 0.114443054161 + 0.114639004275 + 0.114835149961 + 0.115031491277 + 0.115228028277 + 0.115424761016 + 0.115621689552 + 0.115818813939 + 0.116016134233 + 0.116213650489 + 0.116411362764 + 0.116609271112 + 0.116807375589 + 0.117005676251 + 0.117204173153 + 0.117402866351 + 0.117601755899 + 0.117800841855 + 0.118000124271 + 0.118199603205 + 0.118399278712 + 0.118599150846 + 0.118799219663 + 0.118999485219 + 0.119199947568 + 0.119400606766 + 0.119601462868 + 0.119802515928 + 0.120003766004 + 0.120205213148 + 0.120406857417 + 0.120608698866 + 0.120810737549 + 0.121012973522 + 0.121215406839 + 0.121418037556 + 0.121620865728 + 0.121823891409 + 0.122027114655 + 0.12223053552 + 0.122434154059 + 0.122637970327 + 0.122841984379 + 0.123046196269 + 0.123250606053 + 0.123455213785 + 0.123660019521 + 0.123865023313 + 0.124070225218 + 0.12427562529 + 0.124481223584 + 0.124687020154 + 0.124893015054 + 0.12509920834 + 0.125305600066 + 0.125512190287 + 0.125718979056 + 0.125925966429 + 0.12613315246 + 0.126340537203 + 0.126548120713 + 0.126755903044 + 0.126963884251 + 0.127172064387 + 0.127380443508 + 0.127589021667 + 0.127797798919 + 0.128006775318 + 0.128215950919 + 0.128425325775 + 0.12863489994 + 0.12884467347 + 0.129054646417 + 0.129264818837 + 0.129475190783 + 0.129685762309 + 0.12989653347 + 0.130107504319 + 0.130318674911 + 0.130530045299 + 0.130741615537 + 0.13095338568 + 0.131165355781 + 0.131377525894 + 0.131589896074 + 0.131802466373 + 0.132015236847 + 0.132228207548 + 0.13244137853 + 0.132654749847 + 0.132868321554 + 0.133082093703 + 0.133296066349 + 0.133510239544 + 0.133724613344 + 0.133939187801 + 0.134153962969 + 0.134368938901 + 0.134584115652 + 0.134799493275 + 0.135015071823 + 0.135230851349 + 0.135446831909 + 0.135663013554 + 0.135879396338 + 0.136095980315 + 0.136312765538 + 0.136529752061 + 0.136746939936 + 0.136964329218 + 0.13718191996 + 0.137399712214 + 0.137617706034 + 0.137835901474 + 0.138054298587 + 0.138272897425 + 0.138491698043 + 0.138710700493 + 0.138929904828 + 0.139149311102 + 0.139368919367 + 0.139588729678 + 0.139808742086 + 0.140028956645 + 0.140249373408 + 0.140469992428 + 0.140690813758 + 0.140911837451 + 0.14113306356 + 0.141354492137 + 0.141576123237 + 0.141797956911 + 0.142019993212 + 0.142242232194 + 0.142464673909 + 0.142687318409 + 0.142910165749 + 0.14313321598 + 0.143356469155 + 0.143579925328 + 0.143803584549 + 0.144027446874 + 0.144251512353 + 0.14447578104 + 0.144700252987 + 0.144924928247 + 0.145149806873 + 0.145374888917 + 0.145600174431 + 0.145825663468 + 0.146051356081 + 0.146277252321 + 0.146503352243 + 0.146729655897 + 0.146956163337 + 0.147182874614 + 0.147409789782 + 0.147636908892 + 0.147864231997 + 0.148091759149 + 0.1483194904 + 0.148547425803 + 0.14877556541 + 0.149003909274 + 0.149232457445 + 0.149461209978 + 0.149690166923 + 0.149919328333 + 0.15014869426 + 0.150378264757 + 0.150608039875 + 0.150838019666 + 0.151068204182 + 0.151298593476 + 0.1515291876 + 0.151759986605 + 0.151990990543 + 0.152222199467 + 0.152453613428 + 0.152685232479 + 0.152917056671 + 0.153149086055 + 0.153381320685 + 0.153613760611 + 0.153846405886 + 0.154079256562 + 0.154312312689 + 0.154545574321 + 0.154779041508 + 0.155012714302 + 0.155246592756 + 0.15548067692 + 0.155714966847 + 0.155949462588 + 0.156184164194 + 0.156419071718 + 0.156654185211 + 0.156889504724 + 0.157125030309 + 0.157360762017 + 0.157596699901 + 0.157832844011 + 0.1580691944 + 0.158305751117 + 0.158542514216 + 0.158779483747 + 0.159016659761 + 0.159254042311 + 0.159491631447 + 0.15972942722 + 0.159967429683 + 0.160205638886 + 0.160444054881 + 0.160682677718 + 0.16092150745 + 0.161160544127 + 0.161399787801 + 0.161639238523 + 0.161878896343 + 0.162118761313 + 0.162358833485 + 0.162599112909 + 0.162839599636 + 0.163080293717 + 0.163321195205 + 0.163562304148 + 0.163803620599 + 0.164045144609 + 0.164286876228 + 0.164528815508 + 0.164770962499 + 0.165013317253 + 0.165255879819 + 0.16549865025 + 0.165741628596 + 0.165984814907 + 0.166228209235 + 0.166471811631 + 0.166715622145 + 0.166959640827 + 0.16720386773 + 0.167448302903 + 0.167692946397 + 0.167937798263 + 0.168182858552 + 0.168428127314 + 0.168673604599 + 0.16891929046 + 0.169165184945 + 0.169411288106 + 0.169657599993 + 0.169904120657 + 0.170150850149 + 0.170397788518 + 0.170644935816 + 0.170892292092 + 0.171139857397 + 0.171387631783 + 0.171635615298 + 0.171883807994 + 0.172132209921 + 0.172380821129 + 0.172629641669 + 0.17287867159 + 0.173127910944 + 0.173377359781 + 0.17362701815 + 0.173876886103 + 0.174126963688 + 0.174377250957 + 0.17462774796 + 0.174878454747 + 0.175129371368 + 0.175380497874 + 0.175631834313 + 0.175883380737 + 0.176135137196 + 0.176387103739 + 0.176639280417 + 0.17689166728 + 0.177144264378 + 0.17739707176 + 0.177650089477 + 0.177903317579 + 0.178156756116 + 0.178410405137 + 0.178664264693 + 0.178918334833 + 0.179172615607 + 0.179427107066 + 0.179681809259 + 0.179936722235 + 0.180191846045 + 0.180447180739 + 0.180702726366 + 0.180958482976 + 0.181214450619 + 0.181470629344 + 0.181727019201 + 0.18198362024 + 0.182240432511 + 0.182497456063 + 0.182754690946 + 0.183012137209 + 0.183269794902 + 0.183527664074 + 0.183785744776 + 0.184044037057 + 0.184302540966 + 0.184561256552 + 0.184820183866 + 0.185079322956 + 0.185338673873 + 0.185598236666 + 0.185858011383 + 0.186117998075 + 0.186378196791 + 0.18663860758 + 0.186899230491 + 0.187160065575 + 0.187421112879 + 0.187682372455 + 0.18794384435 + 0.188205528614 + 0.188467425296 + 0.188729534447 + 0.188991856114 + 0.189254390347 + 0.189517137195 + 0.189780096708 + 0.190043268934 + 0.190306653923 + 0.190570251723 + 0.190834062385 + 0.191098085956 + 0.191362322487 + 0.191626772025 + 0.191891434621 + 0.192156310323 + 0.19242139918 + 0.192686701241 + 0.192952216555 + 0.193217945171 + 0.193483887138 + 0.193750042505 + 0.19401641132 + 0.194282993634 + 0.194549789494 + 0.194816798949 + 0.195084022048 + 0.19535145884 + 0.195619109375 + 0.195886973699 + 0.196155051863 + 0.196423343915 + 0.196691849904 + 0.196960569879 + 0.197229503888 + 0.197498651979 + 0.197768014202 + 0.198037590606 + 0.198307381238 + 0.198577386148 + 0.198847605383 + 0.199118038994 + 0.199388687027 + 0.199659549532 + 0.199930626558 + 0.200201918152 + 0.200473424364 + 0.200745145241 + 0.201017080833 + 0.201289231187 + 0.201561596353 + 0.201834176378 + 0.202106971311 + 0.2023799812 + 0.202653206094 + 0.202926646041 + 0.203200301089 + 0.203474171287 + 0.203748256683 + 0.204022557326 + 0.204297073263 + 0.204571804542 + 0.204846751213 + 0.205121913323 + 0.20539729092 + 0.205672884053 + 0.20594869277 + 0.206224717119 + 0.206500957148 + 0.206777412905 + 0.207054084439 + 0.207330971797 + 0.207608075028 + 0.207885394179 + 0.208162929299 + 0.208440680435 + 0.208718647636 + 0.20899683095 + 0.209275230424 + 0.209553846107 + 0.209832678047 + 0.210111726291 + 0.210390990887 + 0.210670471884 + 0.210950169329 + 0.21123008327 + 0.211510213755 + 0.211790560831 + 0.212071124548 + 0.212351904951 + 0.21263290209 + 0.212914116012 + 0.213195546764 + 0.213477194395 + 0.213759058951 + 0.214041140482 + 0.214323439035 + 0.214605954656 + 0.214888687395 + 0.215171637298 + 0.215454804413 + 0.215738188788 + 0.21602179047 + 0.216305609507 + 0.216589645947 + 0.216873899837 + 0.217158371225 + 0.217443060158 + 0.217727966683 + 0.218013090849 + 0.218298432702 + 0.218583992291 + 0.218869769662 + 0.219155764862 + 0.219441977941 + 0.219728408944 + 0.220015057919 + 0.220301924914 + 0.220589009975 + 0.220876313151 + 0.221163834489 + 0.221451574035 + 0.221739531837 + 0.222027707943 + 0.222316102399 + 0.222604715254 + 0.222893546553 + 0.223182596345 + 0.223471864677 + 0.223761351595 + 0.224051057147 + 0.22434098138 + 0.224631124341 + 0.224921486078 + 0.225212066637 + 0.225502866066 + 0.225793884411 + 0.22608512172 + 0.226376578039 + 0.226668253416 + 0.226960147898 + 0.227252261532 + 0.227544594365 + 0.227837146443 + 0.228129917814 + 0.228422908524 + 0.228716118621 + 0.229009548151 + 0.229303197162 + 0.2295970657 + 0.229891153812 + 0.230185461545 + 0.230479988946 + 0.230774736061 + 0.231069702938 + 0.231364889622 + 0.231660296162 + 0.231955922604 + 0.232251768994 + 0.232547835379 + 0.232844121806 + 0.233140628321 + 0.233437354972 + 0.233734301805 + 0.234031468867 + 0.234328856203 + 0.234626463862 + 0.234924291889 + 0.235222340331 + 0.235520609235 + 0.235819098647 + 0.236117808614 + 0.236416739182 + 0.236715890398 + 0.237015262309 + 0.23731485496 + 0.237614668399 + 0.237914702671 + 0.238214957824 + 0.238515433903 + 0.238816130955 + 0.239117049027 + 0.239418188165 + 0.239719548415 + 0.240021129824 + 0.240322932438 + 0.240624956303 + 0.240927201466 + 0.241229667972 + 0.241532355869 + 0.241835265203 + 0.242138396019 + 0.242441748364 + 0.242745322285 + 0.243049117826 + 0.243353135036 + 0.243657373959 + 0.243961834643 + 0.244266517132 + 0.244571421474 + 0.244876547715 + 0.2451818959 + 0.245487466075 + 0.245793258288 + 0.246099272583 + 0.246405509007 + 0.246711967606 + 0.247018648426 + 0.247325551513 + 0.247632676913 + 0.247940024672 + 0.248247594836 + 0.248555387451 + 0.248863402563 + 0.249171640217 + 0.249480100461 + 0.249788783338 + 0.250097688897 + 0.250406817181 + 0.250716168238 + 0.251025742113 + 0.251335538852 + 0.2516455585 + 0.251955801104 + 0.25226626671 + 0.252576955362 + 0.252887867107 + 0.253199001991 + 0.253510360059 + 0.253821941357 + 0.254133745931 + 0.254445773826 + 0.254758025089 + 0.255070499764 + 0.255383197898 + 0.255696119535 + 0.256009264722 + 0.256322633505 + 0.256636225928 + 0.256950042038 + 0.25726408188 + 0.2575783455 + 0.257892832942 + 0.258207544253 + 0.258522479479 + 0.258837638663 + 0.259153021853 + 0.259468629094 + 0.259784460431 + 0.260100515909 + 0.260416795574 + 0.260733299471 + 0.261050027646 + 0.261366980144 + 0.26168415701 + 0.26200155829 + 0.26231918403 + 0.262637034274 + 0.262955109067 + 0.263273408456 + 0.263591932485 + 0.2639106812 + 0.264229654646 + 0.264548852868 + 0.264868275911 + 0.265187923821 + 0.265507796643 + 0.265827894421 + 0.266148217202 + 0.266468765031 + 0.266789537952 + 0.26711053601 + 0.267431759251 + 0.267753207721 + 0.268074881463 + 0.268396780523 + 0.268718904946 + 0.269041254778 + 0.269363830063 + 0.269686630846 + 0.270009657172 + 0.270332909087 + 0.270656386635 + 0.270980089861 + 0.27130401881 + 0.271628173528 + 0.271952554058 + 0.272277160447 + 0.272601992739 + 0.272927050978 + 0.27325233521 + 0.27357784548 + 0.273903581832 + 0.274229544312 + 0.274555732963 + 0.274882147832 + 0.275208788962 + 0.275535656399 + 0.275862750187 + 0.276190070371 + 0.276517616996 + 0.276845390106 + 0.277173389747 + 0.277501615962 + 0.277830068798 + 0.278158748297 + 0.278487654506 + 0.278816787468 + 0.279146147229 + 0.279475733832 + 0.279805547324 + 0.280135587747 + 0.280465855147 + 0.280796349568 + 0.281127071056 + 0.281458019653 + 0.281789195406 + 0.282120598358 + 0.282452228554 + 0.282784086039 + 0.283116170857 + 0.283448483052 + 0.283781022669 + 0.284113789753 + 0.284446784347 + 0.284780006497 + 0.285113456246 + 0.28544713364 + 0.285781038721 + 0.286115171536 + 0.286449532128 + 0.286784120541 + 0.28711893682 + 0.287453981009 + 0.287789253152 + 0.288124753295 + 0.28846048148 + 0.288796437753 + 0.289132622157 + 0.289469034737 + 0.289805675536 + 0.290142544601 + 0.290479641973 + 0.290816967698 + 0.29115452182 + 0.291492304383 + 0.291830315431 + 0.292168555008 + 0.292507023158 + 0.292845719926 + 0.293184645356 + 0.293523799491 + 0.293863182375 + 0.294202794054 + 0.29454263457 + 0.294882703969 + 0.295223002293 + 0.295563529587 + 0.295904285895 + 0.29624527126 + 0.296586485728 + 0.296927929341 + 0.297269602144 + 0.297611504181 + 0.297953635496 + 0.298295996131 + 0.298638586133 + 0.298981405543 + 0.299324454407 + 0.299667732768 + 0.300011240669 + 0.300354978156 + 0.300698945271 + 0.301043142058 + 0.301387568561 + 0.301732224824 + 0.302077110891 + 0.302422226805 + 0.30276757261 + 0.303113148351 + 0.30345895407 + 0.303804989811 + 0.304151255618 + 0.304497751535 + 0.304844477606 + 0.305191433873 + 0.305538620381 + 0.305886037173 + 0.306233684294 + 0.306581561785 + 0.306929669692 + 0.307278008058 + 0.307626576926 + 0.30797537634 + 0.308324406343 + 0.308673666979 + 0.309023158292 + 0.309372880325 + 0.309722833121 + 0.310073016724 + 0.310423431177 + 0.310774076524 + 0.311124952809 + 0.311476060074 + 0.311827398364 + 0.312178967721 + 0.312530768189 + 0.312882799812 + 0.313235062632 + 0.313587556694 + 0.31394028204 + 0.314293238713 + 0.314646426758 + 0.314999846217 + 0.315353497134 + 0.315707379552 + 0.316061493515 + 0.316415839065 + 0.316770416246 + 0.3171252251 + 0.317480265673 + 0.317835538005 + 0.318191042142 + 0.318546778125 + 0.318902745999 + 0.319258945805 + 0.319615377588 + 0.319972041391 + 0.320328937256 + 0.320686065228 + 0.321043425348 + 0.32140101766 + 0.321758842207 + 0.322116899033 + 0.322475188179 + 0.32283370969 + 0.323192463609 + 0.323551449977 + 0.32391066884 + 0.324270120238 + 0.324629804216 + 0.324989720816 + 0.325349870081 + 0.325710252055 + 0.32607086678 + 0.326431714298 + 0.326792794654 + 0.32715410789 + 0.327515654049 + 0.327877433174 + 0.328239445307 + 0.328601690491 + 0.32896416877 + 0.329326880186 + 0.329689824782 + 0.3300530026 + 0.330416413684 + 0.330780058077 + 0.33114393582 + 0.331508046958 + 0.331872391532 + 0.332236969586 + 0.332601781161 + 0.332966826302 + 0.33333210505 + 0.333697617448 + 0.334063363539 + 0.334429343366 + 0.334795556971 + 0.335162004396 + 0.335528685686 + 0.335895600881 + 0.336262750025 + 0.336630133161 + 0.33699775033 + 0.337365601576 + 0.337733686941 + 0.338102006468 + 0.338470560199 + 0.338839348176 + 0.339208370443 + 0.339577627041 + 0.339947118014 + 0.340316843403 + 0.340686803252 + 0.341056997602 + 0.341427426497 + 0.341798089977 + 0.342168988087 + 0.342540120868 + 0.342911488363 + 0.343283090614 + 0.343654927664 + 0.344026999555 + 0.344399306329 + 0.344771848028 + 0.345144624695 + 0.345517636373 + 0.345890883103 + 0.346264364929 + 0.346638081891 + 0.347012034033 + 0.347386221396 + 0.347760644024 + 0.348135301958 + 0.34851019524 + 0.348885323913 + 0.349260688018 + 0.349636287599 + 0.350012122697 + 0.350388193355 + 0.350764499614 + 0.351141041517 + 0.351517819106 + 0.351894832422 + 0.352272081509 + 0.352649566409 + 0.353027287162 + 0.353405243812 + 0.353783436401 + 0.35416186497 + 0.354540529562 + 0.354919430218 + 0.355298566981 + 0.355677939893 + 0.356057548995 + 0.356437394331 + 0.356817475941 + 0.357197793867 + 0.357578348152 + 0.357959138838 + 0.358340165966 + 0.358721429579 + 0.359102929718 + 0.359484666426 + 0.359866639743 + 0.360248849713 + 0.360631296376 + 0.361013979776 + 0.361396899953 + 0.361780056949 + 0.362163450807 + 0.362547081568 + 0.362930949273 + 0.363315053966 + 0.363699395687 + 0.364083974478 + 0.364468790381 + 0.364853843438 + 0.365239133691 + 0.365624661181 + 0.36601042595 + 0.366396428039 + 0.366782667491 + 0.367169144347 + 0.367555858648 + 0.367942810438 + 0.368329999756 + 0.368717426644 + 0.369105091145 + 0.3694929933 + 0.369881133151 + 0.370269510739 + 0.370658126105 + 0.371046979292 + 0.37143607034 + 0.371825399292 + 0.372214966189 + 0.372604771073 + 0.372994813984 + 0.373385094965 + 0.373775614057 + 0.374166371302 + 0.37455736674 + 0.374948600414 + 0.375340072365 + 0.375731782634 + 0.376123731263 + 0.376515918294 + 0.376908343767 + 0.377301007723 + 0.377693910206 + 0.378087051255 + 0.378480430912 + 0.378874049219 + 0.379267906217 + 0.379662001946 + 0.38005633645 + 0.380450909768 + 0.380845721942 + 0.381240773014 + 0.381636063025 + 0.382031592016 + 0.382427360027 + 0.382823367102 + 0.38321961328 + 0.383616098603 + 0.384012823112 + 0.384409786849 + 0.384806989855 + 0.38520443217 + 0.385602113836 + 0.386000034894 + 0.386398195386 + 0.386796595352 + 0.387195234834 + 0.387594113872 + 0.387993232508 + 0.388392590783 + 0.388792188739 + 0.389192026415 + 0.389592103853 + 0.389992421095 + 0.390392978181 + 0.390793775152 + 0.39119481205 + 0.391596088915 + 0.391997605788 + 0.392399362711 + 0.392801359724 + 0.393203596868 + 0.393606074185 + 0.394008791715 + 0.394411749499 + 0.394814947578 + 0.395218385993 + 0.395622064786 + 0.396025983996 + 0.396430143665 + 0.396834543833 + 0.397239184543 + 0.397644065833 + 0.398049187746 + 0.398454550322 + 0.398860153601 + 0.399265997626 + 0.399672082436 + 0.400078408072 + 0.400484974575 + 0.400891781987 + 0.401298830347 + 0.401706119696 + 0.402113650076 + 0.402521421526 + 0.402929434088 + 0.403337687803 + 0.40374618271 + 0.404154918852 + 0.404563896267 + 0.404973114998 + 0.405382575085 + 0.405792276568 + 0.406202219488 + 0.406612403886 + 0.407022829802 + 0.407433497277 + 0.407844406352 + 0.408255557067 + 0.408666949462 + 0.409078583579 + 0.409490459458 + 0.409902577139 + 0.410314936663 + 0.410727538071 + 0.411140381402 + 0.411553466698 + 0.411966793999 + 0.412380363346 + 0.412794174779 + 0.413208228338 + 0.413622524064 + 0.414037061997 + 0.414451842178 + 0.414866864648 + 0.415282129446 + 0.415697636613 + 0.41611338619 + 0.416529378216 + 0.416945612733 + 0.417362089781 + 0.417778809399 + 0.418195771629 + 0.41861297651 + 0.419030424084 + 0.419448114389 + 0.419866047468 + 0.420284223359 + 0.420702642103 + 0.421121303741 + 0.421540208313 + 0.421959355859 + 0.422378746419 + 0.422798380033 + 0.423218256742 + 0.423638376586 + 0.424058739606 + 0.424479345841 + 0.424900195331 + 0.425321288117 + 0.425742624239 + 0.426164203737 + 0.426586026651 + 0.427008093021 + 0.427430402888 + 0.427852956292 + 0.428275753272 + 0.428698793869 + 0.429122078123 + 0.429545606074 + 0.429969377762 + 0.430393393228 + 0.43081765251 + 0.431242155649 + 0.431666902686 + 0.43209189366 + 0.432517128611 + 0.43294260758 + 0.433368330606 + 0.433794297729 + 0.434220508989 + 0.434646964426 + 0.435073664081 + 0.435500607993 + 0.435927796201 + 0.436355228747 + 0.436782905669 + 0.437210827008 + 0.437638992804 + 0.438067403096 + 0.438496057925 + 0.43892495733 + 0.43935410135 + 0.439783490027 + 0.4402131234 + 0.440643001508 + 0.441073124392 + 0.441503492091 + 0.441934104645 + 0.442364962094 + 0.442796064477 + 0.443227411835 + 0.443659004207 + 0.444090841633 + 0.444522924152 + 0.444955251805 + 0.445387824631 + 0.445820642669 + 0.44625370596 + 0.446687014543 + 0.447120568458 + 0.447554367744 + 0.447988412442 + 0.44842270259 + 0.448857238229 + 0.449292019398 + 0.449727046137 + 0.450162318484 + 0.450597836481 + 0.451033600167 + 0.45146960958 + 0.451905864761 + 0.45234236575 + 0.452779112585 + 0.453216105306 + 0.453653343954 + 0.454090828566 + 0.454528559184 + 0.454966535846 + 0.455404758592 + 0.455843227462 + 0.456281942494 + 0.456720903729 + 0.457160111205 + 0.457599564963 + 0.458039265042 + 0.45847921148 + 0.458919404318 + 0.459359843596 + 0.459800529351 + 0.460241461625 + 0.460682640455 + 0.461124065882 + 0.461565737945 + 0.462007656684 + 0.462449822136 + 0.462892234343 + 0.463334893343 + 0.463777799176 + 0.464220951881 + 0.464664351496 + 0.465107998063 + 0.465551891619 + 0.465996032204 + 0.466440419857 + 0.466885054618 + 0.467329936526 + 0.46777506562 + 0.468220441939 + 0.468666065522 + 0.469111936409 + 0.469558054639 + 0.470004420252 + 0.470451033285 + 0.470897893779 + 0.471345001772 + 0.471792357304 + 0.472239960414 + 0.472687811141 + 0.473135909525 + 0.473584255603 + 0.474032849416 + 0.474481691002 + 0.4749307804 + 0.475380117651 + 0.475829702792 + 0.476279535862 + 0.476729616902 + 0.477179945949 + 0.477630523043 + 0.478081348223 + 0.478532421528 + 0.478983742996 + 0.479435312668 + 0.479887130581 + 0.480339196776 + 0.48079151129 + 0.481244074162 + 0.481696885433 + 0.48214994514 + 0.482603253323 + 0.48305681002 + 0.48351061527 + 0.483964669113 + 0.484418971587 + 0.484873522731 + 0.485328322584 + 0.485783371185 + 0.486238668573 + 0.486694214786 + 0.487150009863 + 0.487606053844 + 0.488062346767 + 0.48851888867 + 0.488975679593 + 0.489432719575 + 0.489890008653 + 0.490347546868 + 0.490805334257 + 0.49126337086 + 0.491721656715 + 0.492180191862 + 0.492638976337 + 0.493098010182 + 0.493557293433 + 0.49401682613 + 0.494476608312 + 0.494936640017 + 0.495396921284 + 0.495857452151 + 0.496318232658 + 0.496779262842 + 0.497240542743 + 0.497702072399 + 0.498163851849 + 0.498625881131 + 0.499088160284 + 0.499550689347 + 0.500013468358 + 0.500476497356 + 0.500939776379 + 0.501403305466 + 0.501867084655 + 0.502331113985 + 0.502795393495 + 0.503259923222 + 0.503724703206 + 0.504189733486 + 0.504655014098 + 0.505120545083 + 0.505586326478 + 0.506052358322 + 0.506518640654 + 0.506985173511 + 0.507451956933 + 0.507918990957 + 0.508386275623 + 0.508853810968 + 0.509321597032 + 0.509789633851 + 0.510257921466 + 0.510726459913 + 0.511195249232 + 0.511664289461 + 0.512133580639 + 0.512603122803 + 0.513072915991 + 0.513542960243 + 0.514013255597 + 0.514483802091 + 0.514954599763 + 0.515425648651 + 0.515896948794 + 0.51636850023 + 0.516840302998 + 0.517312357135 + 0.51778466268 + 0.518257219671 + 0.518730028146 + 0.519203088144 + 0.519676399702 + 0.52014996286 + 0.520623777654 + 0.521097844124 + 0.521572162308 + 0.522046732243 + 0.522521553968 + 0.522996627522 + 0.523471952941 + 0.523947530264 + 0.52442335953 + 0.524899440777 + 0.525375774042 + 0.525852359364 + 0.526329196781 + 0.526806286331 + 0.527283628051 + 0.527761221981 + 0.528239068158 + 0.52871716662 + 0.529195517406 + 0.529674120552 + 0.530152976098 + 0.530632084081 + 0.53111144454 + 0.531591057512 + 0.532070923035 + 0.532551041148 + 0.533031411887 + 0.533512035292 + 0.5339929114 + 0.53447404025 + 0.534955421878 + 0.535437056323 + 0.535918943623 + 0.536401083817 + 0.53688347694 + 0.537366123033 + 0.537849022132 + 0.538332174275 + 0.538815579501 + 0.539299237846 + 0.53978314935 + 0.54026731405 + 0.540751731983 + 0.541236403188 + 0.541721327702 + 0.542206505563 + 0.542691936809 + 0.543177621478 + 0.543663559608 + 0.544149751235 + 0.544636196399 + 0.545122895137 + 0.545609847486 + 0.546097053484 + 0.54658451317 + 0.54707222658 + 0.547560193753 + 0.548048414726 + 0.548536889537 + 0.549025618223 + 0.549514600823 + 0.550003837374 + 0.550493327913 + 0.550983072479 + 0.551473071109 + 0.55196332384 + 0.552453830711 + 0.552944591758 + 0.55343560702 + 0.553926876534 + 0.554418400337 + 0.554910178468 + 0.555402210964 + 0.555894497862 + 0.5563870392 + 0.556879835016 + 0.557372885347 + 0.557866190231 + 0.558359749704 + 0.558853563806 + 0.559347632573 + 0.559841956042 + 0.560336534252 + 0.560831367239 + 0.561326455042 + 0.561821797697 + 0.562317395243 + 0.562813247716 + 0.563309355154 + 0.563805717595 + 0.564302335076 + 0.564799207634 + 0.565296335307 + 0.565793718133 + 0.566291356147 + 0.566789249389 + 0.567287397896 + 0.567785801704 + 0.568284460851 + 0.568783375375 + 0.569282545312 + 0.569781970701 + 0.570281651579 + 0.570781587982 + 0.571281779949 + 0.571782227516 + 0.572282930721 + 0.572783889601 + 0.573285104193 + 0.573786574535 + 0.574288300664 + 0.574790282617 + 0.575292520432 + 0.575795014146 + 0.576297763795 + 0.576800769418 + 0.577304031051 + 0.577807548731 + 0.578311322497 + 0.578815352385 + 0.579319638431 + 0.579824180675 + 0.580328979151 + 0.580834033899 + 0.581339344954 + 0.581844912355 + 0.582350736138 + 0.58285681634 + 0.583363152998 + 0.58386974615 + 0.584376595833 + 0.584883702084 + 0.585391064939 + 0.585898684437 + 0.586406560613 + 0.586914693506 + 0.587423083152 + 0.587931729588 + 0.588440632851 + 0.588949792979 + 0.589459210008 + 0.589968883975 + 0.590478814918 + 0.590989002873 + 0.591499447878 + 0.592010149968 + 0.592521109183 + 0.593032325557 + 0.593543799129 + 0.594055529935 + 0.594567518013 + 0.595079763398 + 0.595592266129 + 0.596105026241 + 0.596618043773 + 0.59713131876 + 0.59764485124 + 0.59815864125 + 0.598672688826 + 0.599186994006 + 0.599701556825 + 0.600216377322 + 0.600731455533 + 0.601246791495 + 0.601762385244 + 0.602278236818 + 0.602794346254 + 0.603310713587 + 0.603827338855 + 0.604344222095 + 0.604861363344 + 0.605378762638 + 0.605896420014 + 0.606414335509 + 0.606932509159 + 0.607450941002 + 0.607969631074 + 0.608488579412 + 0.609007786052 + 0.609527251032 + 0.610046974388 + 0.610566956156 + 0.611087196374 + 0.611607695078 + 0.612128452304 + 0.612649468091 + 0.613170742473 + 0.613692275488 + 0.614214067172 + 0.614736117563 + 0.615258426696 + 0.615780994609 + 0.616303821338 + 0.616826906919 + 0.617350251389 + 0.617873854785 + 0.618397717144 + 0.618921838501 + 0.619446218894 + 0.619970858359 + 0.620495756933 + 0.621020914651 + 0.621546331552 + 0.622072007671 + 0.622597943044 + 0.623124137709 + 0.623650591702 + 0.624177305059 + 0.624704277817 + 0.625231510012 + 0.625759001681 + 0.62628675286 + 0.626814763586 + 0.627343033895 + 0.627871563823 + 0.628400353408 + 0.628929402685 + 0.629458711692 + 0.629988280463 + 0.630518109036 + 0.631048197448 + 0.631578545734 + 0.632109153931 + 0.632640022075 + 0.633171150203 + 0.633702538351 + 0.634234186555 + 0.634766094852 + 0.635298263279 + 0.63583069187 + 0.636363380664 + 0.636896329695 + 0.637429539001 + 0.637963008618 + 0.638496738582 + 0.639030728929 + 0.639564979695 + 0.640099490918 + 0.640634262632 + 0.641169294875 + 0.641704587683 + 0.642240141091 + 0.642775955137 + 0.643312029856 + 0.643848365284 + 0.644384961458 + 0.644921818414 + 0.645458936189 + 0.645996314818 + 0.646533954337 + 0.647071854783 + 0.647610016192 + 0.648148438599 + 0.648687122043 + 0.649226066557 + 0.649765272179 + 0.650304738945 + 0.65084446689 + 0.651384456051 + 0.651924706464 + 0.652465218165 + 0.653005991191 + 0.653547025576 + 0.654088321358 + 0.654629878573 + 0.655171697255 + 0.655713777443 + 0.656256119171 + 0.656798722475 + 0.657341587392 + 0.657884713958 + 0.658428102208 + 0.65897175218 + 0.659515663907 + 0.660059837428 + 0.660604272777 + 0.661148969991 + 0.661693929106 + 0.662239150157 + 0.66278463318 + 0.663330378213 + 0.663876385289 + 0.664422654446 + 0.66496918572 + 0.665515979145 + 0.666063034759 + 0.666610352597 + 0.667157932695 + 0.667705775089 + 0.668253879814 + 0.668802246907 + 0.669350876404 + 0.66989976834 + 0.670448922751 + 0.670998339673 + 0.671548019143 + 0.672097961194 + 0.672648165865 + 0.67319863319 + 0.673749363205 + 0.674300355946 + 0.674851611449 + 0.675403129749 + 0.675954910883 + 0.676506954886 + 0.677059261794 + 0.677611831643 + 0.678164664468 + 0.678717760305 + 0.67927111919 + 0.679824741159 + 0.680378626247 + 0.68093277449 + 0.681487185924 + 0.682041860585 + 0.682596798508 + 0.683151999728 + 0.683707464283 + 0.684263192206 + 0.684819183534 + 0.685375438303 + 0.685931956549 + 0.686488738306 + 0.68704578361 + 0.687603092498 + 0.688160665004 + 0.688718501165 + 0.689276601016 + 0.689834964592 + 0.69039359193 + 0.690952483064 + 0.691511638031 + 0.692071056865 + 0.692630739603 + 0.693190686281 + 0.693750896932 + 0.694311371594 + 0.694872110301 + 0.69543311309 + 0.695994379996 + 0.696555911053 + 0.697117706299 + 0.697679765767 + 0.698242089495 + 0.698804677516 + 0.699367529867 + 0.699930646584 + 0.700494027701 + 0.701057673254 + 0.701621583279 + 0.702185757811 + 0.702750196885 + 0.703314900537 + 0.703879868802 + 0.704445101716 + 0.705010599314 + 0.705576361632 + 0.706142388705 + 0.706708680567 + 0.707275237256 + 0.707842058805 + 0.708409145251 + 0.708976496629 + 0.709544112974 + 0.710111994321 + 0.710680140706 + 0.711248552164 + 0.711817228731 + 0.712386170441 + 0.712955377331 + 0.713524849435 + 0.714094586788 + 0.714664589427 + 0.715234857385 + 0.7158053907 + 0.716376189405 + 0.716947253536 + 0.717518583128 + 0.718090178217 + 0.718662038838 + 0.719234165025 + 0.719806556815 + 0.720379214243 + 0.720952137343 + 0.72152532615 + 0.722098780701 + 0.722672501031 + 0.723246487173 + 0.723820739164 + 0.724395257039 + 0.724970040833 + 0.725545090581 + 0.726120406319 + 0.72669598808 + 0.727271835902 + 0.727847949817 + 0.728424329863 + 0.729000976073 + 0.729577888484 + 0.730155067129 + 0.730732512045 + 0.731310223266 + 0.731888200827 + 0.732466444764 + 0.733044955111 + 0.733623731904 + 0.734202775177 + 0.734782084966 + 0.735361661306 + 0.735941504231 + 0.736521613777 + 0.737101989979 + 0.737682632871 + 0.738263542489 + 0.738844718868 + 0.739426162043 + 0.740007872048 + 0.740589848919 + 0.741172092691 + 0.741754603398 + 0.742337381076 + 0.742920425759 + 0.743503737483 + 0.744087316282 + 0.744671162192 + 0.745255275246 + 0.745839655481 + 0.746424302931 + 0.747009217631 + 0.747594399616 + 0.74817984892 + 0.748765565579 + 0.749351549628 + 0.749937801101 + 0.750524320033 + 0.751111106459 + 0.751698160414 + 0.752285481933 + 0.75287307105 + 0.753460927801 + 0.75404905222 + 0.754637444342 + 0.755226104201 + 0.755815031834 + 0.756404227274 + 0.756993690556 + 0.757583421715 + 0.758173420785 + 0.758763687802 + 0.759354222801 + 0.759945025815 + 0.760536096881 + 0.761127436031 + 0.761719043302 + 0.762310918728 + 0.762903062344 + 0.763495474184 + 0.764088154283 + 0.764681102675 + 0.765274319397 + 0.765867804481 + 0.766461557963 + 0.767055579878 + 0.76764987026 + 0.768244429143 + 0.768839256563 + 0.769434352554 + 0.770029717151 + 0.770625350388 + 0.7712212523 + 0.771817422922 + 0.772413862288 + 0.773010570433 + 0.773607547391 + 0.774204793197 + 0.774802307886 + 0.775400091492 + 0.775998144049 + 0.776596465593 + 0.777195056158 + 0.777793915778 + 0.778393044488 + 0.778992442322 + 0.779592109316 + 0.780192045503 + 0.780792250918 + 0.781392725595 + 0.781993469569 + 0.782594482875 + 0.783195765547 + 0.78379731762 + 0.784399139127 + 0.785001230104 + 0.785603590585 + 0.786206220604 + 0.786809120196 + 0.787412289396 + 0.788015728237 + 0.788619436754 + 0.789223414981 + 0.789827662954 + 0.790432180706 + 0.791036968271 + 0.791642025685 + 0.792247352982 + 0.792852950195 + 0.793458817359 + 0.79406495451 + 0.79467136168 + 0.795278038905 + 0.795884986218 + 0.796492203655 + 0.797099691249 + 0.797707449035 + 0.798315477047 + 0.798923775319 + 0.799532343886 + 0.800141182782 + 0.800750292042 + 0.801359671699 + 0.801969321788 + 0.802579242343 + 0.803189433398 + 0.803799894988 + 0.804410627148 + 0.80502162991 + 0.80563290331 + 0.806244447382 + 0.806856262159 + 0.807468347677 + 0.808080703969 + 0.80869333107 + 0.809306229013 + 0.809919397834 + 0.810532837566 + 0.811146548243 + 0.8117605299 + 0.81237478257 + 0.812989306288 + 0.813604101089 + 0.814219167006 + 0.814834504073 + 0.815450112324 + 0.816065991795 + 0.816682142518 + 0.817298564528 + 0.817915257859 + 0.818532222545 + 0.81914945862 + 0.819766966119 + 0.820384745075 + 0.821002795523 + 0.821621117496 + 0.822239711029 + 0.822858576156 + 0.82347771291 + 0.824097121327 + 0.824716801439 + 0.825336753281 + 0.825956976887 + 0.826577472291 + 0.827198239527 + 0.82781927863 + 0.828440589632 + 0.829062172568 + 0.829684027472 + 0.830306154379 + 0.830928553321 + 0.831551224333 + 0.832174167449 + 0.832797382703 + 0.833420870129 + 0.834044629761 + 0.834668661633 + 0.835292965778 + 0.835917542231 + 0.836542391025 + 0.837167512195 + 0.837792905774 + 0.838418571797 + 0.839044510296 + 0.839670721307 + 0.840297204863 + 0.840923960998 + 0.841550989745 + 0.842178291139 + 0.842805865213 + 0.843433712002 + 0.844061831539 + 0.844690223858 + 0.845318888993 + 0.845947826977 + 0.846577037845 + 0.84720652163 + 0.847836278366 + 0.848466308088 + 0.849096610828 + 0.84972718662 + 0.850358035499 + 0.850989157498 + 0.851620552651 + 0.852252220991 + 0.852884162553 + 0.85351637737 + 0.854148865475 + 0.854781626904 + 0.855414661689 + 0.856047969864 + 0.856681551462 + 0.857315406519 + 0.857949535066 + 0.858583937139 + 0.85921861277 + 0.859853561993 + 0.860488784843 + 0.861124281352 + 0.861760051555 + 0.862396095484 + 0.863032413175 + 0.86366900466 + 0.864305869972 + 0.864943009147 + 0.865580422217 + 0.866218109215 + 0.866856070177 + 0.867494305134 + 0.868132814121 + 0.868771597172 + 0.869410654319 + 0.870049985598 + 0.87068959104 + 0.87132947068 + 0.871969624552 + 0.872610052688 + 0.873250755123 + 0.87389173189 + 0.874532983022 + 0.875174508554 + 0.875816308518 + 0.876458382948 + 0.877100731878 + 0.877743355342 + 0.878386253372 + 0.879029426002 + 0.879672873266 + 0.880316595197 + 0.880960591829 + 0.881604863195 + 0.882249409329 + 0.882894230264 + 0.883539326034 + 0.884184696672 + 0.884830342211 + 0.885476262686 + 0.886122458128 + 0.886768928573 + 0.887415674053 + 0.888062694601 + 0.888709990252 + 0.889357561038 + 0.890005406994 + 0.890653528151 + 0.891301924544 + 0.891950596207 + 0.892599543171 + 0.893248765472 + 0.893898263142 + 0.894548036215 + 0.895198084723 + 0.895848408701 + 0.896499008181 + 0.897149883198 + 0.897801033783 + 0.898452459971 + 0.899104161796 + 0.899756139289 + 0.900408392485 + 0.901060921417 + 0.901713726118 + 0.902366806622 + 0.903020162961 + 0.903673795169 + 0.90432770328 + 0.904981887326 + 0.905636347341 + 0.906291083358 + 0.90694609541 + 0.907601383531 + 0.908256947753 + 0.908912788111 + 0.909568904637 + 0.910225297364 + 0.910881966326 + 0.911538911555 + 0.912196133086 + 0.912853630951 + 0.913511405184 + 0.914169455817 + 0.914827782883 + 0.915486386417 + 0.916145266451 + 0.916804423018 + 0.917463856152 + 0.918123565885 + 0.918783552251 + 0.919443815283 + 0.920104355013 + 0.920765171476 + 0.921426264704 + 0.92208763473 + 0.922749281588 + 0.92341120531 + 0.92407340593 + 0.92473588348 + 0.925398637995 + 0.926061669506 + 0.926724978047 + 0.927388563651 + 0.928052426351 + 0.92871656618 + 0.929380983171 + 0.930045677357 + 0.930710648772 + 0.931375897448 + 0.932041423418 + 0.932707226715 + 0.933373307372 + 0.934039665423 + 0.9347063009 + 0.935373213836 + 0.936040404265 + 0.936707872218 + 0.93737561773 + 0.938043640833 + 0.93871194156 + 0.939380519944 + 0.940049376018 + 0.940718509816 + 0.941387921369 + 0.94205761071 + 0.942727577874 + 0.943397822892 + 0.944068345798 + 0.944739146624 + 0.945410225404 + 0.94608158217 + 0.946753216955 + 0.947425129793 + 0.948097320715 + 0.948769789755 + 0.949442536946 + 0.95011556232 + 0.950788865911 + 0.951462447751 + 0.952136307873 + 0.95281044631 + 0.953484863095 + 0.95415955826 + 0.954834531839 + 0.955509783864 + 0.956185314368 + 0.956861123384 + 0.957537210945 + 0.958213577083 + 0.958890221831 + 0.959567145223 + 0.96024434729 + 0.960921828065 + 0.961599587582 + 0.962277625873 + 0.962955942971 + 0.963634538909 + 0.964313413718 + 0.964992567433 + 0.965672000086 + 0.966351711709 + 0.967031702335 + 0.967711971997 + 0.968392520728 + 0.96907334856 + 0.969754455527 + 0.970435841659 + 0.971117506992 + 0.971799451556 + 0.972481675385 + 0.973164178512 + 0.973846960968 + 0.974530022788 + 0.975213364002 + 0.975896984645 + 0.976580884748 + 0.977265064345 + 0.977949523468 + 0.978634262149 + 0.979319280421 + 0.980004578317 + 0.980690155869 + 0.98137601311 + 0.982062150073 + 0.98274856679 + 0.983435263294 + 0.984122239617 + 0.984809495791 + 0.985497031851 + 0.986184847827 + 0.986872943752 + 0.98756131966 + 0.988249975582 + 0.988938911552 + 0.989628127601 + 0.990317623762 + 0.991007400068 + 0.991697456552 + 0.992387793245 + 0.99307841018 + 0.99376930739 + 0.994460484907 + 0.995151942764 + 0.995843680993 + 0.996535699626 + 0.997227998697 + 0.997920578237 + 0.998613438279 + 0.999306578856 + 1.0 + 1.00069370174 + 1.00138768412 + 1.00208194716 + 1.00277649089 + 1.00347131536 + 1.00416642058 + 1.0048618066 + 1.00555747345 + 1.00625342116 + 1.00694964975 + 1.00764615927 + 1.00834294975 + 1.00904002121 + 1.00973737369 + 1.01043500723 + 1.01113292185 + 1.01183111759 + 1.01252959448 + 1.01322835255 + 1.01392739184 + 1.01462671237 + 1.01532631419 + 1.01602619731 + 1.01672636178 + 1.01742680762 + 1.01812753488 + 1.01882854357 + 1.01952983374 + 1.02023140541 + 1.02093325862 + 1.0216353934 + 1.02233780978 + 1.0230405078 + 1.02374348748 + 1.02444674886 + 1.02515029198 + 1.02585411685 + 1.02655822353 + 1.02726261203 + 1.02796728239 + 1.02867223464 + 1.02937746881 + 1.03008298495 + 1.03078878307 + 1.03149486321 + 1.03220122541 + 1.03290786969 + 1.03361479609 + 1.03432200464 + 1.03502949538 + 1.03573726832 + 1.03644532351 + 1.03715366099 + 1.03786228077 + 1.03857118289 + 1.03928036739 + 1.0399898343 + 1.04069958364 + 1.04140961546 + 1.04211992978 + 1.04283052663 + 1.04354140606 + 1.04425256808 + 1.04496401273 + 1.04567574005 + 1.04638775006 + 1.04710004281 + 1.04781261831 + 1.0485254766 + 1.04923861772 + 1.04995204169 + 1.05066574856 + 1.05137973834 + 1.05209401108 + 1.0528085668 + 1.05352340553 + 1.05423852732 + 1.05495393218 + 1.05566962016 + 1.05638559128 + 1.05710184558 + 1.05781838308 + 1.05853520383 + 1.05925230785 + 1.05996969517 + 1.06068736583 + 1.06140531986 + 1.06212355729 + 1.06284207815 + 1.06356088248 + 1.0642799703 + 1.06499934165 + 1.06571899655 + 1.06643893505 + 1.06715915718 + 1.06787966296 + 1.06860045243 + 1.06932152561 + 1.07004288255 + 1.07076452328 + 1.07148644781 + 1.0722086562 + 1.07293114846 + 1.07365392463 + 1.07437698475 + 1.07510032884 + 1.07582395694 + 1.07654786908 + 1.07727206528 + 1.07799654559 + 1.07872131003 + 1.07944635864 + 1.08017169145 + 1.08089730848 + 1.08162320978 + 1.08234939536 + 1.08307586528 + 1.08380261955 + 1.08452965821 + 1.08525698128 + 1.08598458881 + 1.08671248083 + 1.08744065736 + 1.08816911843 + 1.08889786409 + 1.08962689435 + 1.09035620926 + 1.09108580884 + 1.09181569313 + 1.09254586216 + 1.09327631595 + 1.09400705455 + 1.09473807798 + 1.09546938627 + 1.09620097946 + 1.09693285758 + 1.09766502066 + 1.09839746872 + 1.09913020182 + 1.09986321996 + 1.1005965232 + 1.10133011155 + 1.10206398505 + 1.10279814373 + 1.10353258762 + 1.10426731676 + 1.10500233118 + 1.1057376309 + 1.10647321597 + 1.1072090864 + 1.10794524224 + 1.10868168351 + 1.10941841025 + 1.11015542248 + 1.11089272025 + 1.11163030357 + 1.11236817249 + 1.11310632704 + 1.11384476723 + 1.11458349312 + 1.11532250472 + 1.11606180208 + 1.11680138521 + 1.11754125416 + 1.11828140896 + 1.11902184963 + 1.11976257621 + 1.12050358873 + 1.12124488722 + 1.12198647171 + 1.12272834223 + 1.12347049883 + 1.12421294152 + 1.12495567034 + 1.12569868531 + 1.12644198648 + 1.12718557388 + 1.12792944753 + 1.12867360746 + 1.12941805371 + 1.13016278631 + 1.13090780529 + 1.13165311069 + 1.13239870252 + 1.13314458084 + 1.13389074565 + 1.13463719701 + 1.13538393494 + 1.13613095946 + 1.13687827062 + 1.13762586844 + 1.13837375296 + 1.1391219242 + 1.1398703822 + 1.14061912699 + 1.1413681586 + 1.14211747706 + 1.1428670824 + 1.14361697466 + 1.14436715387 + 1.14511762005 + 1.14586837324 + 1.14661941347 + 1.14737074078 + 1.14812235518 + 1.14887425672 + 1.14962644542 + 1.15037892133 + 1.15113168446 + 1.15188473484 + 1.15263807252 + 1.15339169753 + 1.15414560988 + 1.15489980962 + 1.15565429677 + 1.15640907138 + 1.15716413346 + 1.15791948305 + 1.15867512018 + 1.15943104488 + 1.16018725719 + 1.16094375713 + 1.16170054474 + 1.16245762004 + 1.16321498308 + 1.16397263387 + 1.16473057246 + 1.16548879886 + 1.16624731312 + 1.16700611527 + 1.16776520533 + 1.16852458333 + 1.16928424932 + 1.17004420331 + 1.17080444535 + 1.17156497546 + 1.17232579367 + 1.17308690001 + 1.17384829452 + 1.17460997723 + 1.17537194816 + 1.17613420735 + 1.17689675484 + 1.17765959064 + 1.17842271479 + 1.17918612733 + 1.17994982829 + 1.18071381769 + 1.18147809556 + 1.18224266195 + 1.18300751687 + 1.18377266036 + 1.18453809246 + 1.18530381318 + 1.18606982257 + 1.18683612066 + 1.18760270746 + 1.18836958303 + 1.18913674738 + 1.18990420056 + 1.19067194258 + 1.19143997348 + 1.1922082933 + 1.19297690205 + 1.19374579979 + 1.19451498652 + 1.19528446229 + 1.19605422713 + 1.19682428107 + 1.19759462414 + 1.19836525637 + 1.19913617778 + 1.19990738842 + 1.20067888832 + 1.20145067749 + 1.20222275598 + 1.20299512382 + 1.20376778104 + 1.20454072766 + 1.20531396372 + 1.20608748925 + 1.20686130429 + 1.20763540885 + 1.20840980298 + 1.2091844867 + 1.20995946004 + 1.21073472304 + 1.21151027573 + 1.21228611814 + 1.21306225029 + 1.21383867222 + 1.21461538396 + 1.21539238555 + 1.216169677 + 1.21694725836 + 1.21772512965 + 1.21850329091 + 1.21928174216 + 1.22006048344 + 1.22083951477 + 1.22161883619 + 1.22239844774 + 1.22317834943 + 1.2239585413 + 1.22473902338 + 1.22551979571 + 1.22630085831 + 1.22708221121 + 1.22786385445 + 1.22864578805 + 1.22942801205 + 1.23021052647 + 1.23099333136 + 1.23177642673 + 1.23255981262 + 1.23334348906 + 1.23412745609 + 1.23491171372 + 1.235696262 + 1.23648110095 + 1.23726623061 + 1.238051651 + 1.23883736216 + 1.23962336411 + 1.24040965689 + 1.24119624053 + 1.24198311505 + 1.2427702805 + 1.2435577369 + 1.24434548427 + 1.24513352266 + 1.24592185209 + 1.24671047259 + 1.2474993842 + 1.24828858694 + 1.24907808085 + 1.24986786595 + 1.25065794228 + 1.25144830986 + 1.25223896874 + 1.25302991893 + 1.25382116047 + 1.25461269339 + 1.25540451772 + 1.25619663349 + 1.25698904073 + 1.25778173948 + 1.25857472976 + 1.2593680116 + 1.26016158504 + 1.2609554501 + 1.26174960681 + 1.26254405521 + 1.26333879533 + 1.2641338272 + 1.26492915084 + 1.26572476629 + 1.26652067358 + 1.26731687274 + 1.26811336381 + 1.2689101468 + 1.26970722175 + 1.27050458869 + 1.27130224766 + 1.27210019868 + 1.27289844178 + 1.273696977 + 1.27449580436 + 1.27529492389 + 1.27609433563 + 1.27689403961 + 1.27769403586 + 1.2784943244 + 1.27929490527 + 1.28009577849 + 1.28089694411 + 1.28169840214 + 1.28250015263 + 1.28330219559 + 1.28410453107 + 1.28490715908 + 1.28571007967 + 1.28651329286 + 1.28731679868 + 1.28812059716 + 1.28892468834 + 1.28972907224 + 1.2905337489 + 1.29133871834 + 1.29214398059 + 1.29294953569 + 1.29375538367 + 1.29456152455 + 1.29536795837 + 1.29617468516 + 1.29698170494 + 1.29778901775 + 1.29859662362 + 1.29940452258 + 1.30021271466 + 1.30102119989 + 1.3018299783 + 1.30263904991 + 1.30344841477 + 1.3042580729 + 1.30506802433 + 1.30587826909 + 1.30668880721 + 1.30749963873 + 1.30831076367 +} diff --git a/tests/python/ColorSpaceTest.py b/tests/python/ColorSpaceTest.py index 0c04f04b3..a78dcb750 100644 --- a/tests/python/ColorSpaceTest.py +++ b/tests/python/ColorSpaceTest.py @@ -352,7 +352,7 @@ def test_transform(self): self.colorspace.setTransform(self.log_tr, direction) log_transform = self.colorspace.getTransform(direction) self.assertIsInstance(log_transform, OCIO.LogTransform) - self.assertEquals(self.log_tr.getBase(), log_transform.getBase()) + self.assertEqual(self.log_tr.getBase(), log_transform.getBase()) def test_aliases(self): """ @@ -817,7 +817,7 @@ def check_processor_inv(self, p): self.assertEqual(csname, "Linear ITU-R BT.709") csname = OCIO.Config.IdentifyBuiltinColorSpace(editableCfg, builtinConfig, "ACEScct") - self.assertEqual(csname, "CS Transform color space") + self.assertEqual(csname, "not sRGB") csname = OCIO.Config.IdentifyBuiltinColorSpace(editableCfg, builtinConfig, "lin_ap1") self.assertEqual(csname, "ACES cg") diff --git a/tests/python/TransformsTest.py b/tests/python/TransformsTest.py index 9354eec6e..3ef7f0380 100644 --- a/tests/python/TransformsTest.py +++ b/tests/python/TransformsTest.py @@ -48,14 +48,14 @@ def test_copy(self): other = copy.deepcopy(transform) self.assertFalse(other is transform) - self.assertEquals(other.getTransformType(), transform.getTransformType()) - self.assertEquals(other.getDirection(), transform.getDirection()) + self.assertEqual(other.getTransformType(), transform.getTransformType()) + self.assertEqual(other.getDirection(), transform.getDirection()) # Not all OCIO.Transform have equals methods if hasattr(transform, 'equals'): self.assertTrue(other.equals(transform)) other.setDirection(OCIO.TRANSFORM_DIR_INVERSE) - self.assertNotEquals(other.getDirection(), transform.getDirection()) + self.assertNotEqual(other.getDirection(), transform.getDirection()) def test_binding_group_polymorphism(self): """ diff --git a/tests/utils/StringUtils_tests.cpp b/tests/utils/StringUtils_tests.cpp index fb9cb79de..0154a4fc1 100644 --- a/tests/utils/StringUtils_tests.cpp +++ b/tests/utils/StringUtils_tests.cpp @@ -50,6 +50,12 @@ OCIO_ADD_TEST(StringUtils, trim) const std::string str = StringUtils::Trim(ref); OCIO_CHECK_EQUAL(str, "lOwEr 1*& ctfG"); } + + { + // Test that no assert happens when the Trim argument is not an unsigned char (see issue #1874). + constexpr char ref2[]{ -1, -2, -3, '\0' }; + const std::string str = StringUtils::Trim(ref2); + } } OCIO_ADD_TEST(StringUtils, split) diff --git a/vendor/openfx/OCIOUtils.cpp b/vendor/openfx/OCIOUtils.cpp index ca4490506..90f6e4252 100644 --- a/vendor/openfx/OCIOUtils.cpp +++ b/vendor/openfx/OCIOUtils.cpp @@ -8,8 +8,9 @@ namespace OCIO = OCIO_NAMESPACE; #include #include +#include + #include "ofxsLog.h" -#include "pystring/pystring.h" namespace {