pax_global_header00006660000000000000000000000064152372126640014522gustar00rootroot0000000000000052 comment=e5f5a022fe96fc4dc2ee35c19f733a50df807abe libultrahdr-2.0.2/000077500000000000000000000000001523721266400140375ustar00rootroot00000000000000libultrahdr-2.0.2/.clang-format000066400000000000000000000002311523721266400164060ustar00rootroot00000000000000# Run manually to reformat a file: # clang-format -i --style=file Language: Cpp BasedOnStyle: Google ColumnLimit: 100 SortIncludes: false libultrahdr-2.0.2/.github/000077500000000000000000000000001523721266400153775ustar00rootroot00000000000000libultrahdr-2.0.2/.github/workflows/000077500000000000000000000000001523721266400174345ustar00rootroot00000000000000libultrahdr-2.0.2/.github/workflows/cifuzz.yml000066400000000000000000000012761523721266400214770ustar00rootroot00000000000000name: CIFuzz on: [pull_request] jobs: Fuzzing: runs-on: ubuntu-latest strategy: matrix: sanitizer: [address, memory] steps: - name: Build Fuzzers id: build uses: google/oss-fuzz/infra/cifuzz/actions/build_fuzzers@master with: oss-fuzz-project-name: 'libultrahdr' language: c++ - name: Run Fuzzers uses: google/oss-fuzz/infra/cifuzz/actions/run_fuzzers@master with: oss-fuzz-project-name: 'libultrahdr' language: c++ fuzz-seconds: 600 - name: Upload Crash uses: actions/upload-artifact@v4 if: failure() && steps.build.outcome == 'success' with: name: artifacts path: ./out/artifacts libultrahdr-2.0.2/.github/workflows/cmake_android.yml000066400000000000000000000023621523721266400227420ustar00rootroot00000000000000name: Build CI - Android # Build CI for Android on: [ push, pull_request ] jobs: build: runs-on: ${{ matrix.os }} strategy: fail-fast: true matrix: os: [ubuntu-latest] abi: [armeabi-v7a, arm64-v8a, x86, x86_64] steps: - name: Checkout the repository uses: actions/checkout@v3 - name: Set up JDK 17 uses: actions/setup-java@v3 with: java-version: '17' distribution: 'temurin' - name: Download and Setup the Android NDK uses: nttld/setup-ndk@v1 id: setup-ndk with: # r25c is the same as 25.2.9519653. ndk-version: r25c add-to-path: false - name: Setup ninja uses: seanmiddleditch/gha-setup-ninja@master - name: Setup cmake uses: jwlawson/actions-setup-cmake@v2 - name: Configure CMake shell: bash run: | mkdir build cmake -G Ninja -B build -DCMAKE_TOOLCHAIN_FILE=./cmake/toolchains/android.cmake -DUHDR_ANDROID_NDK_PATH=${{ steps.setup-ndk.outputs.ndk-path }} -DUHDR_BUILD_TESTS=1 -DUHDR_ENABLE_LOGS=1 -DUHDR_BUILD_DEPS=1 -DANDROID_ABI=${{ matrix.abi }} -DANDROID_PLATFORM=android-23 -DUHDR_BUILD_JAVA=1 -DUHDR_ENABLE_WERROR=1 - name: Build run: cmake --build build libultrahdr-2.0.2/.github/workflows/cmake_linux.yml000066400000000000000000000101771523721266400224640ustar00rootroot00000000000000name: Build and Test CI - Linux # Build and Test CI for ubuntu-latest on: [ push, pull_request ] jobs: build: name: ${{ matrix.config.name }} runs-on: ${{ matrix.config.os }} strategy: fail-fast: true matrix: config: # - name: "ubuntu latest gcc rel ninja" os: ubuntu-latest build_type: Release cc: gcc cxx: g++ cmake-opts: '-DUHDR_BUILD_TESTS=1 -DUHDR_ENABLE_LOGS=1 -DUHDR_ENABLE_INSTALL=1 -DUHDR_ENABLE_WERROR=1' # - name: "ubuntu latest clang rel ninja" os: ubuntu-latest build_type: Release cc: clang cxx: clang++ cmake-opts: '-DUHDR_BUILD_TESTS=1 -DUHDR_ENABLE_LOGS=1 -DUHDR_ENABLE_INSTALL=1 -DUHDR_ENABLE_WERROR=1' # - name: "ubuntu latest gcc rel ninja with deps" os: ubuntu-latest build_type: Release cc: gcc cxx: g++ cmake-opts: '-DUHDR_BUILD_TESTS=1 -DUHDR_ENABLE_LOGS=1 -DUHDR_BUILD_DEPS=1 -DUHDR_ENABLE_WERROR=1' # - name: "ubuntu latest gcc rel ninja with deps and libsmpte2094_50" os: ubuntu-latest build_type: Release cc: gcc cxx: g++ cmake-opts: '-DUHDR_BUILD_TESTS=1 -DUHDR_ENABLE_LOGS=1 -DUHDR_BUILD_DEPS=1 -DUHDR_ENABLE_WERROR=1 -DUHDR_ENABLE_SMPTE2094_50=1' # - name: "ubuntu latest clang rel ninja with deps sanitize address" os: ubuntu-latest build_type: Release cc: clang cxx: clang++ cmake-opts: '-DUHDR_BUILD_TESTS=1 -DUHDR_ENABLE_LOGS=1 -DUHDR_BUILD_DEPS=1 -DUHDR_SANITIZE_OPTIONS=address -DUHDR_ENABLE_WERROR=1' # - name: "ubuntu latest clang rel ninja fuzzers sanitize address" os: ubuntu-latest build_type: Release cc: clang cxx: clang++ cmake-opts: '-DUHDR_BUILD_FUZZERS=1 -DUHDR_SANITIZE_OPTIONS=address -DUHDR_ENABLE_WERROR=1' # - name: "ubuntu latest gcc rel ninja static" os: ubuntu-latest build_type: Release cc: gcc cxx: g++ cmake-opts: '-DUHDR_BUILD_TESTS=1 -DUHDR_ENABLE_LOGS=1 -DUHDR_ENABLE_INSTALL=1 -DBUILD_SHARED_LIBS=0 -DUHDR_ENABLE_WERROR=1' # - name: "ubuntu latest clang rel ninja static" os: ubuntu-latest build_type: Release cc: clang cxx: clang++ cmake-opts: '-DUHDR_BUILD_TESTS=1 -DUHDR_ENABLE_LOGS=1 -DUHDR_ENABLE_INSTALL=1 -DBUILD_SHARED_LIBS=0 -DUHDR_ENABLE_WERROR=1' steps: - name: Checkout the repository uses: actions/checkout@v4 - name: Setup ninja uses: seanmiddleditch/gha-setup-ninja@master - name: Setup cmake uses: jwlawson/actions-setup-cmake@v2 - name: Install dependencies on Ubuntu run: sudo apt install -y libjpeg-dev - name: Configure CMake shell: bash run: | export CC=${{ matrix.config.cc }} export CXX=${{ matrix.config.cxx }} mkdir build cmake -G Ninja -B build -DCMAKE_BUILD_TYPE=${{ matrix.config.build_type }} ${{ matrix.config.cmake-opts }} - name: Build run: cmake --build build --config ${{ matrix.config.build_type }} - name: Test working-directory: build run: ctest --build-config ${{ matrix.config.build_type }} libultrahdr-2.0.2/.github/workflows/cmake_mac.yml000066400000000000000000000107501523721266400220620ustar00rootroot00000000000000name: Build and Test CI - macOS # Build and Test CI for macOS-latest on: [ push, pull_request ] jobs: build: name: ${{ matrix.config.name }} runs-on: ${{ matrix.config.os }} strategy: fail-fast: true matrix: config: # - name: "macOS latest ARM64 clang rel ninja" os: macos-latest build_type: Release cc: clang cxx: clang++ cmake-opts: '-DUHDR_BUILD_TESTS=1 -DUHDR_ENABLE_LOGS=1 -DUHDR_ENABLE_INSTALL=1 -DUHDR_ENABLE_WERROR=1' run_tests: true expected_arch: arm64 # - name: "macOS latest ARM64 clang rel ninja with deps" os: macos-latest build_type: Release cc: clang cxx: clang++ cmake-opts: '-DUHDR_BUILD_TESTS=1 -DUHDR_ENABLE_LOGS=1 -DUHDR_BUILD_DEPS=1 -DUHDR_ENABLE_WERROR=1' run_tests: true expected_arch: arm64 # - name: "macOS latest ARM64 clang rel ninja static" os: macos-latest build_type: Release cc: clang cxx: clang++ cmake-opts: '-DUHDR_BUILD_TESTS=1 -DUHDR_ENABLE_LOGS=1 -DUHDR_ENABLE_INSTALL=1 -DBUILD_SHARED_LIBS=0 -DUHDR_ENABLE_WERROR=1' run_tests: true expected_arch: arm64 # - name: "macOS-15 Intel clang rel ninja" os: macos-15-intel build_type: Release cc: clang cxx: clang++ cmake-opts: '-DUHDR_BUILD_TESTS=1 -DUHDR_ENABLE_LOGS=1 -DUHDR_ENABLE_INSTALL=1 -DUHDR_ENABLE_WERROR=1' run_tests: true run_cli_smoke: true expected_arch: x86_64 # - name: "macOS latest ARM64 cross x86_64 clang rel ninja with deps" os: macos-latest build_type: Release cc: clang cxx: clang++ cmake-opts: '-DUHDR_BUILD_TESTS=1 -DUHDR_ENABLE_LOGS=1 -DUHDR_BUILD_DEPS=1 -DUHDR_ENABLE_WERROR=1 -DCMAKE_OSX_ARCHITECTURES=x86_64 -DCMAKE_OSX_DEPLOYMENT_TARGET=11.0' run_tests: false expected_arch: x86_64 steps: - name: Checkout the repository uses: actions/checkout@v4 - name: Setup ninja uses: seanmiddleditch/gha-setup-ninja@master - name: Setup cmake uses: jwlawson/actions-setup-cmake@v2 - name: Install dependencies on macOS run: | if ! command -v pkg-config &> /dev/null; then brew install pkg-config fi if ! brew list jpeg-turbo &> /dev/null; then brew install jpeg-turbo fi - name: Configure CMake shell: bash run: | export CC=${{ matrix.config.cc }} export CXX=${{ matrix.config.cxx }} mkdir build cmake -G Ninja -B build -DCMAKE_BUILD_TYPE=${{ matrix.config.build_type }} ${{ matrix.config.cmake-opts }} - name: Build run: cmake --build build --config ${{ matrix.config.build_type }} - name: Verify artifact architecture env: EXPECTED_ARCH: ${{ matrix.config.expected_arch }} shell: bash run: | artifacts=( build/libuhdr.a build/ultrahdr_app build/ultrahdr_unit_test ) if [[ -e build/libuhdr.dylib ]]; then artifacts+=(build/libuhdr.dylib) fi if [[ -e build/turbojpeg/src/turbojpeg-build/libjpeg.a ]]; then artifacts+=(build/turbojpeg/src/turbojpeg-build/libjpeg.a) fi for artifact in "${artifacts[@]}"; do test "$(lipo -archs "$artifact")" = "$EXPECTED_ARCH" done - name: Test if: matrix.config.run_tests working-directory: build run: ctest --build-config ${{ matrix.config.build_type }} - name: Test CLI if: matrix.config.run_cli_smoke run: | build/ultrahdr_app -m 0 -p tests/data/raw_p010_image.p010 -w 1280 -h 720 -a 0 -z build/cli-smoke.jpeg build/ultrahdr_app -m 1 -j build/cli-smoke.jpeg -P build/ultrahdr_app -m 1 -j build/cli-smoke.jpeg -o 1 -O 5 -z build/cli-smoke.raw libultrahdr-2.0.2/.github/workflows/cmake_win.yml000066400000000000000000000023631523721266400221200ustar00rootroot00000000000000name: Build and Test CI - Windows # Build and Test CI for windows-latest on: [ push, pull_request ] jobs: build: name: ${{ matrix.config.name }} runs-on: ${{ matrix.config.os }} strategy: fail-fast: true matrix: config: # - name: "windows latest cl rel visual studio 17 2022 with deps" os: windows-latest build_type: Release cc: cl cxx: cl cmake-opts: '-DUHDR_BUILD_TESTS=1 -DUHDR_ENABLE_LOGS=1 -DUHDR_BUILD_DEPS=1 -DUHDR_ENABLE_WERROR=1' steps: - name: Checkout the repository uses: actions/checkout@v4 - name: Setup cmake uses: jwlawson/actions-setup-cmake@v2 - name: Configure CMake shell: bash run: | export CC=${{ matrix.config.cc }} export CXX=${{ matrix.config.cxx }} mkdir build cmake -B build -DCMAKE_BUILD_TYPE=${{ matrix.config.build_type }} ${{ matrix.config.cmake-opts }} - name: Build run: cmake --build build --config ${{ matrix.config.build_type }} - name: Test working-directory: build run: ctest --build-config ${{ matrix.config.build_type }}libultrahdr-2.0.2/.github/workflows/daily-regression.yml000066400000000000000000000120271523721266400234410ustar00rootroot00000000000000name: Daily Regression Matrix & Auto-Triage on: schedule: # Runs every day at 02:00 UTC - cron: '0 2 * * *' workflow_dispatch: permissions: contents: write issues: write pull-requests: write jobs: regression-matrix: name: ${{ matrix.config.name }} runs-on: ${{ matrix.config.os }} strategy: fail-fast: false matrix: config: # --- Linux Configurations --- - name: "Linux GCC (HEIF ON)" os: ubuntu-latest cc: gcc cxx: g++ cmake-opts: "-DUHDR_BUILD_TESTS=1 -DUHDR_BUILD_DEPS=1 -DUHDR_ENABLE_HEIF=1 -DUHDR_ENABLE_WERROR=1" enable_heif: "ON" - name: "Linux Clang ASan/UBSan (HEIF ON)" os: ubuntu-latest cc: clang cxx: clang++ cmake-opts: "-DUHDR_BUILD_TESTS=1 -DUHDR_BUILD_DEPS=1 -DUHDR_ENABLE_HEIF=1 -DUHDR_SANITIZE_OPTIONS=address,undefined -DUHDR_ENABLE_WERROR=1" enable_heif: "ON" - name: "Linux Clang (HEIF OFF - JPEG Only)" os: ubuntu-latest cc: clang cxx: clang++ cmake-opts: "-DUHDR_BUILD_TESTS=1 -DUHDR_BUILD_DEPS=1 -DUHDR_ENABLE_HEIF=0 -DUHDR_ENABLE_WERROR=1" enable_heif: "OFF" # --- macOS Configurations --- - name: "macOS Apple Silicon (HEIF ON)" os: macos-14 cc: clang cxx: clang++ cmake-opts: "-DUHDR_BUILD_TESTS=1 -DUHDR_BUILD_DEPS=1 -DUHDR_ENABLE_HEIF=1 -DUHDR_ENABLE_WERROR=1" enable_heif: "ON" # --- Windows Configurations --- - name: "Windows MSVC 2022 (HEIF OFF)" os: windows-latest cc: cl cxx: cl cmake-opts: "-DUHDR_BUILD_TESTS=1 -DUHDR_BUILD_DEPS=1 -DUHDR_ENABLE_HEIF=0 -DUHDR_ENABLE_WERROR=1" enable_heif: "OFF" steps: - name: Checkout repository uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: fetch-depth: 50 - name: Setup Ninja uses: seanmiddleditch/gha-setup-ninja@7e868db0f3406270dd46e1dac26c65f621456723 # master - name: Setup CMake uses: jwlawson/actions-setup-cmake@802fa1a2c4e212495c05bf94dba2704a92a472be # v2.0.2 - name: Install dependencies (Ubuntu) if: runner.os == 'Linux' run: | sudo apt update sudo apt install -y libjpeg-dev pkg-config yasm - name: Install dependencies (macOS) if: runner.os == 'macOS' run: | brew install yasm pkg-config || true - name: Configure CMake shell: bash env: RUNNER_OS_NAME: ${{ runner.os }} MATRIX_CC: ${{ matrix.config.cc }} MATRIX_CXX: ${{ matrix.config.cxx }} MATRIX_CMAKE_OPTS: ${{ matrix.config.cmake-opts }} run: | if [ "$RUNNER_OS_NAME" != "Windows" ]; then export CC="$MATRIX_CC" export CXX="$MATRIX_CXX" cmake -G Ninja -B build -DCMAKE_BUILD_TYPE=Release $MATRIX_CMAKE_OPTS else cmake -B build -DCMAKE_BUILD_TYPE=Release $MATRIX_CMAKE_OPTS fi - name: Build Targets shell: bash run: | cmake --build build --config Release - name: Execute Regression Test Suite id: run_tests working-directory: build shell: bash run: | set -o pipefail export DYLD_LIBRARY_PATH="$(pwd)/libheif/src/libheif-build/libheif:$DYLD_LIBRARY_PATH" export LD_LIBRARY_PATH="$(pwd)/libheif/src/libheif-build/libheif:$LD_LIBRARY_PATH" if [ "$RUNNER_OS" = "Windows" ]; then ctest --output-on-failure --build-config Release -R UHDRUnitTests 2>&1 | tee test_output.log else ./ultrahdr_unit_test 2>&1 | tee test_output.log fi - name: Report Regression Issue (On Failure) if: failure() && steps.run_tests.outcome == 'failure' shell: bash env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} MATRIX_OS: ${{ matrix.config.os }} MATRIX_CC: ${{ matrix.config.cc }} MATRIX_CMAKE_OPTS: ${{ matrix.config.cmake-opts }} GITHUB_REPO: ${{ github.repository }} run: | LOG_FILE="build/test_output.log" if [ ! -f "$LOG_FILE" ]; then LOG_FILE="test_output.log"; fi python3 scripts/report_regression_issue.py \ --log-file "$LOG_FILE" \ --os "$MATRIX_OS" \ --compiler "$MATRIX_CC" \ --config "$MATRIX_CMAKE_OPTS" \ --repo "$GITHUB_REPO" \ --token "$GITHUB_TOKEN" \ --run-url "$GITHUB_RUN_URL" - name: Upload Test Failure Logs if: failure() uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 with: name: regression-logs-${{ matrix.config.name }} path: | build/test_output.log test_output.log libultrahdr-2.0.2/.gitignore000066400000000000000000000002201523721266400160210ustar00rootroot00000000000000# build output directories build build* # downloaded dependencies third_party/googletest third_party/turbojpeg third_party/benchmark tests/datalibultrahdr-2.0.2/Android.bp000066400000000000000000000056671523721266400157600ustar00rootroot00000000000000// Copyright 2022 The Android Open Source Project // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. package { // See: http://go/android-license-faq default_applicable_licenses: [ "external_libultrahdr_license", "adobe_hdr_gain_map_license", ], } license { name: "external_libultrahdr_license", visibility: [":__subpackages__"], license_kinds: [ "SPDX-license-identifier-Apache-2.0", ], license_text: [ "LICENSE", ], } cc_library { name: "libultrahdr", host_supported: true, vendor_available: true, export_include_dirs: [ ".", "lib/include", ], local_include_dirs: ["lib/include"], cflags: [ "-DUHDR_ENABLE_INTRINSICS", "-DUHDR_WRITE_ISO", "-DUHDR_WRITE_XMP", ], srcs: [ "lib/src/icc.cpp", "lib/src/jpegr.cpp", "lib/src/gainmapmath.cpp", "lib/src/gainmapmetadata.cpp", "lib/src/jpegrutils.cpp", "lib/src/multipictureformat.cpp", "lib/src/editorhelper.cpp", "lib/src/ultrahdr_api.cpp", ], shared_libs: [ "libimage_io", "libjpeg", "libjpegencoder", "libjpegdecoder", "liblog", ], rtti: true, target: { windows: { enabled: true, }, android: { srcs: [ "lib/src/gpu/applygainmap_gl.cpp", "lib/src/gpu/editorhelper_gl.cpp", "lib/src/gpu/uhdr_gl_utils.cpp", ], cflags: ["-DUHDR_ENABLE_GLES"], shared_libs: [ "libEGL", "libGLESv3", ], }, }, arch: { arm: { srcs: [ "lib/src/dsp/arm/editorhelper_neon.cpp", "lib/src/dsp/arm/gainmapmath_neon.cpp", ], }, arm64: { srcs: [ "lib/src/dsp/arm/editorhelper_neon.cpp", "lib/src/dsp/arm/gainmapmath_neon.cpp", ], }, }, } cc_library { name: "libjpegencoder", host_supported: true, vendor_available: true, shared_libs: [ "libjpeg", "liblog", ], export_include_dirs: ["lib/include"], srcs: [ "lib/src/jpegencoderhelper.cpp", ], target: { windows: { enabled: true, }, }, } cc_library { name: "libjpegdecoder", host_supported: true, vendor_available: true, shared_libs: [ "libjpeg", "liblog", ], export_include_dirs: ["lib/include"], srcs: [ "lib/src/jpegdecoderhelper.cpp", ], target: { windows: { enabled: true, }, }, } libultrahdr-2.0.2/CMakeLists.txt000066400000000000000000001215051523721266400166030ustar00rootroot00000000000000# # Copyright (C) 2023 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 or the MIT license # , at your # option. This file may not be copied, modified, or distributed # except according to those terms. # cmake_minimum_required(VERSION 3.15) cmake_policy(SET CMP0091 NEW) set(UHDR_MAJOR_VERSION 2) set(UHDR_MINOR_VERSION 0) set(UHDR_PATCH_VERSION 2) project(libuhdr VERSION ${UHDR_MAJOR_VERSION}.${UHDR_MINOR_VERSION}.${UHDR_PATCH_VERSION} LANGUAGES C CXX DESCRIPTION "Library for encoding and decoding ultrahdr images") ########################################################### # Detect system ########################################################### if(${CMAKE_SYSTEM_NAME} MATCHES "Linux") elseif(${CMAKE_SYSTEM_NAME} MATCHES "Emscripten") elseif(${CMAKE_SYSTEM_NAME} MATCHES "Android") elseif(${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD") elseif(WIN32) elseif(APPLE) else() message(FATAL_ERROR "Platform ${CMAKE_SYSTEM_NAME} not recognized") endif() if(CMAKE_SYSTEM_PROCESSOR MATCHES "amd64.*|x86_64.*|AMD64.*") if(CMAKE_SIZEOF_VOID_P EQUAL 8) set(ARCH "amd64") else() set(ARCH "i386") endif() elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "i686.*|i386.*|x86.*") set(ARCH "i386") elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^(aarch64.*|AARCH64.*|arm64.*|ARM64.*)") if(CMAKE_SIZEOF_VOID_P EQUAL 8) set(ARCH "aarch64") else() set(ARCH "arm") endif() elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^(arm.*|ARM.*)") set(ARCH "arm") elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^riscv64") set(ARCH "riscv64") elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^riscv32") set(ARCH "riscv32") elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^loongarch64") set(ARCH "loong64") else() message(FATAL_ERROR "Architecture: ${CMAKE_SYSTEM_PROCESSOR} not recognized") endif() # Cross compiling on macOS. The cross compiling architecture should override # auto-detected system architecture settings. if(CMAKE_OSX_ARCHITECTURES) if(APPLE) if(CMAKE_OSX_ARCHITECTURES STREQUAL "arm64") set(ARCH "aarch64") elseif(CMAKE_OSX_ARCHITECTURES STREQUAL "x86_64") set(ARCH "x86_64") elseif(CMAKE_OSX_ARCHITECTURES STREQUAL "i386") set(ARCH "x86") else() message(FATAL_ERROR "Architecture ${CMAKE_OSX_ARCHITECTURES} is not " "supported. Only one architecture (arm64, x86_64 " "or i386) could be specified at build time.") endif() else() message(WARNING "ignoring setting CMAKE_OSX_ARCHITECTURES:= ${CMAKE_OSX_ARCHITECTURES}") endif() endif() ########################################################### # Directories ########################################################### set(SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/lib) set(THIRD_PARTY_DIR ${CMAKE_CURRENT_SOURCE_DIR}/third_party) set(JAVA_DIR ${CMAKE_CURRENT_SOURCE_DIR}/java) set(TESTS_DIR ${CMAKE_CURRENT_SOURCE_DIR}/tests) set(BENCHMARK_DIR ${CMAKE_CURRENT_SOURCE_DIR}/benchmark) set(FUZZERS_DIR ${CMAKE_CURRENT_SOURCE_DIR}/fuzzer) set(EXAMPLES_DIR ${CMAKE_CURRENT_SOURCE_DIR}/examples) set(EXPORT_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}) if(CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_CURRENT_BINARY_DIR) message(WARNING "Selected in-source build. Preferably, create a build/ directory and build from there.") endif() ########################################################### # Options ########################################################### get_cmake_property(IS_MULTI GENERATOR_IS_MULTI_CONFIG) if(NOT IS_MULTI) if(NOT CMAKE_BUILD_TYPE) message(STATUS "No build type chosen, selecting Release") set(CMAKE_BUILD_TYPE "Release" CACHE STRING "The type of build: Debug Release MinSizeRel RelWithDebInfo." FORCE) endif() endif() function(option_if_not_defined name description default) if(NOT DEFINED ${name}) option(${name} ${description} ${default}) endif() endfunction() option_if_not_defined(BUILD_SHARED_LIBS "Build and use shared libraries" TRUE) option_if_not_defined(UHDR_BUILD_EXAMPLES "Build sample application " TRUE) option_if_not_defined(UHDR_BUILD_TESTS "Build unit tests " FALSE) option_if_not_defined(UHDR_BUILD_BENCHMARK "Build benchmark tests " FALSE) option_if_not_defined(UHDR_BUILD_FUZZERS "Build fuzz test applications " FALSE) option_if_not_defined(UHDR_BUILD_DEPS "Build deps and not use pre-installed packages " FALSE) option_if_not_defined(UHDR_BUILD_JAVA "Build JNI wrapper and Java front-end classes " FALSE) option_if_not_defined(UHDR_BUILD_PACKAGING "Build distribution packages using CPack " FALSE) option_if_not_defined(UHDR_ENABLE_LOGS "Build with verbose logging " FALSE) option_if_not_defined(UHDR_ENABLE_INSTALL "Enable install and uninstall targets for libuhdr package " TRUE) option_if_not_defined(UHDR_ENABLE_INTRINSICS "Build with SIMD acceleration " TRUE) option_if_not_defined(UHDR_ENABLE_GLES "Build with GPU acceleration " FALSE) option_if_not_defined(UHDR_ENABLE_WERROR "Build with -Werror" FALSE) option_if_not_defined(UHDR_ENABLE_SMPTE2094_50 "Build with SMPTE 2094-50 support" FALSE) option_if_not_defined(UHDR_ENABLE_HEIF "Build with HEIF/AVIF support via libheif" TRUE) # These options effect only encoding process. # Decoding continues to support both iso and xmp irrespective of this configuration. # Also, if both packets are present iso is prioritized over xmp. option_if_not_defined(UHDR_WRITE_XMP "Write gainmap metadata in XMP packet" FALSE) option_if_not_defined(UHDR_WRITE_ISO "Write gainmap metadata in ISO 21496_1 packet" TRUE) # pre-requisites if(UHDR_BUILD_TESTS AND EMSCRIPTEN) message(FATAL_ERROR "Building tests not supported for wasm targets") endif() if(UHDR_BUILD_BENCHMARK AND EMSCRIPTEN) message(FATAL_ERROR "Building benchmarks not supported for wasm targets") endif() # side effects if(CMAKE_CROSSCOMPILING AND UHDR_ENABLE_INSTALL) set(UHDR_ENABLE_INSTALL FALSE) # disable install and uninstall targets during cross compilation. message(STATUS "Install and uninstall targets - Disabled") endif() if(UHDR_BUILD_FUZZERS AND NOT UHDR_BUILD_DEPS) set(UHDR_BUILD_DEPS TRUE) # For fuzz testing its best to build all dependencies from source. # This is to instrument dependency libs as well. message(STATUS "Building dependencies from source - Enabled") endif() if(DEFINED UHDR_SANITIZE_OPTIONS AND NOT UHDR_BUILD_DEPS) set(UHDR_BUILD_DEPS TRUE) # If sanitize options are enabled, its best to build all dependencies from source. # This is to instrument dependency libs as well. message(STATUS "Building dependencies from source - Enabled") endif() if(UHDR_BUILD_DEPS AND UHDR_ENABLE_INSTALL) set(UHDR_ENABLE_INSTALL FALSE) # If dependencies are not chosen from installed packages but are built # from source, its best to disallow system wide installation of # uhdr to avoid possible abi/api conflicts. message(STATUS "Install and uninstall targets - Disabled") endif() if(EMSCRIPTEN AND BUILD_SHARED_LIBS) # platform does not support dynamic linking? set(BUILD_SHARED_LIBS FALSE) message(STATUS "For wasm targets, build and use static libraries") endif() if(MSVC AND (UHDR_BUILD_DEPS OR UHDR_BUILD_TESTS OR UHDR_BUILD_BENCHMARK) AND BUILD_SHARED_LIBS) # ExternalProject_Add() for jpeg, gtest, google benchmarks is configured to build static libraries # to avoid msvc run-time mismatches (/MD vs /MT) during linking, explicitly set BUILD_SHARED_LIBS to STATIC set(BUILD_SHARED_LIBS FALSE) message(STATUS "When building dependencies from source, on MSVC, build and use static libraries") endif() ########################################################### # Compile flags ########################################################### set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) if(BUILD_SHARED_LIBS) set(CMAKE_POSITION_INDEPENDENT_CODE ON) set(CMAKE_CXX_VISIBILITY_PRESET hidden) set(CMAKE_VISIBILITY_INLINES_HIDDEN YES) set(UHDR_ENABLE_STATIC_LINKING OFF) set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>DLL") add_compile_options(-DUHDR_BUILDING_SHARED_LIBRARY) else() if(WIN32) set(CMAKE_FIND_LIBRARY_SUFFIXES -static.lib .lib .a) else() set(CMAKE_FIND_LIBRARY_SUFFIXES .a) endif() if(APPLE) # Apple does not support statically linking an entire executable message(STATUS "Pure static executable may not possible, disabling '-static' option") set(UHDR_ENABLE_STATIC_LINKING OFF) elseif(DEFINED UHDR_SANITIZE_OPTIONS OR UHDR_BUILD_FUZZERS) # Sanitizer libraries could be only DSO's, static linking an entire executable not possible message(STATUS "Pure static executable may not possible, disabling '-static' option") set(UHDR_ENABLE_STATIC_LINKING OFF) elseif(MSVC AND DEFINED BUILD_FOR_WINUI AND BUILD_FOR_WINUI) # WinUI builds use /MD (DLL CRT), keep the global setting from parent project set(UHDR_ENABLE_STATIC_LINKING OFF) elseif(MSVC) set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") set(UHDR_ENABLE_STATIC_LINKING ON) else() set(UHDR_ENABLE_STATIC_LINKING ON) endif() endif() if(UHDR_ENABLE_LOGS) add_compile_options(-DLOG_NDEBUG) endif() if(UHDR_ENABLE_INTRINSICS) add_compile_options(-DUHDR_ENABLE_INTRINSICS) endif() if(UHDR_WRITE_XMP) add_compile_options(-DUHDR_WRITE_XMP) endif() if(UHDR_WRITE_ISO) add_compile_options(-DUHDR_WRITE_ISO) endif() include(CheckCXXCompilerFlag) function(CheckCompilerOption opt res) set(CMAKE_REQUIRED_FLAGS ${opt}) check_cxx_compiler_flag(${opt} ${res}) unset(CMAKE_REQUIRED_FLAGS) if(NOT ${res}) message(FATAL_ERROR "Unsupported compiler option(s) ${opt}") endif() endfunction(CheckCompilerOption) if(DEFINED UHDR_SANITIZE_OPTIONS) CheckCompilerOption("-fsanitize=${UHDR_SANITIZE_OPTIONS}" SUPPORTS_SAN_OPTIONS) add_compile_options(-fsanitize=${UHDR_SANITIZE_OPTIONS}) add_link_options(-fsanitize=${UHDR_SANITIZE_OPTIONS}) endif() if(UHDR_BUILD_FUZZERS) CheckCompilerOption("-fsanitize=fuzzer-no-link" fuzz) add_compile_options(-fsanitize=fuzzer-no-link) endif() set(UHDR_WERROR_FLAGS "") if(MSVC) add_definitions(-D_CRT_SECURE_NO_WARNINGS) # Disable specific warnings # TODO: None of these should be disabled, but for now, for a warning-free msvc build these are # added. fix the warnings and remove these filters add_compile_options(/wd4244) # conversion from 'type1' to 'type2', possible loss of data add_compile_options(/wd4267) # conversion from 'size_t' to 'type' possible loss of data add_compile_options(/wd4305) # truncation from 'double' to 'float' add_compile_options(/wd4838) # conversion from 'type1' to 'type2' requires a narrowing conversion add_compile_options(/wd26812) # Prefer enum class over enum elseif(EMSCRIPTEN) if(NOT UHDR_BUILD_DEPS) include(CheckCSourceCompiles) set(CMAKE_REQUIRED_FLAGS "--use-port=libjpeg") set(CMAKE_REQUIRED_LINK_OPTIONS "--use-port=libjpeg") check_c_source_compiles([=[ #include #include int main(void) { struct jpeg_compress_struct cinfo; struct jpeg_error_mgr jerr; cinfo.err=jpeg_std_error(&jerr); jpeg_create_compress(&cinfo); jpeg_destroy_compress(&cinfo); return 0; } ]=] HAVE_JPEG) if(NOT HAVE_JPEG) message(FATAL_ERROR "Could NOT compile with --use-port=libjpeg, resolve this \ or try 'cmake -DUHDR_BUILD_DEPS=1'") endif() endif() else() add_compile_options(-ffunction-sections) add_compile_options(-fdata-sections) add_compile_options(-fomit-frame-pointer) add_compile_options(-ffp-contract=fast) if(ARCH STREQUAL "i386") add_compile_options(-m32) add_compile_options(-march=i386) add_compile_options(-mtune=generic) elseif(ARCH STREQUAL "amd64") add_compile_options(-m64) add_compile_options(-march=x86-64) add_compile_options(-mtune=generic) elseif(ARCH STREQUAL "arm") add_compile_options(-march=armv7-a) add_compile_options(-marm) if(NOT ANDROID_ABI) add_compile_options(-mfloat-abi=hard) endif() add_compile_options(-mfpu=neon-vfpv3) add_compile_options(-fno-lax-vector-conversions) elseif(ARCH STREQUAL "aarch64") add_compile_options(-march=armv8-a) add_compile_options(-fno-lax-vector-conversions) elseif(ARCH STREQUAL "riscv64") add_compile_options(-march=rv64gc) add_compile_options(-mabi=lp64d) elseif(ARCH STREQUAL "riscv32") add_compile_options(-march=rv32gc) add_compile_options(-mabi=ilp32d) elseif(ARCH STREQUAL "loong64") add_compile_options(-march=loongarch64) add_compile_options(-mabi=lp64d) endif() if(UHDR_ENABLE_WERROR) CheckCompilerOption("-Werror" SUPPORTS_WERROR) set(UHDR_WERROR_FLAGS "-Werror") endif() endif() ########################################################### # Utils ########################################################### # copied from https://github.com/google/shaderc/blob/main/cmake/utils.cmake macro(get_transitive_static_libs target out_list) if(TARGET ${target}) get_target_property(target_type ${target} TYPE) if(target_type STREQUAL "STATIC_LIBRARY") list(INSERT ${out_list} 0 ${target}) get_target_property(libs ${target} LINK_LIBRARIES) if(libs) foreach(lib ${libs}) get_transitive_static_libs(${lib} ${out_list}) endforeach() endif() endif() endif() endmacro() # combine a list of static libraries in to a single library function(combine_static_libs target output_target) set(all_libs_list "") get_transitive_static_libs(${target} all_libs_list) foreach(lib IN LISTS all_libs_list) target_sources(${output_target} PRIVATE $) endforeach() endfunction() ########################################################### # Dependencies ########################################################### list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") if(${CMAKE_SYSTEM_NAME} MATCHES "Android") if (UHDR_ENABLE_LOGS) find_library(log-lib log QUIET) if(NOT log-lib) message(FATAL_ERROR "Could NOT find log library, retry after installing \ log library at sysroot or try 'cmake -DUHDR_ENABLE_LOGS=0'") else() message(STATUS "Found log-lib: ${log-lib}") endif() endif() endif() # Threads set(CMAKE_THREAD_PREFER_PTHREAD ON) set(THREADS_PREFER_PTHREAD_FLAG ON) find_package(Threads REQUIRED) include(ExternalProject) if(UHDR_ENABLE_SMPTE2094_50) if(UHDR_BUILD_DEPS) add_compile_options(-DUHDR_ENABLE_SMPTE2094_50) include(FetchContent) FetchContent_Declare( libsmpte2094_50 GIT_REPOSITORY https://github.com/webmproject/libsmpte2094-50.git GIT_TAG v0.1.4) set(OLD_BUILD_SHARED_LIBS ${BUILD_SHARED_LIBS}) set(BUILD_SHARED_LIBS OFF) FetchContent_MakeAvailable(libsmpte2094_50) set(BUILD_SHARED_LIBS ${OLD_BUILD_SHARED_LIBS}) else() message(WARNING "libsmpte2094-50 package not available") set(UHDR_ENABLE_SMPTE2094_50 OFF) endif() endif() get_directory_property(UHDR_COMPILE_FLAGS COMPILE_OPTIONS) string (REPLACE ";" " " UHDR_COMPILE_FLAGS_STR "${UHDR_COMPILE_FLAGS}") set(UHDR_CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${UHDR_COMPILE_FLAGS_STR}") set(UHDR_CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${UHDR_COMPILE_FLAGS_STR}") set(UHDR_CMAKE_ARGS -DCMAKE_C_COMPILER=${CMAKE_C_COMPILER}) list(APPEND UHDR_CMAKE_ARGS -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}) list(APPEND UHDR_CMAKE_ARGS -DCMAKE_C_FLAGS=${UHDR_CMAKE_C_FLAGS}) list(APPEND UHDR_CMAKE_ARGS -DCMAKE_C_FLAGS_DEBUG=${CMAKE_C_FLAGS_DEBUG}) list(APPEND UHDR_CMAKE_ARGS -DCMAKE_C_FLAGS_RELEASE=${CMAKE_C_FLAGS_RELEASE}) list(APPEND UHDR_CMAKE_ARGS -DCMAKE_C_FLAGS_MINSIZEREL=${CMAKE_C_FLAGS_MINSIZEREL}) list(APPEND UHDR_CMAKE_ARGS -DCMAKE_C_FLAGS_RELWITHDEBINFO=${CMAKE_C_FLAGS_RELWITHDEBINFO}) list(APPEND UHDR_CMAKE_ARGS -DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}) list(APPEND UHDR_CMAKE_ARGS -DCMAKE_CXX_FLAGS=${UHDR_CMAKE_CXX_FLAGS}) list(APPEND UHDR_CMAKE_ARGS -DCMAKE_CXX_FLAGS_DEBUG=${CMAKE_CXX_FLAGS_DEBUG}) list(APPEND UHDR_CMAKE_ARGS -DCMAKE_CXX_FLAGS_RELEASE=${CMAKE_CXX_FLAGS_RELEASE}) list(APPEND UHDR_CMAKE_ARGS -DCMAKE_CXX_FLAGS_MINSIZEREL=${CMAKE_CXX_FLAGS_MINSIZEREL}) list(APPEND UHDR_CMAKE_ARGS -DCMAKE_CXX_FLAGS_RELWITHDEBINFO=${CMAKE_CXX_FLAGS_RELWITHDEBINFO}) list(APPEND UHDR_CMAKE_ARGS -DCMAKE_MSVC_RUNTIME_LIBRARY=${CMAKE_MSVC_RUNTIME_LIBRARY}) # Build and use static libraries of dependencies list(APPEND UHDR_CMAKE_ARGS -DBUILD_SHARED_LIBS=OFF) if(BUILD_SHARED_LIBS) list(APPEND UHDR_CMAKE_ARGS -DCMAKE_POSITION_INDEPENDENT_CODE=ON) endif() if(MSVC AND UHDR_BUILD_TESTS) list(APPEND UHDR_CMAKE_ARGS "-Dgtest_force_shared_crt=ON") endif() if(DEFINED CMAKE_TOOLCHAIN_FILE) list(APPEND UHDR_CMAKE_ARGS -DCMAKE_TOOLCHAIN_FILE:FILEPATH=${CMAKE_TOOLCHAIN_FILE}) endif() if(DEFINED ANDROID_PLATFORM) list(APPEND UHDR_CMAKE_ARGS -DANDROID_PLATFORM=${ANDROID_PLATFORM}) endif() if(DEFINED ANDROID_ABI) list(APPEND UHDR_CMAKE_ARGS -DANDROID_ABI=${ANDROID_ABI}) endif() if(DEFINED UHDR_ANDROID_NDK_PATH) list(APPEND UHDR_CMAKE_ARGS -DUHDR_ANDROID_NDK_PATH=${UHDR_ANDROID_NDK_PATH}) endif() if(APPLE) if(DEFINED CMAKE_OSX_ARCHITECTURES AND NOT "${CMAKE_OSX_ARCHITECTURES}" STREQUAL "") list(APPEND UHDR_CMAKE_ARGS "-DCMAKE_OSX_ARCHITECTURES=${CMAKE_OSX_ARCHITECTURES}") endif() if(DEFINED CMAKE_OSX_DEPLOYMENT_TARGET AND NOT "${CMAKE_OSX_DEPLOYMENT_TARGET}" STREQUAL "") list(APPEND UHDR_CMAKE_ARGS "-DCMAKE_OSX_DEPLOYMENT_TARGET=${CMAKE_OSX_DEPLOYMENT_TARGET}") endif() if(DEFINED CMAKE_OSX_SYSROOT AND NOT "${CMAKE_OSX_SYSROOT}" STREQUAL "") list(APPEND UHDR_CMAKE_ARGS "-DCMAKE_OSX_SYSROOT=${CMAKE_OSX_SYSROOT}") endif() endif() # opengl es libraries if(UHDR_ENABLE_GLES) find_package(EGL QUIET) if(EGL_FOUND) message(STATUS "Found EGL: ${EGL_LIBRARIES}") find_package(OpenGLES3 QUIET) if(OpenGLES3_FOUND) message(STATUS "Found GLESv3: ${OPENGLES3_LIBRARIES} (API version \"${OpenGLES3_API_VERSION}\")") else() message(STATUS "Could NOT find GLESv3") endif() else() message(STATUS "Could NOT find EGL") endif() if(EGL_FOUND AND OpenGLES3_FOUND) add_compile_options(-DUHDR_ENABLE_GLES) string(FIND "${OPENGLES3_LIBRARIES}" "GLESv3" result) if(result GREATER -1) set(UHDR_GL_DEPS "-lEGL -lGLESv3") else() set(UHDR_GL_DEPS "-lEGL -lGLESv2") endif() else() set(UHDR_ENABLE_GLES FALSE) endif() endif() # libjpeg-turbo if(NOT UHDR_BUILD_DEPS) find_package(JPEG QUIET) if(NOT JPEG_FOUND) message(FATAL_ERROR "Could NOT find JPEG (missing: JPEG_LIBRARIES JPEG_INCLUDE_DIRS),\ retry after installing JPEG library at sysroot or try 'cmake -DUHDR_BUILD_DEPS=1'") else() message(STATUS "Found JPEG: ${JPEG_LIBRARIES} (found version \"${JPEG_VERSION}\")") endif() endif() if(NOT JPEG_FOUND) set(JPEGTURBO_TARGET_NAME turbojpeg) set(JPEGTURBO_PREFIX_DIR ${CMAKE_CURRENT_BINARY_DIR}/${JPEGTURBO_TARGET_NAME}) set(JPEGTURBO_SOURCE_DIR ${THIRD_PARTY_DIR}/${JPEGTURBO_TARGET_NAME}) set(JPEGTURBO_BINARY_DIR ${JPEGTURBO_PREFIX_DIR}/src/${JPEGTURBO_TARGET_NAME}-build) set(JPEG_INCLUDE_DIRS ${JPEGTURBO_SOURCE_DIR}/src ${JPEGTURBO_BINARY_DIR}) if(MSVC) set(JPEG_LIB ${CMAKE_STATIC_LIBRARY_PREFIX}jpeg-static${CMAKE_STATIC_LIBRARY_SUFFIX}) else() set(JPEG_LIB ${CMAKE_STATIC_LIBRARY_PREFIX}jpeg${CMAKE_STATIC_LIBRARY_SUFFIX}) endif() if(IS_MULTI) set(JPEG_LIB_PREFIX ${JPEGTURBO_BINARY_DIR}/$/) else() set(JPEG_LIB_PREFIX ${JPEGTURBO_BINARY_DIR}/) endif() set(JPEG_LIBRARIES ${JPEG_LIB_PREFIX}${JPEG_LIB}) if(EMSCRIPTEN) ExternalProject_Add(${JPEGTURBO_TARGET_NAME} GIT_REPOSITORY https://github.com/libjpeg-turbo/libjpeg-turbo.git GIT_TAG 3.1.0 PREFIX ${JPEGTURBO_PREFIX_DIR} SOURCE_DIR ${JPEGTURBO_SOURCE_DIR} BINARY_DIR ${JPEGTURBO_BINARY_DIR} CONFIGURE_COMMAND emcmake cmake ${JPEGTURBO_SOURCE_DIR} -DENABLE_SHARED=0 -DWITH_SIMD=0 BUILD_COMMAND ${CMAKE_COMMAND} --build --config $ --target jpeg-static BUILD_BYPRODUCTS ${JPEG_LIBRARIES} INSTALL_COMMAND "" ) else() ExternalProject_Add(${JPEGTURBO_TARGET_NAME} GIT_REPOSITORY https://github.com/libjpeg-turbo/libjpeg-turbo.git GIT_TAG 3.1.0 PREFIX ${JPEGTURBO_PREFIX_DIR} SOURCE_DIR ${JPEGTURBO_SOURCE_DIR} BINARY_DIR ${JPEGTURBO_BINARY_DIR} BUILD_COMMAND ${CMAKE_COMMAND} --build --config $ --target jpeg-static CMAKE_ARGS ${UHDR_CMAKE_ARGS} -DENABLE_SHARED=0 -DWITH_CRT_DLL=ON BUILD_BYPRODUCTS ${JPEG_LIBRARIES} INSTALL_COMMAND "" ) endif() endif() if(UHDR_BUILD_JAVA) # build jni and java util classes find_package(Java REQUIRED) if(${CMAKE_SYSTEM_NAME} MATCHES "Android") find_package(JNI QUIET) if(NOT JAVA_INCLUDE_PATH) message(FATAL_ERROR "Could NOT find JNI Component") else() message(STATUS "Found JNI Component") endif() set(UHDR_JNI_INCLUDE_PATH ${JAVA_INCLUDE_PATH}) else() find_package(JNI REQUIRED) set(UHDR_JNI_INCLUDE_PATH ${JNI_INCLUDE_DIRS}) endif() endif() if(UHDR_BUILD_TESTS) # gtest and gmock set(GTEST_TARGET_NAME googletest) set(GTEST_PREFIX_DIR ${CMAKE_CURRENT_BINARY_DIR}/${GTEST_TARGET_NAME}) set(GTEST_SOURCE_DIR ${THIRD_PARTY_DIR}/${GTEST_TARGET_NAME}) set(GTEST_BINARY_DIR ${GTEST_PREFIX_DIR}/src/${GTEST_TARGET_NAME}-build) set(GTEST_INCLUDE_DIRS ${GTEST_SOURCE_DIR}/googletest/include ${GTEST_SOURCE_DIR}/googlemock/include) set(GTEST_LIB ${CMAKE_STATIC_LIBRARY_PREFIX}gtest${CMAKE_STATIC_LIBRARY_SUFFIX}) set(GTEST_LIB_MAIN ${CMAKE_STATIC_LIBRARY_PREFIX}gtest_main${CMAKE_STATIC_LIBRARY_SUFFIX}) if(IS_MULTI) set(GTEST_LIB_PREFIX ${GTEST_BINARY_DIR}/lib/$/) else() set(GTEST_LIB_PREFIX ${GTEST_BINARY_DIR}/lib/) endif() set(GTEST_BOTH_LIBRARIES ${GTEST_LIB_PREFIX}${GTEST_LIB} ${GTEST_LIB_PREFIX}${GTEST_LIB_MAIN}) ExternalProject_Add(${GTEST_TARGET_NAME} GIT_REPOSITORY https://github.com/google/googletest GIT_TAG v1.14.0 PREFIX ${GTEST_PREFIX_DIR} SOURCE_DIR ${GTEST_SOURCE_DIR} BINARY_DIR ${GTEST_BINARY_DIR} CMAKE_ARGS ${UHDR_CMAKE_ARGS} BUILD_BYPRODUCTS ${GTEST_BOTH_LIBRARIES} INSTALL_COMMAND "" ) endif() if(UHDR_BUILD_BENCHMARK) # benchmark set(BM_TARGET_NAME benchmark) set(BM_PREFIX_DIR ${CMAKE_CURRENT_BINARY_DIR}/${BM_TARGET_NAME}) set(BM_SOURCE_DIR ${THIRD_PARTY_DIR}/${BM_TARGET_NAME}) set(BM_BINARY_DIR ${BM_PREFIX_DIR}/src/${BM_TARGET_NAME}-build) set(BENCHMARK_INCLUDE_DIR ${BM_SOURCE_DIR}/include) set(BM_LIB ${CMAKE_STATIC_LIBRARY_PREFIX}benchmark${CMAKE_STATIC_LIBRARY_SUFFIX}) set(BM_LIB_MAIN ${CMAKE_STATIC_LIBRARY_PREFIX}benchmark_main${CMAKE_STATIC_LIBRARY_SUFFIX}) if(IS_MULTI) set(BM_LIB_PREFIX ${BM_BINARY_DIR}/src/$/) else() set(BM_LIB_PREFIX ${BM_BINARY_DIR}/src/) endif() set(BENCHMARK_LIBRARIES ${BM_LIB_PREFIX}${BM_LIB} ${BM_LIB_PREFIX}${BM_LIB_MAIN}) ExternalProject_Add(${BM_TARGET_NAME} GIT_REPOSITORY https://github.com/google/benchmark.git GIT_TAG v1.8.3 PREFIX ${BM_PREFIX_DIR} SOURCE_DIR ${BM_SOURCE_DIR} BINARY_DIR ${BM_BINARY_DIR} CMAKE_ARGS ${UHDR_CMAKE_ARGS} -DBENCHMARK_ENABLE_TESTING=OFF -DBENCHMARK_DOWNLOAD_DEPENDENCIES=OFF BUILD_BYPRODUCTS ${BENCHMARK_LIBRARIES} INSTALL_COMMAND "" ) endif() set_property(DIRECTORY PROPERTY ADDITIONAL_MAKE_CLEAN_FILES ${JPEGTURBO_BINARY_DIR} ${GTEST_BINARY_DIR} ${BM_BINARY_DIR}) ########################################################### # File Lists ########################################################### file(GLOB UHDR_CORE_SRCS_LIST "${SOURCE_DIR}/src/*.cpp") if(UHDR_ENABLE_INTRINSICS) if(ARCH STREQUAL "arm" OR ARCH STREQUAL "aarch64") file(GLOB UHDR_CORE_NEON_SRCS_LIST "${SOURCE_DIR}/src/dsp/arm/*.cpp") list(APPEND UHDR_CORE_SRCS_LIST ${UHDR_CORE_NEON_SRCS_LIST}) endif() endif() if(UHDR_ENABLE_GLES) file(GLOB UHDR_CORE_GLES_SRCS_LIST "${SOURCE_DIR}/src/gpu/*.cpp") list(APPEND UHDR_CORE_SRCS_LIST ${UHDR_CORE_GLES_SRCS_LIST}) endif() if(UHDR_BUILD_JAVA) file(GLOB UHDR_JNI_SRCS_LIST "${JAVA_DIR}/jni/*.cpp") file(GLOB UHDR_JAVA_SRCS_LIST "${JAVA_DIR}/com/google/media/codecs/ultrahdr/*.java") file(GLOB UHDR_APP_SRC "${JAVA_DIR}/UltraHdrApp.java") endif() file(GLOB UHDR_TEST_SRCS_LIST "${TESTS_DIR}/*.cpp") file(GLOB UHDR_BM_SRCS_LIST "${BENCHMARK_DIR}/*.cpp") file(GLOB IMAGE_IO_SRCS_LIST "${THIRD_PARTY_DIR}/image_io/src/**/*.cc") set(PRIVATE_INCLUDE_DIR ${SOURCE_DIR}/include/ ${JPEG_INCLUDE_DIRS}) set(PRIVATE_LINK_LIBS ${JPEG_LIBRARIES} Threads::Threads) if(UHDR_ENABLE_SMPTE2094_50) list(APPEND PRIVATE_LINK_LIBS smpte2094_50_utils) endif() if(UHDR_ENABLE_GLES) list(APPEND PRIVATE_INCLUDE_DIR ${EGL_INCLUDE_DIRS} ${OPENGLES3_INCLUDE_DIRS}) list(APPEND PRIVATE_LINK_LIBS ${EGL_LIBRARIES} ${OPENGLES3_LIBRARIES}) endif() if(UHDR_ENABLE_HEIF) if(NOT UHDR_BUILD_DEPS) find_package(libheif QUIET) if(LIBHEIF_FOUND AND NOT LIBHEIF_HAS_GAIN_MAP) message(WARNING "System libheif found but lacks ISO 21496-1 gain map API (PR 1503). " "Switching to bundled dependency build.") set(LIBHEIF_FOUND FALSE) endif() endif() if(NOT LIBHEIF_FOUND AND UHDR_BUILD_DEPS) find_package(Git QUIET) set(LIBHEIF_TARGET_NAME libheif) set(LIBHEIF_PREFIX_DIR ${CMAKE_CURRENT_BINARY_DIR}/${LIBHEIF_TARGET_NAME}) set(LIBHEIF_SOURCE_DIR ${THIRD_PARTY_DIR}/${LIBHEIF_TARGET_NAME}) set(LIBHEIF_BINARY_DIR ${LIBHEIF_PREFIX_DIR}/src/${LIBHEIF_TARGET_NAME}-build) file(MAKE_DIRECTORY "${LIBHEIF_SOURCE_DIR}/libheif/api") file(MAKE_DIRECTORY "${LIBHEIF_SOURCE_DIR}/libheif/api/libheif") file(MAKE_DIRECTORY "${LIBHEIF_BINARY_DIR}") file(MAKE_DIRECTORY "${LIBHEIF_BINARY_DIR}/libheif") set(LIBHEIF_INCLUDE_DIRS ${LIBHEIF_SOURCE_DIR}/libheif/api ${LIBHEIF_SOURCE_DIR}/libheif/api/libheif ${LIBHEIF_BINARY_DIR} ${LIBHEIF_BINARY_DIR}/libheif) if(IS_MULTI) if(WIN32 AND NOT MINGW) set(LIBHEIF_LIB_PREFIX ${LIBHEIF_BINARY_DIR}/libheif/Release/) else() set(LIBHEIF_LIB_PREFIX ${LIBHEIF_BINARY_DIR}/libheif/$/) endif() else() set(LIBHEIF_LIB_PREFIX ${LIBHEIF_BINARY_DIR}/libheif/) endif() if(WIN32 AND NOT MINGW) set(LIBHEIF_LIB heif.lib) else() set(LIBHEIF_LIB ${CMAKE_STATIC_LIBRARY_PREFIX}heif${CMAKE_STATIC_LIBRARY_SUFFIX}) endif() set(LIBHEIF_LIBRARIES ${LIBHEIF_LIB_PREFIX}${LIBHEIF_LIB}) if(GIT_FOUND) set(LIBHEIF_PATCH_CMD ${GIT_EXECUTABLE} checkout 4a3f74bc593ebfc29becc1ed5dd0a61cc66d40e1 -- . COMMAND ${GIT_EXECUTABLE} apply --ignore-space-change --whitespace=nowarn ${CMAKE_CURRENT_SOURCE_DIR}/cmake/patches/libheif_pr1503.patch) else() set(LIBHEIF_PATCH_CMD git checkout 4a3f74bc593ebfc29becc1ed5dd0a61cc66d40e1 -- . COMMAND git apply --ignore-space-change --whitespace=nowarn ${CMAKE_CURRENT_SOURCE_DIR}/cmake/patches/libheif_pr1503.patch) endif() ExternalProject_Add(${LIBHEIF_TARGET_NAME} GIT_REPOSITORY https://github.com/strukturag/libheif.git GIT_TAG 4a3f74bc593ebfc29becc1ed5dd0a61cc66d40e1 PATCH_COMMAND ${LIBHEIF_PATCH_CMD} PREFIX ${LIBHEIF_PREFIX_DIR} SOURCE_DIR ${LIBHEIF_SOURCE_DIR} BINARY_DIR ${LIBHEIF_BINARY_DIR} BUILD_COMMAND ${CMAKE_COMMAND} --build --config $ --target heif INSTALL_COMMAND "" CMAKE_ARGS ${UHDR_CMAKE_ARGS} -DBUILD_SHARED_LIBS=OFF -DCMAKE_DISABLE_FIND_PACKAGE_Doxygen=ON -DWITH_DOCS=OFF -DENABLE_PLUGIN_LOADING=ON -DWITH_EXAMPLES=OFF -DWITH_GDK_PIXBUF=OFF -DWITH_LIBSHARPYUV=OFF -DBUILD_TESTING=OFF -DWITH_EXPERIMENTAL_GAIN_MAP=ON -DWITH_HEIFIO=OFF -DWITH_JPEG_DECODER=OFF -DWITH_JPEG_ENCODER=OFF -DWITH_OpenJPEG=OFF -DWITH_OpenJPH=OFF -DWITH_LIBPNG=OFF -DWITH_LIBTIFF=OFF -DWITH_FFMPEG_DECODER=OFF BUILD_BYPRODUCTS ${LIBHEIF_LIBRARIES} ) if(NOT TARGET libheif::heif) add_library(libheif::heif UNKNOWN IMPORTED) add_dependencies(libheif::heif ${LIBHEIF_TARGET_NAME}) set_target_properties(libheif::heif PROPERTIES IMPORTED_LOCATION "${LIBHEIF_LIBRARIES}" INTERFACE_INCLUDE_DIRECTORIES "${LIBHEIF_INCLUDE_DIRS}" INTERFACE_COMPILE_DEFINITIONS "LIBHEIF_STATIC_BUILD;WITH_EXPERIMENTAL_GAIN_MAP=1" INTERFACE_LINK_LIBRARIES "${CMAKE_DL_LIBS}" ) endif() set(LIBHEIF_FOUND TRUE) endif() if(LIBHEIF_FOUND) message(STATUS "Found libheif: ${LIBHEIF_LIBRARIES}") add_compile_options(-DUHDR_ENABLE_HEIF) list(APPEND PRIVATE_LINK_LIBS libheif::heif) else() message(WARNING "libheif not found. Building without HEIF/AVIF support.") set(UHDR_ENABLE_HEIF FALSE) endif() endif() ########################################################### # Targets ########################################################### set(IMAGEIO_TARGET_NAME image_io) add_library(${IMAGEIO_TARGET_NAME} STATIC ${IMAGE_IO_SRCS_LIST}) target_include_directories(${IMAGEIO_TARGET_NAME} PRIVATE "${THIRD_PARTY_DIR}/image_io/includes" "${THIRD_PARTY_DIR}/image_io/src/modp_b64" "${THIRD_PARTY_DIR}/image_io/src/modp_b64/modp_b64") set(UHDR_CORE_LIB_NAME core) add_library(${UHDR_CORE_LIB_NAME} STATIC ${UHDR_CORE_SRCS_LIST}) target_compile_options(${UHDR_CORE_LIB_NAME} PRIVATE ${UHDR_WERROR_FLAGS}) if(NOT JPEG_FOUND) add_dependencies(${UHDR_CORE_LIB_NAME} ${JPEGTURBO_TARGET_NAME}) endif() if(TARGET libheif) add_dependencies(${UHDR_CORE_LIB_NAME} ${LIBHEIF_TARGET_NAME}) endif() if(NOT MSVC) target_compile_options(${UHDR_CORE_LIB_NAME} PRIVATE -Wall -Wextra -Wshadow) endif() if(DEFINED UHDR_MAX_DIMENSION) target_compile_options(${UHDR_CORE_LIB_NAME} PRIVATE -DUHDR_MAX_DIMENSION=${UHDR_MAX_DIMENSION}) endif() target_include_directories(${UHDR_CORE_LIB_NAME} PRIVATE ${PRIVATE_INCLUDE_DIR} "${THIRD_PARTY_DIR}/image_io/includes/" ) target_include_directories(${UHDR_CORE_LIB_NAME} PUBLIC ${EXPORT_INCLUDE_DIR}) if(${CMAKE_SYSTEM_NAME} MATCHES "Android") target_link_libraries(${UHDR_CORE_LIB_NAME} PUBLIC ${log-lib}) endif() target_link_libraries(${UHDR_CORE_LIB_NAME} PRIVATE ${PRIVATE_LINK_LIBS} ${IMAGEIO_TARGET_NAME}) if(UHDR_BUILD_EXAMPLES) set(UHDR_SAMPLE_APP ultrahdr_app) add_executable(${UHDR_SAMPLE_APP} "${EXAMPLES_DIR}/ultrahdr_app.cpp") add_dependencies(${UHDR_SAMPLE_APP} ${UHDR_CORE_LIB_NAME}) target_compile_options(${UHDR_SAMPLE_APP} PRIVATE ${UHDR_WERROR_FLAGS}) if(UHDR_BUILD_FUZZERS) target_link_options(${UHDR_SAMPLE_APP} PRIVATE -fsanitize=fuzzer-no-link) endif() if(UHDR_ENABLE_STATIC_LINKING AND NOT MSVC) # "MSVC_RUNTIME_LIBRARY is set, no need for '-static' option" target_link_options(${UHDR_SAMPLE_APP} PRIVATE -static) endif() target_link_libraries(${UHDR_SAMPLE_APP} PRIVATE ${UHDR_CORE_LIB_NAME}) endif() if(UHDR_BUILD_TESTS OR UHDR_BUILD_BENCHMARK) include(CTest) if(WIN32) file(COPY "${TESTS_DIR}/data/" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/data") else() execute_process(COMMAND "${CMAKE_COMMAND}" -E create_symlink "${TESTS_DIR}/data/" "${CMAKE_CURRENT_BINARY_DIR}/data" RESULT_VARIABLE result ERROR_VARIABLE errorinfo) string(FIND "${errorinfo}" "error" errorstatus) if(result GREATER 0 OR errorstatus GREATER -1) message(FATAL_ERROR "Creating sym link failed with info ${errorinfo}") endif() endif() endif() if(UHDR_BUILD_TESTS) add_executable(ultrahdr_unit_test ${UHDR_TEST_SRCS_LIST}) add_dependencies(ultrahdr_unit_test ${GTEST_TARGET_NAME} ${UHDR_CORE_LIB_NAME}) if(TARGET libheif) add_dependencies(ultrahdr_unit_test ${LIBHEIF_TARGET_NAME}) endif() target_compile_options(ultrahdr_unit_test PRIVATE ${UHDR_WERROR_FLAGS}) target_include_directories(ultrahdr_unit_test PRIVATE ${PRIVATE_INCLUDE_DIR} ${GTEST_INCLUDE_DIRS} ) if(UHDR_BUILD_FUZZERS) target_link_options(ultrahdr_unit_test PRIVATE -fsanitize=fuzzer-no-link) endif() if(UHDR_ENABLE_SMPTE2094_50) target_link_libraries(ultrahdr_unit_test ${UHDR_CORE_LIB_NAME} ${GTEST_BOTH_LIBRARIES} smpte2094_50_utils ${PRIVATE_LINK_LIBS}) else() target_link_libraries(ultrahdr_unit_test ${UHDR_CORE_LIB_NAME} ${GTEST_BOTH_LIBRARIES} ${PRIVATE_LINK_LIBS}) endif() if(LIBHEIF_LIB_PREFIX) set_target_properties(ultrahdr_unit_test PROPERTIES BUILD_RPATH "${LIBHEIF_LIB_PREFIX}") endif() add_test(NAME UHDRUnitTests COMMAND ultrahdr_unit_test) endif() if(UHDR_BUILD_BENCHMARK) add_executable(ultrahdr_bm ${UHDR_BM_SRCS_LIST}) add_dependencies(ultrahdr_bm ${BM_TARGET_NAME} ${UHDR_CORE_LIB_NAME}) target_compile_options(ultrahdr_bm PRIVATE ${UHDR_WERROR_FLAGS}) target_compile_options(ultrahdr_bm PRIVATE -DBENCHMARK_STATIC_DEFINE) target_include_directories(ultrahdr_bm PRIVATE ${PRIVATE_INCLUDE_DIR} ${BENCHMARK_INCLUDE_DIR} ) if(UHDR_BUILD_FUZZERS) target_link_options(ultrahdr_bm PRIVATE -fsanitize=fuzzer-no-link) endif() target_link_libraries(ultrahdr_bm ${UHDR_CORE_LIB_NAME} ${BENCHMARK_LIBRARIES}) # We need extra libraries on Windows if(${CMAKE_SYSTEM_NAME} MATCHES "Windows") target_link_libraries(ultrahdr_bm shlwapi) endif() set(RES_FILE "${TESTS_DIR}/data/UltrahdrBenchmarkTestRes-1.2.zip") set(RES_FILE_MD5SUM "14eac767ef7252051cc5658c4ad776d9") set(GET_RES_FILE TRUE) if(EXISTS ${RES_FILE}) file(MD5 ${RES_FILE} CURR_MD5_SUM) if(CURR_MD5_SUM STREQUAL RES_FILE_MD5SUM) message("Zip File already exists: " ${RES_FILE}) set(GET_RES_FILE FALSE) else() file(REMOVE "${RES_FILE}") endif() endif() if(GET_RES_FILE) message("-- Downloading benchmark test resources") set(RES_URL "https://storage.googleapis.com/android_media/external/libultrahdr/benchmark/UltrahdrBenchmarkTestRes-1.2.zip") file(DOWNLOAD ${RES_URL} ${RES_FILE} STATUS result EXPECTED_MD5 ${RES_FILE_MD5SUM}) list(GET result 0 retval) if(retval) file(REMOVE "${RES_FILE}") list(GET result 0 errcode) list(GET result 1 info) message(FATAL_ERROR "Error downloading ${RES_URL}: ${info} (${errcode})") endif() endif() message("-- Extracting benchmark test resources") execute_process(COMMAND "${CMAKE_COMMAND}" -E tar xf "${RES_FILE}" WORKING_DIRECTORY "${TESTS_DIR}/data/" RESULT_VARIABLE result ERROR_VARIABLE errorinfo) string(FIND "${errorinfo}" "error" errorstatus) if(result GREATER 0 OR errorstatus GREATER -1) message(FATAL_ERROR "Extracting benchmark test resources failed with info ${errorinfo}") endif() endif() if(UHDR_BUILD_FUZZERS) add_executable(ultrahdr_enc_fuzzer ${FUZZERS_DIR}/ultrahdr_enc_fuzzer.cpp) add_dependencies(ultrahdr_enc_fuzzer ${UHDR_CORE_LIB_NAME}) target_compile_options(ultrahdr_enc_fuzzer PRIVATE ${UHDR_WERROR_FLAGS}) target_include_directories(ultrahdr_enc_fuzzer PRIVATE ${PRIVATE_INCLUDE_DIR}) if(DEFINED ENV{LIB_FUZZING_ENGINE}) target_link_options(ultrahdr_enc_fuzzer PRIVATE $ENV{LIB_FUZZING_ENGINE}) else() target_link_options(ultrahdr_enc_fuzzer PRIVATE -fsanitize=fuzzer) endif() target_link_libraries(ultrahdr_enc_fuzzer ${UHDR_CORE_LIB_NAME}) add_executable(ultrahdr_dec_fuzzer ${FUZZERS_DIR}/ultrahdr_dec_fuzzer.cpp) add_dependencies(ultrahdr_dec_fuzzer ${UHDR_CORE_LIB_NAME}) target_compile_options(ultrahdr_dec_fuzzer PRIVATE ${UHDR_WERROR_FLAGS}) target_include_directories(ultrahdr_dec_fuzzer PRIVATE ${PRIVATE_INCLUDE_DIR}) if(DEFINED ENV{LIB_FUZZING_ENGINE}) target_link_options(ultrahdr_dec_fuzzer PRIVATE $ENV{LIB_FUZZING_ENGINE}) else() target_link_options(ultrahdr_dec_fuzzer PRIVATE -fsanitize=fuzzer) endif() target_link_libraries(ultrahdr_dec_fuzzer ${UHDR_CORE_LIB_NAME}) add_executable(ultrahdr_legacy_fuzzer ${FUZZERS_DIR}/ultrahdr_legacy_fuzzer.cpp) add_dependencies(ultrahdr_legacy_fuzzer ${UHDR_CORE_LIB_NAME}) target_compile_options(ultrahdr_legacy_fuzzer PRIVATE ${UHDR_WERROR_FLAGS}) target_include_directories(ultrahdr_legacy_fuzzer PRIVATE ${PRIVATE_INCLUDE_DIR}) if(DEFINED ENV{LIB_FUZZING_ENGINE}) target_link_options(ultrahdr_legacy_fuzzer PRIVATE $ENV{LIB_FUZZING_ENGINE}) else() target_link_options(ultrahdr_legacy_fuzzer PRIVATE -fsanitize=fuzzer) endif() target_link_libraries(ultrahdr_legacy_fuzzer ${UHDR_CORE_LIB_NAME}) endif() if(MSVC AND NOT BUILD_SHARED_LIBS) set(UHDR_TARGET_NAME uhdr-static) else() set(UHDR_TARGET_NAME uhdr) endif() add_library(${UHDR_TARGET_NAME}) add_dependencies(${UHDR_TARGET_NAME} ${UHDR_CORE_LIB_NAME}) target_compile_options(${UHDR_TARGET_NAME} PRIVATE ${UHDR_WERROR_FLAGS}) if(${CMAKE_SYSTEM_NAME} MATCHES "Android") target_link_libraries(${UHDR_TARGET_NAME} PRIVATE ${log-lib}) endif() target_link_libraries(${UHDR_TARGET_NAME} PRIVATE ${PRIVATE_LINK_LIBS}) set_target_properties(${UHDR_TARGET_NAME} PROPERTIES PUBLIC_HEADER ultrahdr_api.h) target_include_directories(${UHDR_TARGET_NAME} PUBLIC ${EXPORT_INCLUDE_DIR}) if(BUILD_SHARED_LIBS) # If target is STATIC no need to set VERSION and SOVERSION set_target_properties(${UHDR_TARGET_NAME} PROPERTIES VERSION ${PROJECT_VERSION} SOVERSION ${PROJECT_VERSION_MAJOR}) endif() combine_static_libs(${UHDR_CORE_LIB_NAME} ${UHDR_TARGET_NAME}) # Build static library as well if(BUILD_SHARED_LIBS AND NOT MSVC) set(UHDR_TARGET_NAME_STATIC uhdr-static) add_library(${UHDR_TARGET_NAME_STATIC} STATIC) add_dependencies(${UHDR_TARGET_NAME_STATIC} ${UHDR_CORE_LIB_NAME}) target_compile_options(${UHDR_TARGET_NAME_STATIC} PRIVATE ${UHDR_WERROR_FLAGS}) if(${CMAKE_SYSTEM_NAME} MATCHES "Android") target_link_libraries(${UHDR_TARGET_NAME_STATIC} PRIVATE ${log-lib}) endif() target_link_libraries(${UHDR_TARGET_NAME_STATIC} PRIVATE ${PRIVATE_LINK_LIBS}) target_include_directories(${UHDR_TARGET_NAME_STATIC} PUBLIC ${EXPORT_INCLUDE_DIR}) combine_static_libs(${UHDR_CORE_LIB_NAME} ${UHDR_TARGET_NAME_STATIC}) if(NOT MSVC) set_target_properties(${UHDR_TARGET_NAME_STATIC} PROPERTIES OUTPUT_NAME ${UHDR_TARGET_NAME}) endif() endif() if(UHDR_BUILD_JAVA) include(UseJava) set(UHDR_JNI_TARGET_NAME uhdrjni) add_library(${UHDR_JNI_TARGET_NAME} SHARED ${UHDR_JNI_SRCS_LIST}) add_dependencies(${UHDR_JNI_TARGET_NAME} ${UHDR_TARGET_NAME}) target_include_directories(${UHDR_JNI_TARGET_NAME} PRIVATE ${UHDR_JNI_INCLUDE_PATH} ${EXPORT_INCLUDE_DIR}) target_compile_options(${UHDR_JNI_TARGET_NAME} PRIVATE ${UHDR_WERROR_FLAGS}) target_link_libraries(${UHDR_JNI_TARGET_NAME} PRIVATE ${UHDR_TARGET_NAME}) add_jar(uhdr-java SOURCES ${UHDR_JAVA_SRCS_LIST} ${UHDR_APP_SRC} ENTRY_POINT UltraHdrApp) endif() if(UHDR_ENABLE_INSTALL) # XCODE seems to have issues when the target has only object files as input # we use combine_static_libs to prepare install targets if(NOT XCODE) include(GNUInstallDirs) # pkg-config: libuhdr.pc configure_file("${CMAKE_CURRENT_SOURCE_DIR}/cmake/libuhdr.pc.in" "${CMAKE_CURRENT_BINARY_DIR}/libuhdr.pc" @ONLY NEWLINE_STYLE UNIX) install(FILES "${CMAKE_CURRENT_BINARY_DIR}/libuhdr.pc" DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig") install(TARGETS ${UHDR_TARGET_NAME} ${UHDR_TARGET_NAME_STATIC} ${UHDR_SAMPLE_APP} RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}" PUBLIC_HEADER DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}") if(BUILD_SHARED_LIBS) if(APPLE) install(CODE "message(STATUS \"You may need to add path \" \"${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}/ \" \"to DYLD_FALLBACK_LIBRARY_PATH if binaries are unable to load uhdr library \n\" \"e.g. export DYLD_FALLBACK_LIBRARY_PATH=$DYLD_FALLBACK_LIBRARY_PATH:${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}/ \")") elseif(UNIX) install(CODE "message(STATUS \"You may need to add path \" \"${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}/ \" \"to LD_LIBRARY_PATH if binaries are unable to load uhdr library \n\" \"e.g. export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}/ \")") endif() endif() configure_file("${CMAKE_CURRENT_SOURCE_DIR}/cmake/cmake_uninstall.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake" IMMEDIATE @ONLY) add_custom_target(uninstall COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake) # packaging if(UHDR_BUILD_PACKAGING) include(cmake/package.cmake) include(CPack) endif() endif() endif() libultrahdr-2.0.2/DESCRIPTION000066400000000000000000000014121523721266400155430ustar00rootroot00000000000000libultrahdr is an image compression library that uses gain map technology to store and distribute HDR images. Conceptually on the encoding side, the library accepts SDR and HDR rendition of an image and from these a Gain Map (quotient between the two renditions) is computed. The library then uses backward compatible means to store the base image (SDR), gain map image and some associated metadata. Legacy readers that do not support handling the gain map image and/or metadata, will display the base image. Readers that support the format combine the base image with the gain map and render a high dynamic range image on compatible displays. For additional information, see android hdr-image-format https://developer.android.com/guide/topics/media/platform/hdr-image-format. libultrahdr-2.0.2/LICENSE000066400000000000000000000304261523721266400150510ustar00rootroot00000000000000This software is distributed under the terms of both the MIT license and the Apache License (Version 2.0). ==== MIT License Copyright (c) 2022 The Android Open Source Project Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ==== Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. libultrahdr-2.0.2/LICENSE-APACHE000066400000000000000000000261361523721266400157730ustar00rootroot00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. libultrahdr-2.0.2/LICENSE-MIT000066400000000000000000000021001523721266400154640ustar00rootroot00000000000000MIT License Copyright (c) 2022 The Android Open Source Project Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. libultrahdr-2.0.2/README.md000066400000000000000000000075651523721266400153330ustar00rootroot00000000000000## Introduction libultrahdr is an image compression library that uses gain map technology to store and distribute HDR images. Conceptually on the encoding side, the library accepts SDR and HDR rendition of an image and from these a Gain Map (quotient between the two renditions) is computed. The library then uses backward compatible means to store the base image (SDR), gain map image and some associated metadata. Legacy readers that do not support handling the gain map image and/or metadata, will display the base image. Readers that support the format combine the base image with the gain map and render a high dynamic range image on compatible displays. For additional information, see android hdr-image-format [guide](https://developer.android.com/guide/topics/media/platform/hdr-image-format). ## Build from source using CMake This software suite has been built and tested on platforms: - Android - Linux - macOS - Windows Refer to [building.md](docs/building.md) for complete instructions. ## Using libultrahdr A detailed description of libultrahdr encode and decode api is included in [ultrahdr_api.h](ultrahdr_api.h) and for sample usage refer [demo app](examples/ultrahdr_app.cpp). libultrahdr includes two classes of APIs, one to compress and the other to decompress HDR images: ### Encoding api outline: | Scenario | Hdr intent raw | Sdr intent raw | Sdr intent compressed | Gain map compressed | Quality | Exif | Use Case | |:---------:| :----------: | :----------: | :---------------------: | :-------------------: | :-------: | :---------: | :-------- | | API - 0 | P010 or rgba1010102 or rgbaf16 | No | No | No | Optional| Optional | Used if, only hdr raw intent is present. [^1] | | API - 1 | P010 or rgba1010102 or rgbaf16 | YUV420 or rgba8888 | No | No | Optional| Optional | Used if, hdr raw and sdr raw intents are present.[^2] | | API - 2 | P010 or rgba1010102 or rgbaf16 | YUV420 or rgba8888 | Yes | No | No | No | Used if, hdr raw, sdr raw and sdr compressed intents are present.[^3] | | API - 3 | P010 or rgba1010102 or rgbaf16 | No | Yes | No | No | No | Used if, hdr raw and sdr compressed intents are present.[^4] | | API - 4 | No | No | Yes | Yes | No | No | Used if, sdr compressed, gain map compressed and GainMap Metadata are present.[^5] | [^1]: Tonemap hdr to sdr. Compute gain map from hdr and sdr. Compress sdr and gainmap at quality configured. Add exif if provided. Combine sdr compressed, gainmap in multi picture format with gainmap metadata. [^2]: Compute gain map from hdr and sdr. Compress sdr and gainmap at quality configured. Add exif if provided. Combine sdr compressed, gainmap in multi picture format with gainmap metadata. [^3]: Compute gain map from hdr and raw sdr. Compress gainmap. Combine sdr compressed, gainmap in multi picture format with gainmap metadata. [^4]: Decode compressed sdr input. Compute gain map from hdr and decoded sdr. Compress gainmap. Combine sdr compressed, gainmap in multi picture format with gainmap metadata. [^5]: Combine sdr compressed, gainmap in multi picture format with gainmap metadata. ### Decoding api outline: Configure display device characteristics (display transfer characteristics, max display boost) for optimal usage. | Input | Usage | | ------------- | ------------- | | max_display_boost | (optional, >= 1.0) the maximum available boost supported by a display. | | supported color transfer format pairs |
color transferColor format
SDR32bppRGBA8888
HDR_LINEAR64bppRGBAHalfFloat
HDR_PQ32bppRGBA1010102 PQ
HDR_HLG32bppRGBA1010102 HLG
| ## License This software is distributed under the terms of both the MIT license and the Apache License (Version 2.0). See [LICENSE](LICENSE) for details. libultrahdr-2.0.2/adobe-hdr-gain-map-license/000077500000000000000000000000001523721266400207735ustar00rootroot00000000000000libultrahdr-2.0.2/adobe-hdr-gain-map-license/Android.bp000066400000000000000000000007471523721266400227060ustar00rootroot00000000000000// Copyright 2023 The Android Open Source Project // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. license { name: "adobe_hdr_gain_map_license", license_kinds: ["legacy_by_exception_only"], license_text: ["NOTICE"], } libultrahdr-2.0.2/adobe-hdr-gain-map-license/NOTICE000066400000000000000000000001021523721266400216700ustar00rootroot00000000000000This product includes Gain Map technology under license by Adobe. libultrahdr-2.0.2/benchmark/000077500000000000000000000000001523721266400157715ustar00rootroot00000000000000libultrahdr-2.0.2/benchmark/Android.bp000066400000000000000000000022511523721266400176740ustar00rootroot00000000000000// Copyright 2023 The Android Open Source Project // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. package { // See: http://go/android-license-faq // A large-scale-change added 'default_applicable_licenses' to import // all of the 'license_kinds' from "external_libultrahdr_license" // to get the below license kinds: // SPDX-license-identifier-Apache-2.0 default_applicable_licenses: ["external_libultrahdr_license"], } cc_benchmark { name: "ultrahdr_benchmark", host_supported: true, srcs: ["benchmark_test.cpp"], static_libs: [ "libjpegdecoder", "libjpegencoder", "libultrahdr", ], shared_libs: [ "libimage_io", "libjpeg", "liblog", ], target: { android: { cflags: ["-DUHDR_ENABLE_GLES"], shared_libs: [ "libEGL", "libGLESv3", ], }, }, } libultrahdr-2.0.2/benchmark/AndroidTest.xml000066400000000000000000000033131523721266400207330ustar00rootroot00000000000000 libultrahdr-2.0.2/benchmark/DynamicConfig.xml000066400000000000000000000011131523721266400212210ustar00rootroot00000000000000 https://storage.googleapis.com/android_media/external/libultrahdr/benchmark/UltrahdrBenchmarkTestRes-1.2.zip libultrahdr-2.0.2/benchmark/benchmark_test.cpp000066400000000000000000000533061523721266400214750ustar00rootroot00000000000000/* * Copyright 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 or the MIT license * , at your * option. This file may not be copied, modified, or distributed * except according to those terms. */ #include #include #include #include #include "ultrahdr_api.h" #ifdef __ANDROID__ std::string kTestImagesPath = "/sdcard/test/UltrahdrBenchmarkTestRes-1.2/"; #ifdef LOG_NDEBUG #include "android/log.h" #ifndef LOG_TAG #define LOG_TAG "UHDR_BENCHMARK" #endif #ifndef ALOGE #define ALOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__) #endif #else #define ALOGE(...) ((void)0) #endif #else std::string kTestImagesPath = "./data/UltrahdrBenchmarkTestRes-1.2/"; #ifdef LOG_NDEBUG #include #define ALOGE(...) \ do { \ fprintf(stderr, __VA_ARGS__); \ fprintf(stderr, "\n"); \ } while (0) #else #define ALOGE(...) ((void)0) #endif #endif std::vector kDecodeAPITestImages = { "mountains_singlechannelgainmap.jpg", "mountains_multichannelgainmap.jpg", "mountains_singlechannelgamma.jpg", "mountains_multichannelgamma.jpg", }; std::vector kEncodeApi0TestImages12MpName = { "mountains_rgba1010102.raw", "mountains_rgba16F.raw", "mountains_p010.p010", }; std::vector> kEncodeApi1TestImages12MpName = { {"mountains_rgba1010102.raw", "mountains_rgba8888.raw"}, {"mountains_rgba16F.raw", "mountains_rgba8888.raw"}, {"mountains_p010.p010", "mountains_yuv420.yuv"}, }; using TestParamsDecodeAPI = std::tuple; using TestParamsEncoderAPI0 = std::tuple; using TestParamsEncoderAPI1 = std::tuple; std::vector testParamsDecodeAPI; std::vector testParamsAPI0; std::vector testParamsAPI1; std::string imgFmtToString(const uhdr_img_fmt of) { switch (of) { case UHDR_IMG_FMT_32bppRGBA8888: return "rgba8888"; case UHDR_IMG_FMT_64bppRGBAHalfFloat: return "64rgbaHalftoFloat"; case UHDR_IMG_FMT_32bppRGBA1010102: return "rgba1010102"; default: return "Unknown"; } } std::string colorGamutToString(const uhdr_color_gamut_t cg) { switch (cg) { case UHDR_CG_BT_709: return "bt709"; case UHDR_CG_DISPLAY_P3: return "p3"; case UHDR_CG_BT_2100: return "bt2100"; default: return "Unknown"; } } std::string tfToString(const uhdr_color_transfer_t of) { switch (of) { case UHDR_CT_LINEAR: return "linear"; case UHDR_CT_HLG: return "hlg"; case UHDR_CT_PQ: return "pq"; case UHDR_CT_SRGB: return "srgb"; default: return "Unknown"; } } #define READ_BYTES(DESC, ADDR, LEN) \ DESC.read(static_cast(ADDR), (LEN)); \ if (DESC.gcount() != (LEN)) { \ ALOGE("Failed to read: %u bytes, read: %zu bytes", LEN, DESC.gcount()); \ return false; \ } static bool loadFile(const char* filename, uhdr_raw_image_t* handle) { std::ifstream ifd(filename, std::ios::binary); if (ifd.good()) { if (handle->fmt == UHDR_IMG_FMT_24bppYCbCrP010) { const int bpp = 2; READ_BYTES(ifd, handle->planes[UHDR_PLANE_Y], handle->w * handle->h * bpp) READ_BYTES(ifd, handle->planes[UHDR_PLANE_UV], (handle->w / 2) * (handle->h / 2) * bpp * 2) return true; } else if (handle->fmt == UHDR_IMG_FMT_32bppRGBA1010102 || handle->fmt == UHDR_IMG_FMT_32bppRGBA8888 || handle->fmt == UHDR_IMG_FMT_64bppRGBAHalfFloat) { const int bpp = handle->fmt == UHDR_IMG_FMT_64bppRGBAHalfFloat ? 8 : 4; READ_BYTES(ifd, handle->planes[UHDR_PLANE_PACKED], handle->w * handle->h * bpp) return true; } else if (handle->fmt == UHDR_IMG_FMT_12bppYCbCr420) { READ_BYTES(ifd, handle->planes[UHDR_PLANE_Y], handle->w * handle->h) READ_BYTES(ifd, handle->planes[UHDR_PLANE_U], (handle->w / 2) * (handle->h / 2)) READ_BYTES(ifd, handle->planes[UHDR_PLANE_V], (handle->w / 2) * (handle->h / 2)) return true; } return false; } ALOGE("Unable to open file: %s", filename); return false; } static bool loadFile(const char* filename, void*& result, int length) { std::ifstream ifd(filename, std::ios::binary | std::ios::ate); if (ifd.good()) { int size = ifd.tellg(); if (size < length) { ALOGE("Requested to read %d bytes from file: %s, file contains only %d bytes", length, filename, size); return false; } ifd.seekg(0, std::ios::beg); result = malloc(length); if (result == nullptr) { ALOGE("Failed to allocate memory to store contents of file: %s", filename); return false; } READ_BYTES(ifd, result, length) return true; } ALOGE("Unable to open file: %s", filename); return false; } class DecBenchmark { public: std::string mUhdrFile; uhdr_color_transfer_t mTf; uhdr_img_fmt_t mOfmt; bool mEnableGLES; uhdr_compressed_image_t mUhdrImg{}; DecBenchmark(TestParamsDecodeAPI testParams) { mUhdrFile = std::get<0>(testParams); mTf = std::get<1>(testParams); mOfmt = std::get<2>(testParams); mEnableGLES = std::get<3>(testParams); } ~DecBenchmark() { if (mUhdrImg.data) { free(mUhdrImg.data); mUhdrImg.data = nullptr; } } bool fillJpegImageHandle(uhdr_compressed_image_t* uhdrImg, std::string mUhdrFile); }; bool DecBenchmark::fillJpegImageHandle(uhdr_compressed_image_t* uhdrImg, std::string filename) { std::ifstream ifd(filename, std::ios::binary | std::ios::ate); if (ifd.good()) { int size = ifd.tellg(); uhdrImg->capacity = size; uhdrImg->data_sz = size; uhdrImg->data = nullptr; uhdrImg->cg = UHDR_CG_UNSPECIFIED; uhdrImg->ct = UHDR_CT_UNSPECIFIED; uhdrImg->range = UHDR_CR_UNSPECIFIED; ifd.close(); return loadFile(filename.c_str(), uhdrImg->data, size); } return false; } class EncBenchmark { public: std::string mHdrFile, mSdrFile; uhdr_color_gamut_t mHdrCg, mSdrCg; uhdr_img_fmt_t mHdrCf, mSdrCf; int mWidth, mHeight; uhdr_color_transfer_t mHdrCt, mSdrCt = UHDR_CT_SRGB; int mUseMultiChannelGainMap; int mMapDimensionScaleFactor = 1; float mGamma; uhdr_enc_preset_t mEncPreset; uhdr_raw_image_t mHdrImg{}, mSdrImg{}; EncBenchmark(TestParamsEncoderAPI0 testParams) { mHdrFile = std::get<0>(testParams); mWidth = std::get<1>(testParams); mHeight = std::get<2>(testParams); mHdrCg = std::get<3>(testParams); mHdrCt = std::get<4>(testParams); mUseMultiChannelGainMap = std::get<5>(testParams); mGamma = std::get<6>(testParams); }; EncBenchmark(TestParamsEncoderAPI1 testParams) { mHdrFile = std::get<0>(testParams); mSdrFile = std::get<1>(testParams); mWidth = std::get<2>(testParams); mHeight = std::get<3>(testParams); mHdrCg = std::get<4>(testParams); mHdrCt = std::get<5>(testParams); mSdrCg = std::get<6>(testParams); mUseMultiChannelGainMap = std::get<7>(testParams); mGamma = std::get<8>(testParams); mEncPreset = std::get<9>(testParams); } ~EncBenchmark() { int count = sizeof mHdrImg.planes / sizeof mHdrImg.planes[0]; for (int i = 0; i < count; i++) { if (mHdrImg.planes[i]) { free(mHdrImg.planes[i]); mHdrImg.planes[i] = nullptr; } if (mSdrImg.planes[i]) { free(mSdrImg.planes[i]); mSdrImg.planes[i] = nullptr; } } } bool fillRawImageHandle(uhdr_raw_image_t* rawImg, int width, int height, std::string file, uhdr_img_fmt_t cf, uhdr_color_gamut_t cg, uhdr_color_transfer_t ct); }; bool EncBenchmark::fillRawImageHandle(uhdr_raw_image_t* rawImg, int width, int height, std::string file, uhdr_img_fmt_t cf, uhdr_color_gamut_t cg, uhdr_color_transfer_t ct) { rawImg->fmt = cf; rawImg->cg = cg; rawImg->ct = ct; rawImg->w = width; rawImg->h = height; if (cf == UHDR_IMG_FMT_24bppYCbCrP010) { const int bpp = 2; rawImg->range = std::rand() % 2 ? UHDR_CR_FULL_RANGE : UHDR_CR_LIMITED_RANGE; rawImg->planes[UHDR_PLANE_Y] = malloc(width * height * bpp); rawImg->planes[UHDR_PLANE_UV] = malloc((width / 2) * (height / 2) * bpp * 2); rawImg->planes[UHDR_PLANE_V] = nullptr; rawImg->stride[UHDR_PLANE_Y] = width; rawImg->stride[UHDR_PLANE_UV] = width; rawImg->stride[UHDR_PLANE_V] = 0; return loadFile(file.c_str(), rawImg); } else if (cf == UHDR_IMG_FMT_32bppRGBA1010102 || cf == UHDR_IMG_FMT_32bppRGBA8888 || cf == UHDR_IMG_FMT_64bppRGBAHalfFloat) { const int bpp = cf == UHDR_IMG_FMT_64bppRGBAHalfFloat ? 8 : 4; rawImg->range = UHDR_CR_FULL_RANGE; rawImg->planes[UHDR_PLANE_PACKED] = malloc(width * height * bpp); rawImg->planes[UHDR_PLANE_UV] = nullptr; rawImg->planes[UHDR_PLANE_V] = nullptr; rawImg->stride[UHDR_PLANE_PACKED] = width; rawImg->stride[UHDR_PLANE_UV] = 0; rawImg->stride[UHDR_PLANE_V] = 0; return loadFile(file.c_str(), rawImg); } else if (cf == UHDR_IMG_FMT_12bppYCbCr420) { rawImg->range = UHDR_CR_FULL_RANGE; rawImg->planes[UHDR_PLANE_Y] = malloc(width * height); rawImg->planes[UHDR_PLANE_U] = malloc((width / 2) * (height / 2)); rawImg->planes[UHDR_PLANE_V] = malloc((width / 2) * (height / 2)); rawImg->stride[UHDR_PLANE_Y] = width; rawImg->stride[UHDR_PLANE_U] = width / 2; rawImg->stride[UHDR_PLANE_V] = width / 2; return loadFile(file.c_str(), rawImg); } return false; } static void BM_UHDRDecode(benchmark::State& s, TestParamsDecodeAPI testVectors) { DecBenchmark benchmark(testVectors); s.SetLabel(benchmark.mUhdrFile + ", OutputFormat: " + imgFmtToString(benchmark.mOfmt) + ", ColorTransfer: " + tfToString(benchmark.mTf) + ", enableGLES: " + (benchmark.mEnableGLES ? "true" : "false")); benchmark.mUhdrFile = kTestImagesPath + "jpegr/" + benchmark.mUhdrFile; if (!benchmark.fillJpegImageHandle(&benchmark.mUhdrImg, benchmark.mUhdrFile)) { s.SkipWithError("unable to load file : " + benchmark.mUhdrFile); return; } #define RET_IF_ERR(x) \ { \ uhdr_error_info_t status = (x); \ if (status.error_code != UHDR_CODEC_OK) { \ uhdr_release_decoder(decHandle); \ s.SkipWithError(status.has_detail ? status.detail : "Unknown error"); \ return; \ } \ } uhdr_codec_private_t* decHandle = uhdr_create_decoder(); for (auto _ : s) { RET_IF_ERR(uhdr_dec_set_image(decHandle, &benchmark.mUhdrImg)) RET_IF_ERR(uhdr_dec_set_out_color_transfer(decHandle, benchmark.mTf)) RET_IF_ERR(uhdr_dec_set_out_img_format(decHandle, benchmark.mOfmt)) RET_IF_ERR(uhdr_enable_gpu_acceleration(decHandle, benchmark.mEnableGLES)) RET_IF_ERR(uhdr_decode(decHandle)) uhdr_reset_decoder(decHandle); } uhdr_release_decoder(decHandle); #undef RET_IF_ERR } #define RET_IF_ERR(x) \ { \ uhdr_error_info_t status = (x); \ if (status.error_code != UHDR_CODEC_OK) { \ uhdr_release_encoder(encHandle); \ s.SkipWithError(status.has_detail ? status.detail : "Unknown error"); \ return; \ } \ } static void BM_UHDREncode_Api0(benchmark::State& s, TestParamsEncoderAPI0 testVectors) { EncBenchmark benchmark(testVectors); s.SetLabel( benchmark.mHdrFile + ", " + std::to_string(benchmark.mWidth) + "x" + std::to_string(benchmark.mHeight) + ", " + colorGamutToString(benchmark.mHdrCg) + ", " + (benchmark.mHdrFile.find("rgba16F") != std::string::npos ? "linear" : tfToString(benchmark.mHdrCt)) + ", " + (benchmark.mUseMultiChannelGainMap == 0 ? "singlechannelgainmap" : "multichannelgainmap") + ", gamma: " + std::to_string(benchmark.mGamma)); if (benchmark.mHdrFile.find("p010") != std::string::npos) { benchmark.mHdrFile = kTestImagesPath + "p010/" + benchmark.mHdrFile; benchmark.mHdrCf = UHDR_IMG_FMT_24bppYCbCrP010; } else if (benchmark.mHdrFile.find("rgba1010102") != std::string::npos) { benchmark.mHdrFile = kTestImagesPath + "rgba1010102/" + benchmark.mHdrFile; benchmark.mHdrCf = UHDR_IMG_FMT_32bppRGBA1010102; } else if (benchmark.mHdrFile.find("rgba16F") != std::string::npos) { benchmark.mHdrFile = kTestImagesPath + "rgba16F/" + benchmark.mHdrFile; benchmark.mHdrCf = UHDR_IMG_FMT_64bppRGBAHalfFloat; benchmark.mHdrCt = UHDR_CT_LINEAR; } else { s.SkipWithError("Invalid file format : " + benchmark.mHdrFile); return; } if (!benchmark.fillRawImageHandle(&benchmark.mHdrImg, benchmark.mWidth, benchmark.mHeight, benchmark.mHdrFile, benchmark.mHdrCf, benchmark.mHdrCg, benchmark.mHdrCt)) { s.SkipWithError("unable to load file : " + benchmark.mHdrFile); return; } uhdr_codec_private_t* encHandle = uhdr_create_encoder(); for (auto _ : s) { RET_IF_ERR(uhdr_enc_set_raw_image(encHandle, &benchmark.mHdrImg, UHDR_HDR_IMG)) RET_IF_ERR( uhdr_enc_set_using_multi_channel_gainmap(encHandle, benchmark.mUseMultiChannelGainMap)) RET_IF_ERR(uhdr_enc_set_gainmap_scale_factor(encHandle, benchmark.mMapDimensionScaleFactor)) RET_IF_ERR(uhdr_enc_set_gainmap_gamma(encHandle, benchmark.mGamma)) RET_IF_ERR(uhdr_encode(encHandle)) uhdr_reset_encoder(encHandle); } uhdr_release_encoder(encHandle); } static void BM_UHDREncode_Api1(benchmark::State& s, TestParamsEncoderAPI1 testVectors) { EncBenchmark benchmark(testVectors); s.SetLabel( benchmark.mHdrFile + ", " + benchmark.mSdrFile + ", " + std::to_string(benchmark.mWidth) + "x" + std::to_string(benchmark.mHeight) + ", hdrCg: " + colorGamutToString(benchmark.mHdrCg) + ", hdrCt: " + (benchmark.mHdrFile.find("rgba16F") != std::string::npos ? "linear" : tfToString(benchmark.mHdrCt)) + ", sdrCg: " + colorGamutToString(benchmark.mSdrCg) + ", " + (benchmark.mUseMultiChannelGainMap == 0 ? "singlechannelgainmap" : "multichannelgainmap") + ", gamma: " + std::to_string(benchmark.mGamma) + ", " + (benchmark.mEncPreset == UHDR_USAGE_BEST_QUALITY ? "best_quality" : "realtime")); if (benchmark.mHdrFile.find("p010") != std::string::npos) { benchmark.mHdrFile = kTestImagesPath + "p010/" + benchmark.mHdrFile; benchmark.mHdrCf = UHDR_IMG_FMT_24bppYCbCrP010; } else if (benchmark.mHdrFile.find("rgba1010102") != std::string::npos) { benchmark.mHdrFile = kTestImagesPath + "rgba1010102/" + benchmark.mHdrFile; benchmark.mHdrCf = UHDR_IMG_FMT_32bppRGBA1010102; } else if (benchmark.mHdrFile.find("rgba16F") != std::string::npos) { benchmark.mHdrFile = kTestImagesPath + "rgba16F/" + benchmark.mHdrFile; benchmark.mHdrCf = UHDR_IMG_FMT_64bppRGBAHalfFloat; benchmark.mHdrCt = UHDR_CT_LINEAR; } else { s.SkipWithError("Invalid hdr file format : " + benchmark.mHdrFile); return; } if (benchmark.mSdrFile.find("yuv420") != std::string::npos) { benchmark.mSdrFile = kTestImagesPath + "yuv420/" + benchmark.mSdrFile; benchmark.mSdrCf = UHDR_IMG_FMT_12bppYCbCr420; } else if (benchmark.mSdrFile.find("rgba8888") != std::string::npos) { benchmark.mSdrFile = kTestImagesPath + "rgba8888/" + benchmark.mSdrFile; benchmark.mSdrCf = UHDR_IMG_FMT_32bppRGBA8888; } else { s.SkipWithError("Invalid sdr file format : " + benchmark.mSdrFile); return; } if (!benchmark.fillRawImageHandle(&benchmark.mHdrImg, benchmark.mWidth, benchmark.mHeight, benchmark.mHdrFile, benchmark.mHdrCf, benchmark.mHdrCg, benchmark.mHdrCt)) { s.SkipWithError("unable to load file : " + benchmark.mHdrFile); return; } if (!benchmark.fillRawImageHandle(&benchmark.mSdrImg, benchmark.mWidth, benchmark.mHeight, benchmark.mSdrFile, benchmark.mSdrCf, benchmark.mSdrCg, benchmark.mSdrCt)) { s.SkipWithError("unable to load sdr file : " + benchmark.mSdrFile); return; } uhdr_codec_private_t* encHandle = uhdr_create_encoder(); for (auto _ : s) { RET_IF_ERR(uhdr_enc_set_raw_image(encHandle, &benchmark.mHdrImg, UHDR_HDR_IMG)) RET_IF_ERR(uhdr_enc_set_raw_image(encHandle, &benchmark.mSdrImg, UHDR_SDR_IMG)) RET_IF_ERR( uhdr_enc_set_using_multi_channel_gainmap(encHandle, benchmark.mUseMultiChannelGainMap)) RET_IF_ERR(uhdr_enc_set_gainmap_scale_factor(encHandle, benchmark.mMapDimensionScaleFactor)) RET_IF_ERR(uhdr_enc_set_gainmap_gamma(encHandle, benchmark.mGamma)) RET_IF_ERR(uhdr_enc_set_preset(encHandle, benchmark.mEncPreset)) RET_IF_ERR(uhdr_encode(encHandle)) uhdr_reset_encoder(encHandle); } uhdr_release_encoder(encHandle); } void addTestVectors() { for (const auto& uhdrFile : kDecodeAPITestImages) { /* Decode API - uhdrFile, colorTransfer, imgFormat, enableGLES */ testParamsDecodeAPI.push_back({uhdrFile, UHDR_CT_HLG, UHDR_IMG_FMT_32bppRGBA1010102, false}); testParamsDecodeAPI.push_back({uhdrFile, UHDR_CT_PQ, UHDR_IMG_FMT_32bppRGBA1010102, false}); testParamsDecodeAPI.push_back( {uhdrFile, UHDR_CT_LINEAR, UHDR_IMG_FMT_64bppRGBAHalfFloat, false}); testParamsDecodeAPI.push_back({uhdrFile, UHDR_CT_HLG, UHDR_IMG_FMT_32bppRGBA1010102, true}); testParamsDecodeAPI.push_back({uhdrFile, UHDR_CT_PQ, UHDR_IMG_FMT_32bppRGBA1010102, true}); testParamsDecodeAPI.push_back( {uhdrFile, UHDR_CT_LINEAR, UHDR_IMG_FMT_64bppRGBAHalfFloat, true}); testParamsDecodeAPI.push_back({uhdrFile, UHDR_CT_SRGB, UHDR_IMG_FMT_32bppRGBA8888, false}); } for (const auto& hdrFile : kEncodeApi0TestImages12MpName) { /* Encode API 0 - hdrFile, width, height, hdrColorGamut, hdrColorTransfer, useMultiChannelGainmap, gamma */ testParamsAPI0.push_back({hdrFile, 4080, 3072, UHDR_CG_BT_2100, UHDR_CT_PQ, 0, 1.0f}); testParamsAPI0.push_back({hdrFile, 4080, 3072, UHDR_CG_BT_2100, UHDR_CT_PQ, 1, 1.0f}); testParamsAPI0.push_back({hdrFile, 4080, 3072, UHDR_CG_BT_2100, UHDR_CT_PQ, 0, 1.571f}); testParamsAPI0.push_back({hdrFile, 4080, 3072, UHDR_CG_BT_2100, UHDR_CT_PQ, 1, 1.616f}); } for (const auto& inputFiles : kEncodeApi1TestImages12MpName) { /* Encode API 1 - hdrFile, sdrFile, width, height, hdrColorGamut, hdrColorTransfer, sdrColorGamut, useMultiChannelGainmap, gamma, encPreset */ testParamsAPI1.push_back({inputFiles.first, inputFiles.second, 4080, 3072, UHDR_CG_BT_2100, UHDR_CT_PQ, UHDR_CG_BT_709, 0, 1.0f, UHDR_USAGE_REALTIME}); testParamsAPI1.push_back({inputFiles.first, inputFiles.second, 4080, 3072, UHDR_CG_BT_2100, UHDR_CT_PQ, UHDR_CG_BT_709, 1, 1.0f, UHDR_USAGE_REALTIME}); testParamsAPI1.push_back({inputFiles.first, inputFiles.second, 4080, 3072, UHDR_CG_BT_2100, UHDR_CT_PQ, UHDR_CG_BT_709, 0, 1.571f, UHDR_USAGE_REALTIME}); testParamsAPI1.push_back({inputFiles.first, inputFiles.second, 4080, 3072, UHDR_CG_BT_2100, UHDR_CT_PQ, UHDR_CG_BT_709, 0, 1.0f, UHDR_USAGE_BEST_QUALITY}); testParamsAPI1.push_back({inputFiles.first, inputFiles.second, 4080, 3072, UHDR_CG_BT_2100, UHDR_CT_PQ, UHDR_CG_BT_709, 1, 1.571f, UHDR_USAGE_REALTIME}); testParamsAPI1.push_back({inputFiles.first, inputFiles.second, 4080, 3072, UHDR_CG_BT_2100, UHDR_CT_PQ, UHDR_CG_BT_709, 1, 1.0f, UHDR_USAGE_BEST_QUALITY}); testParamsAPI1.push_back({inputFiles.first, inputFiles.second, 4080, 3072, UHDR_CG_BT_2100, UHDR_CT_PQ, UHDR_CG_BT_709, 0, 1.571f, UHDR_USAGE_BEST_QUALITY}); testParamsAPI1.push_back({inputFiles.first, inputFiles.second, 4080, 3072, UHDR_CG_BT_2100, UHDR_CT_PQ, UHDR_CG_BT_709, 1, 1.571f, UHDR_USAGE_BEST_QUALITY}); } } void registerBenchmarks() { for (auto& param : testParamsDecodeAPI) { benchmark::RegisterBenchmark("BM_UHDRDecode", BM_UHDRDecode, param) ->Unit(benchmark::kMillisecond); } for (auto& param : testParamsAPI0) { benchmark::RegisterBenchmark("BM_UHDREncode_Api0", BM_UHDREncode_Api0, param) ->Unit(benchmark::kMillisecond); } for (auto& param : testParamsAPI1) { benchmark::RegisterBenchmark("BM_UHDREncode_Api1", BM_UHDREncode_Api1, param) ->Unit(benchmark::kMillisecond); } } int main(int argc, char** argv) { addTestVectors(); registerBenchmarks(); benchmark::Initialize(&argc, argv); benchmark::RunSpecifiedBenchmarks(nullptr, nullptr); benchmark::Shutdown(); return 0; } libultrahdr-2.0.2/cmake/000077500000000000000000000000001523721266400151175ustar00rootroot00000000000000libultrahdr-2.0.2/cmake/FindEGL.cmake000066400000000000000000000014561523721266400173370ustar00rootroot00000000000000# # Copyright (C) 2024 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 or the MIT license # , at your # option. This file may not be copied, modified, or distributed # except according to those terms. # # # Finds the EGL library. This module defines: # # EGL_FOUND - True if EGL library is found, False otherwise # EGL_LIBRARIES - EGL library # EGL_INCLUDE_DIRS - Include dir # find_path(EGL_INCLUDE_DIRS EGL/egl.h) find_library(EGL_LIBRARIES NAMES EGL libEGL) include(FindPackageHandleStandardArgs) find_package_handle_standard_args(EGL DEFAULT_MSG EGL_INCLUDE_DIRS EGL_LIBRARIES) mark_as_advanced(EGL_INCLUDE_DIRS EGL_LIBRARIES) libultrahdr-2.0.2/cmake/FindOpenGLES3.cmake000066400000000000000000000026221523721266400203630ustar00rootroot00000000000000# # Copyright (C) 2024 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 or the MIT license # , at your # option. This file may not be copied, modified, or distributed # except according to those terms. # # # Finds the OpenGLES3 library. This module defines: # # OpenGLES3_FOUND - True if OpenGLES 3 library is found, False otherwise # OPENGLES3_LIBRARIES - OpenGLES3 library # OPENGLES3_INCLUDE_DIRS - Include dir # OpenGLES3_API_VERSION - OpenGLES3 Supported API version # find_path(OPENGLES3_INCLUDE_DIRS GLES3/gl3.h) # Android has separate library for OpenGLES3 in the form GLESv3 # Many platforms support OpenGLES3 via OpenGLES2 lib. In this case, presence of GLES3/gl*.h will be indicative of GLES3 support find_library(OPENGLES3_LIBRARIES NAMES GLESv3 GLESv2 libGLESv2) if(OPENGLES3_INCLUDE_DIRS) if(EXISTS ${OPENGLES3_INCLUDE_DIRS}/GLES3/gl32.h) set(OpenGLES3_API_VERSION "3.2") elseif(EXISTS ${OPENGLES3_INCLUDE_DIRS}/GLES3/gl31.h) set(OpenGLES3_API_VERSION "3.1") else() set(OpenGLES3_API_VERSION "3.0") endif() endif() include(FindPackageHandleStandardArgs) find_package_handle_standard_args(OpenGLES3 OPENGLES3_INCLUDE_DIRS OPENGLES3_LIBRARIES) mark_as_advanced(OPENGLES3_INCLUDE_DIRS OPENGLES3_LIBRARIES) libultrahdr-2.0.2/cmake/Findlibheif.cmake000066400000000000000000000104771523721266400203350ustar00rootroot00000000000000# cmake/Findlibheif.cmake # # Finds the libheif library and provides the imported target libheif::heif. # # Imported Targets: # libheif::heif - Target representing the libheif library # # Result Variables: # LIBHEIF_FOUND - True if libheif was found # LIBHEIF_HAS_GAIN_MAP - True if libheif includes ISO 21496-1 gain map APIs # LIBHEIF_VERSION - Version of libheif found # LIBHEIF_INCLUDE_DIRS - Include directories for libheif # LIBHEIF_LIBRARIES - Libraries needed to link against libheif include(CheckCXXSymbolExists) include(FindPackageHandleStandardArgs) set(LIBHEIF_TARGET "") # ----------------------------------------------------------------------------- # 1. Primary Method: Modern CMake CONFIG Mode (libheifConfig.cmake) # ----------------------------------------------------------------------------- find_package(libheif CONFIG QUIET) if(libheif_FOUND OR LIBHEIF_FOUND) if(TARGET libheif::heif) set(LIBHEIF_TARGET libheif::heif) elseif(TARGET heif) set(LIBHEIF_TARGET heif) if(NOT TARGET libheif::heif) add_library(libheif::heif ALIAS heif) endif() endif() if(libheif_VERSION) set(LIBHEIF_VERSION ${libheif_VERSION}) endif() endif() # ----------------------------------------------------------------------------- # 2. Secondary Fallback: pkg-config Mode # ----------------------------------------------------------------------------- if(NOT LIBHEIF_TARGET) find_package(PkgConfig QUIET) if(PkgConfig_FOUND) pkg_check_modules(PC_LIBHEIF QUIET IMPORTED_TARGET libheif) if(PC_LIBHEIF_FOUND) if(NOT TARGET libheif::heif) add_library(libheif::heif ALIAS PkgConfig::PC_LIBHEIF) endif() set(LIBHEIF_TARGET libheif::heif) set(LIBHEIF_VERSION ${PC_LIBHEIF_VERSION}) set(LIBHEIF_INCLUDE_DIRS ${PC_LIBHEIF_INCLUDE_DIRS}) set(LIBHEIF_LIBRARIES ${PC_LIBHEIF_LIBRARIES}) endif() endif() endif() # ----------------------------------------------------------------------------- # 3. Tertiary Fallback: Direct Path and Library Discovery # ----------------------------------------------------------------------------- if(NOT LIBHEIF_TARGET) find_path(LIBHEIF_INCLUDE_DIR NAMES "libheif/heif.h") find_library(LIBHEIF_LIBRARY NAMES heif libheif) if(LIBHEIF_INCLUDE_DIR AND LIBHEIF_LIBRARY) if(NOT TARGET libheif::heif) add_library(libheif::heif UNKNOWN IMPORTED) set_target_properties(libheif::heif PROPERTIES IMPORTED_LOCATION "${LIBHEIF_LIBRARY}" INTERFACE_INCLUDE_DIRECTORIES "${LIBHEIF_INCLUDE_DIR}" INTERFACE_LINK_LIBRARIES "${CMAKE_DL_LIBS}" ) endif() set(LIBHEIF_TARGET libheif::heif) set(LIBHEIF_INCLUDE_DIRS "${LIBHEIF_INCLUDE_DIR}") set(LIBHEIF_LIBRARIES "${LIBHEIF_LIBRARY}") endif() endif() # ----------------------------------------------------------------------------- # 4. Feature Verification: ISO 21496-1 Gain Map Support # ----------------------------------------------------------------------------- if(LIBHEIF_TARGET) get_target_property(LIBHEIF_INCLUDES ${LIBHEIF_TARGET} INTERFACE_INCLUDE_DIRECTORIES) get_target_property(LIBHEIF_DEFS ${LIBHEIF_TARGET} INTERFACE_COMPILE_DEFINITIONS) set(CMAKE_REQUIRED_INCLUDES "") if(LIBHEIF_INCLUDES) list(APPEND CMAKE_REQUIRED_INCLUDES ${LIBHEIF_INCLUDES}) endif() if(LIBHEIF_INCLUDE_DIR) list(APPEND CMAKE_REQUIRED_INCLUDES ${LIBHEIF_INCLUDE_DIR}) endif() if(LIBHEIF_DEFS) set(CMAKE_REQUIRED_DEFINITIONS "-D${LIBHEIF_DEFS}") endif() # Perform a compile-only check to avoid linking transitive dependencies # (e.g. AOM::aom, x265) of static libheif targets in the try_compile sandbox. set(_saved_try_compile_target_type ${CMAKE_TRY_COMPILE_TARGET_TYPE}) set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY) check_cxx_symbol_exists( heif_image_handle_get_gain_map_image_handle "libheif/heif.h" LIBHEIF_HAS_GAIN_MAP ) set(CMAKE_TRY_COMPILE_TARGET_TYPE ${_saved_try_compile_target_type}) unset(CMAKE_REQUIRED_INCLUDES) unset(CMAKE_REQUIRED_DEFINITIONS) endif() # ----------------------------------------------------------------------------- # Standard Package Handler # ----------------------------------------------------------------------------- find_package_handle_standard_args(libheif REQUIRED_VARS LIBHEIF_TARGET VERSION_VAR LIBHEIF_VERSION FOUND_VAR LIBHEIF_FOUND ) libultrahdr-2.0.2/cmake/cmake_uninstall.cmake.in000066400000000000000000000016101523721266400216750ustar00rootroot00000000000000# Source: https://gitlab.kitware.com/cmake/community/-/wikis/FAQ#can-i-do-make-uninstall-with-cmake if(NOT EXISTS "@CMAKE_BINARY_DIR@/install_manifest.txt") message(FATAL_ERROR "Cannot find install manifest: @CMAKE_BINARY_DIR@/install_manifest.txt") endif() file(READ "@CMAKE_BINARY_DIR@/install_manifest.txt" files) string(REGEX REPLACE "\n" ";" files "${files}") foreach(file ${files}) message(STATUS "Uninstalling $ENV{DESTDIR}${file}") if(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}") execute_process(COMMAND ${CMAKE_COMMAND} -E remove "$ENV{DESTDIR}${file}" OUTPUT_VARIABLE rm_out RESULT_VARIABLE rm_retval) if(NOT "${rm_retval}" STREQUAL 0) message(FATAL_ERROR "Problem when removing $ENV{DESTDIR}${file}") endif() else() message(STATUS "File $ENV{DESTDIR}${file} does not exist.") endif() endforeach()libultrahdr-2.0.2/cmake/libuhdr.pc.in000066400000000000000000000005371523721266400175060ustar00rootroot00000000000000prefix=@CMAKE_INSTALL_PREFIX@ libdir=${prefix}/@CMAKE_INSTALL_LIBDIR@ includedir=${prefix}/@CMAKE_INSTALL_INCLUDEDIR@ Name: @PROJECT_NAME@ Description: @CMAKE_PROJECT_DESCRIPTION@ Version: @PROJECT_VERSION@ Requires.private: libjpeg Cflags: -I${includedir} Libs: -L${libdir} -l@UHDR_TARGET_NAME@ Libs.private: @CMAKE_THREAD_LIBS_INIT@ @UHDR_GL_DEPS@ libultrahdr-2.0.2/cmake/package.cmake000066400000000000000000000043041523721266400175150ustar00rootroot00000000000000# # Copyright (C) 2024 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 or the MIT license # , at your # option. This file may not be copied, modified, or distributed # except according to those terms. # # common package configuration set(CPACK_PACKAGE_NAME ${CMAKE_PROJECT_NAME}) set(CPACK_PACKAGE_VENDOR "Google, Inc.") set(CPACK_PACKAGE_CONTACT "Dichen Zhang ") set(CPACK_PACKAGE_VERSION_MAJOR ${UHDR_MAJOR_VERSION}) set(CPACK_PACKAGE_VERSION_MINOR ${UHDR_MINOR_VERSION}) set(CPACK_PACKAGE_VERSION_PATCH ${UHDR_PATCH_VERSION}) set(CPACK_PACKAGE_VERSION "${UHDR_MAJOR_VERSION}.${UHDR_MINOR_VERSION}.${UHDR_PATCH_VERSION}") set(CPACK_PACKAGE_DESCRIPTION_FILE ${CMAKE_SOURCE_DIR}/DESCRIPTION) set(CPACK_PACKAGE_DESCRIPTION_SUMMARY ${CMAKE_PROJECT_DESCRIPTION}) set(CPACK_PACKAGE_HOMEPAGE_URL "https://github.com/google/libultrahdr") if("${CMAKE_SYSTEM_NAME}" STREQUAL "") message(FATAL_ERROR "Failed to determine CPACK_SYSTEM_NAME. Is CMAKE_SYSTEM_NAME set?" ) endif() string(TOLOWER "${CMAKE_SYSTEM_NAME}" CPACK_SYSTEM_NAME) set(CPACK_PACKAGE_ARCHITECTURE ${ARCH}) set(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}-${CPACK_SYSTEM_NAME}") set(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_FILE_NAME}-${CPACK_PACKAGE_ARCHITECTURE}") set(CPACK_RESOURCE_FILE_LICENSE ${CMAKE_SOURCE_DIR}/LICENSE) set(CPACK_PACKAGING_INSTALL_PREFIX ${CMAKE_INSTALL_PREFIX}) # platform specific configuration if(APPLE) set(CPACK_GENERATOR "DragNDrop") elseif(UNIX) if(EXISTS "/etc/debian_version") set(CPACK_GENERATOR "DEB") set(CPACK_DEBIAN_PACKAGE_SHLIBDEPS ON) set(CPACK_DEBIAN_PACKAGE_RELEASE 1) set(CPACK_DEBIAN_PACKAGE_HOMEPAGE ${CPACK_PACKAGE_HOMEPAGE_URL}) elseif(EXISTS "/etc/redhat-release") set(CPACK_GENERATOR "RPM") set(CPACK_RPM_PACKAGE_ARCHITECTURE ${CPACK_PACKAGE_ARCHITECTURE}) set(CPACK_RPM_PACKAGE_RELEASE 1) set(CPACK_RPM_PACKAGE_LICENSE "Apache 2.0") set(CPACK_RPM_PACKAGE_URL ${CPACK_PACKAGE_HOMEPAGE_URL}) else() set(CPACK_GENERATOR "TGZ") endif() else() set(CPACK_GENERATOR "ZIP") endif() libultrahdr-2.0.2/cmake/patches/000077500000000000000000000000001523721266400165465ustar00rootroot00000000000000libultrahdr-2.0.2/cmake/patches/libheif_pr1503.patch000066400000000000000000000757171523721266400222240ustar00rootroot00000000000000diff --git a/CMakeLists.txt b/CMakeLists.txt index 9d2702a6..5c4c8d6e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -521,34 +521,36 @@ else () set(CMAKE_CXX_VISIBILITY_PRESET default) endif () -add_subdirectory(heifio) - if(WITH_EXAMPLES) - add_subdirectory (examples) + add_subdirectory(heifio) + add_subdirectory(examples) endif() # --- API documentation -# check if Doxygen is installed -find_package(Doxygen) -if (DOXYGEN_FOUND) - # set input and output files - set(DOXYGEN_IN ${CMAKE_CURRENT_SOURCE_DIR}/libheif/Doxyfile.in) - set(DOXYGEN_OUT ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile) - - # request to configure the file - configure_file(${DOXYGEN_IN} ${DOXYGEN_OUT} @ONLY) - message("Doxygen build started") - - # note the option ALL which allows to build the docs together with the application - add_custom_target( doc_doxygen ALL - COMMAND ${DOXYGEN_EXECUTABLE} ${DOXYGEN_OUT} - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} - COMMENT "Generating API documentation with Doxygen" - VERBATIM ) -else (DOXYGEN_FOUND) - message("Doxygen tool needs to be installed to generate the API documentation") -endif (DOXYGEN_FOUND) +option(WITH_DOCS "Build API documentation" OFF) +if(WITH_DOCS) + # check if Doxygen is installed + find_package(Doxygen) + if (DOXYGEN_FOUND) + # set input and output files + set(DOXYGEN_IN ${CMAKE_CURRENT_SOURCE_DIR}/libheif/Doxyfile.in) + set(DOXYGEN_OUT ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile) + + # request to configure the file + configure_file(${DOXYGEN_IN} ${DOXYGEN_OUT} @ONLY) + message("Doxygen build started") + + # note the option ALL which allows to build the docs together with the application + add_custom_target( doc_doxygen + COMMAND ${DOXYGEN_EXECUTABLE} ${DOXYGEN_OUT} + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + COMMENT "Generating API documentation with Doxygen" + VERBATIM ) + else (DOXYGEN_FOUND) + message("Doxygen tool needs to be installed to generate the API documentation") + endif (DOXYGEN_FOUND) +endif() # --- Testing diff --git a/CMakePresets.json b/CMakePresets.json index bbe8b49a..3662e753 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -54,6 +54,7 @@ "WITH_VVDEC_PLUGIN" : "OFF", "WITH_VVENC" : "ON", "WITH_VVENC_PLUGIN" : "OFF", + "WITH_EXPERIMENTAL_GAIN_MAP" : "OFF", "WITH_REDUCED_VISIBILITY" : "OFF", "WITH_HEADER_COMPRESSION" : "ON", @@ -110,6 +111,7 @@ "WITH_VVDEC_PLUGIN" : "ON", "WITH_VVENC" : "ON", "WITH_VVENC_PLUGIN" : "ON", + "WITH_EXPERIMENTAL_GAIN_MAP" : "OFF", "WITH_REDUCED_VISIBILITY" : "ON", "WITH_HEADER_COMPRESSION" : "ON", @@ -148,6 +150,7 @@ "WITH_UVG266" : "OFF", "WITH_VVDEC" : "OFF", "WITH_VVENC" : "OFF", + "WITH_EXPERIMENTAL_GAIN_MAP" : "OFF", "WITH_REDUCED_VISIBILITY" : "ON", "WITH_HEADER_COMPRESSION" : "OFF", diff --git a/libheif/CMakeLists.txt b/libheif/CMakeLists.txt index e8d7e0dc..290b27f4 100644 --- a/libheif/CMakeLists.txt +++ b/libheif/CMakeLists.txt @@ -249,6 +249,10 @@ if (ENABLE_EXPERIMENTAL_MINI_FORMAT) mini.cc) endif () +if (WITH_EXPERIMENTAL_GAIN_MAP) + target_compile_definitions(heif PUBLIC WITH_EXPERIMENTAL_GAIN_MAP=1) +endif () + write_basic_package_version_file(${PROJECT_NAME}-config-version.cmake COMPATIBILITY ExactVersion) install(TARGETS heif EXPORT ${PROJECT_NAME}-config diff --git a/libheif/api/libheif/heif.cc b/libheif/api/libheif/heif.cc index de275a9d..01c5eeab 100644 --- a/libheif/api/libheif/heif.cc +++ b/libheif/api/libheif/heif.cc @@ -657,6 +657,124 @@ struct heif_error heif_context_get_primary_image_ID(struct heif_context* ctx, he } +#if WITH_EXPERIMENTAL_GAIN_MAP + +struct heif_error heif_image_handle_get_gain_map_image_handle( + const struct heif_image_handle* handle, struct heif_image_handle** gain_map_handle) { + if (!gain_map_handle) { + return {heif_error_Usage_error, heif_suberror_Null_pointer_argument, + "NULL gain_map_handle passed to heif_image_handle_get_gain_map_image_handle()"}; + } + + std::shared_ptr gain_map_image = handle->image->get_gain_map(); + if (!gain_map_image) { + Error err(heif_error_Usage_error, heif_suberror_Nonexisting_item_referenced, + "base image handle is not associated with a gain map image"); + return err.error_struct(handle->image.get()); + } + + *gain_map_handle = new heif_image_handle(); + (*gain_map_handle)->image = gain_map_image; + (*gain_map_handle)->context = handle->context; + + return Error::Ok.error_struct(handle->image.get()); +} + +size_t heif_image_handle_get_gain_map_metadata_size(const struct heif_image_handle* handle) { + std::shared_ptr metadata = handle->image->get_gain_map_metadata(); + + if (metadata) { + // Ignore unsigned int(8) version = 0; field of ToneMapImage syntax + size_t sz = metadata->m_data.size(); + return sz > 0 ? (sz - 1) : 0; + } + + return 0; +} + +struct heif_error heif_image_handle_get_gain_map_metadata(const struct heif_image_handle* handle, + void* out_data) { + if (!out_data) { + return {heif_error_Usage_error, heif_suberror_Null_pointer_argument, + "NULL out_data passed to heif_image_handle_get_gain_map_metadata()"}; + } + + std::shared_ptr metadata = handle->image->get_gain_map_metadata(); + if (!metadata) { + Error err(heif_error_Invalid_input, heif_suberror_No_item_data, + "base image handle is not associated with a gain map image"); + return err.error_struct(handle->image.get()); + } + + uint8_t version = 0xff; + size_t pos = 0; + std::vector& buffer = metadata->m_data; + + if (pos >= buffer.size()) { + Error err(heif_error_Invalid_input, heif_suberror_End_of_data); + return err.error_struct(handle->image.get()); + } + version = buffer[pos++]; + if (version != 0) { + Error err(heif_error_Invalid_input, heif_suberror_Unsupported_data_version, + "Box[tmap] has unsupported version"); + return err.error_struct(handle->image.get()); + } + + memcpy(out_data, buffer.data() + pos, buffer.size() - pos); + + return heif_error_success; +} + +struct heif_error heif_image_handle_get_derived_image_nclx_color_profile( + const struct heif_image_handle* handle, struct heif_color_profile_nclx** out_data) { + if (!out_data) { + return {heif_error_Usage_error, heif_suberror_Null_pointer_argument, + "NULL out_data passed to heif_image_handle_get_derived_image_nclx_color_profile()"}; + } + + auto nclx_profile = handle->image->get_derived_img_color_profile_nclx(); + if (!nclx_profile) { + Error err(heif_error_Color_profile_does_not_exist, heif_suberror_Unspecified); + return err.error_struct(handle->image.get()); + } + + Error err = nclx_profile->get_nclx_color_profile(out_data); + + return err.error_struct(handle->image.get()); +} + +size_t heif_image_handle_get_derived_image_raw_color_profile_size( + const struct heif_image_handle* handle) { + auto profile_icc = handle->image->get_color_profile_icc(); + if (profile_icc) { + return profile_icc->get_data().size(); + } else { + return 0; + } +} + +struct heif_error heif_image_handle_get_derived_image_raw_color_profile( + const struct heif_image_handle* handle, void* out_data) { + if (!out_data) { + return {heif_error_Usage_error, heif_suberror_Null_pointer_argument, + "NULL out_data passed to heif_image_handle_get_derived_image_raw_color_profile()"}; + } + + auto raw_profile = handle->image->get_derived_img_color_profile_icc(); + if (raw_profile) { + memcpy(out_data, raw_profile->get_data().data(), raw_profile->get_data().size()); + } else { + Error err(heif_error_Color_profile_does_not_exist, heif_suberror_Unspecified); + return err.error_struct(handle->image.get()); + } + + return Error::Ok.error_struct(handle->image.get()); +} + +#endif + + int heif_context_is_top_level_image_ID(struct heif_context* ctx, heif_item_id id) { const std::vector> images = ctx->context->get_top_level_images(true); @@ -3442,6 +3560,121 @@ struct heif_error heif_context_encode_image(struct heif_context* ctx, } +#if WITH_EXPERIMENTAL_GAIN_MAP + +struct heif_error heif_context_encode_gain_map_image( + struct heif_context* ctx, const struct heif_image_handle* base_image_handle, + struct heif_encoder* encoder, const struct heif_image* gain_map_image, + const struct heif_encoding_options* input_options, const uint8_t* gain_map_metadata, + int gain_map_metadata_len, const struct heif_color_profile_nclx* derived_image_nclx, + struct heif_image_handle** out_image_handle) { + if (!encoder) { + return Error(heif_error_Usage_error, heif_suberror_Null_pointer_argument) + .error_struct(ctx->context.get()); + } + + if (gain_map_metadata_len <= 0) { + return Error(heif_error_Invalid_input, heif_suberror_Invalid_parameter_value) + .error_struct(ctx->context.get()); + } + + if (out_image_handle) { + *out_image_handle = nullptr; + } + + // --- write tmap item + std::vector metadata; + metadata.push_back(0); // version = 0 + for (int i = 0; i < gain_map_metadata_len; i++) { + metadata.push_back(gain_map_metadata[i]); + } + heif_item_id tmap_item_id = -1; + ctx->context->add_tmap_item(metadata, tmap_item_id); + + std::vector> properties; + + // --- write ISPE property for tmap item + std::shared_ptr ispe = std::make_shared(); + ispe->set_size(base_image_handle->image->get_ispe_width(), + base_image_handle->image->get_ispe_height()); + + properties.push_back(ispe); + + // --- write PIXI property for tmap item + // TODO: this assumes the base image is 8 bit and derived image is 10 bit. Have to handle the + // other way where the base image is hdr and derived image is sdr + std::shared_ptr pixi = std::make_shared(); + pixi->add_channel_bits(10); + pixi->add_channel_bits(10); + pixi->add_channel_bits(10); + + properties.push_back(pixi); + + // --- write COLR property for tmap item + if (derived_image_nclx != nullptr) { + std::shared_ptr colr = std::make_shared(); + auto derived_img_nclx_profile = std::make_shared(); + derived_img_nclx_profile->set_colour_primaries(derived_image_nclx->color_primaries); + derived_img_nclx_profile->set_transfer_characteristics( + derived_image_nclx->transfer_characteristics); + derived_img_nclx_profile->set_matrix_coefficients(derived_image_nclx->matrix_coefficients); + derived_img_nclx_profile->set_full_range_flag(derived_image_nclx->full_range_flag); + colr->set_color_profile(derived_img_nclx_profile); + + properties.push_back(colr); + } + // set tmap item properties + for (auto& propertyBox : properties) { + int index = + ctx->context->get_heif_file()->get_ipco_box()->find_or_append_child_box(propertyBox); + ctx->context->get_heif_file()->get_ipma_box()->add_property_for_item_ID( + tmap_item_id, + Box_ipma::PropertyAssociation{propertyBox->is_essential(), uint16_t(index + 1)}); + } + + heif_encoding_options options; + set_default_encoding_options(options); + if (input_options != nullptr) { + copy_options(options, *input_options); + } + + auto gainmap_encoding_result = ctx->context->encode_image(gain_map_image->image, encoder, options, + heif_image_input_class_gain_map); + if (gainmap_encoding_result.error) { + return gainmap_encoding_result.error.error_struct(ctx->context.get()); + } + + std::shared_ptr gain_map_image_item = *gainmap_encoding_result; + Error error = + ctx->context->link_gain_map(base_image_handle->image, gain_map_image_item, tmap_item_id); + if (error != Error::Ok) { + return error.error_struct(ctx->context.get()); + } + + if (out_image_handle) { + *out_image_handle = new heif_image_handle; + (*out_image_handle)->image = std::move(gain_map_image_item); + (*out_image_handle)->context = ctx->context; + } + + // --- generate altr box + auto altr_box = std::make_shared(); + altr_box->set_short_type(fourcc("altr")); + altr_box->set_group_id(ctx->context->get_heif_file()->get_unused_item_id()); + + std::vector ids; + ids.push_back(tmap_item_id); + ids.push_back(base_image_handle->image->get_id()); + + altr_box->set_item_ids(ids); + ctx->context->get_heif_file()->add_entity_group_box(altr_box); + + return heif_error_success; +} + +#endif + + struct heif_error heif_context_encode_grid(struct heif_context* ctx, struct heif_image** tiles, uint16_t columns, diff --git a/libheif/api/libheif/heif.h b/libheif/api/libheif/heif.h index 7d969f1d..4c88c384 100644 --- a/libheif/api/libheif/heif.h +++ b/libheif/api/libheif/heif.h @@ -884,6 +884,13 @@ typedef uint32_t heif_brand2; */ #define heif_brand2_1pic heif_fourcc('1','p','i','c') +/** + * HEIF tone map brand (`tmap`). + * + * This is a compatible brand indicating the file contains a gainmap image. + */ +#define heif_brand2_tmap heif_fourcc('t', 'm', 'a', 'p') + // input data should be at least 12 bytes LIBHEIF_API heif_brand2 heif_read_main_brand(const uint8_t* data, int len); @@ -1453,6 +1460,42 @@ struct heif_error heif_image_handle_get_auxiliary_image_handle(const struct heif heif_item_id auxiliary_id, struct heif_image_handle** out_auxiliary_handle); +#if WITH_EXPERIMENTAL_GAIN_MAP + +// ------------------------- gain map images ------------------------- + +// Get the gain map image associated with the main image. If no gain map image is available, this +// method will return error. +LIBHEIF_API +struct heif_error heif_image_handle_get_gain_map_image_handle( + const struct heif_image_handle* handle, struct heif_image_handle** gain_map_handle); + +// Get the gain map metadata size associated with the main image. If no gain map image is available, +// this method will return 0 +LIBHEIF_API +size_t heif_image_handle_get_gain_map_metadata_size(const struct heif_image_handle* handle); + +// Get the gain map metadata associated with the main image. if no gain map image is available, this +// method will return error +LIBHEIF_API +struct heif_error heif_image_handle_get_gain_map_metadata(const struct heif_image_handle* handle, + void* out_data); + +// Get nclx color profile for derived image +LIBHEIF_API +struct heif_error heif_image_handle_get_derived_image_nclx_color_profile( + const struct heif_image_handle* handle, struct heif_color_profile_nclx** out_data); + +// Get raw color profile for derived image +LIBHEIF_API +size_t heif_image_handle_get_derived_image_raw_color_profile_size( + const struct heif_image_handle* handle); + +LIBHEIF_API +struct heif_error heif_image_handle_get_derived_image_raw_color_profile( + const struct heif_image_handle* handle, void* out_data); + +#endif // ------------------------- metadata (Exif / XMP) ------------------------- @@ -2621,6 +2664,22 @@ LIBHEIF_API int heif_encoder_descriptor_supportes_lossless_compression(const struct heif_encoder_descriptor*); + +#if WITH_EXPERIMENTAL_GAIN_MAP + +// Compress the gain map image and write metadata. +// Returns a handle to the coded image in 'out_image_handle' unless out_image_handle = NULL. +LIBHEIF_API +struct heif_error heif_context_encode_gain_map_image( + struct heif_context* ctx, const struct heif_image_handle* base_image_handle, + struct heif_encoder* encoder, const struct heif_image* gain_map_image, + const struct heif_encoding_options* input_options, const uint8_t* gain_map_metadata, + int gain_map_metadata_len, const struct heif_color_profile_nclx* derived_image_nclx, + struct heif_image_handle** out_image_handle); + +#endif + + #ifdef __cplusplus } #endif diff --git a/libheif/api/libheif/heif_plugin.h b/libheif/api/libheif/heif_plugin.h index 3a438bfc..533a1ebc 100644 --- a/libheif/api/libheif/heif_plugin.h +++ b/libheif/api/libheif/heif_plugin.h @@ -129,6 +129,9 @@ enum heif_image_input_class heif_image_input_class_alpha = 2, heif_image_input_class_depth = 3, heif_image_input_class_thumbnail = 4 +#if WITH_EXPERIMENTAL_GAIN_MAP + , heif_image_input_class_gain_map = 5 +#endif }; diff --git a/libheif/context.cc b/libheif/context.cc index ab1cbb00..67d61f58 100644 --- a/libheif/context.cc +++ b/libheif/context.cc @@ -337,10 +337,12 @@ Error HeifContext::interpret_heif_file() m_top_level_images.clear(); m_primary_image.reset(); - // --- reference all non-hidden images std::vector image_IDs = m_heif_file->get_item_IDs(); +#if WITH_EXPERIMENTAL_GAIN_MAP + bool check_tmap_item = m_heif_file->get_ftyp_box()->has_compatible_brand(heif_brand2_tmap); +#endif for (heif_item_id id : image_IDs) { auto infe_box = m_heif_file->get_infe_box(id); @@ -807,6 +809,12 @@ Error HeifContext::interpret_heif_file() // these item types should have data return err; } +#if WITH_EXPERIMENTAL_GAIN_MAP + else if (check_tmap_item && item_type == fourcc("tmap")) { + // this also should have data + return err; + } +#endif else { // anything else is probably something that we don't understand yet continue; @@ -831,6 +839,41 @@ Error HeifContext::interpret_heif_file() } img_iter->second->add_metadata(metadata); } +#if WITH_EXPERIMENTAL_GAIN_MAP + if (check_tmap_item && item_type == fourcc("tmap")) { + std::vector image_references = iref_box->get_references(id, fourcc("dimg")); + // "'tmap' item MUST be associated with 2 references to images" + // "'tmap' item first entry is expected to be referencing primary image" + // "'tmap' item reference entries MUST not be duplicate" + // "'tmap' item references MUST be pointing to valid image items" + if ((int)image_references.size() == 2 && + image_references[0] == m_heif_file->get_primary_image_ID() && + image_references[1] != image_references[0] && + m_all_images.find(image_references[0]) != m_all_images.end() && + m_all_images.find(image_references[1]) != m_all_images.end()) { + std::shared_ptr baseItem = m_all_images.find(image_references[0])->second; + std::shared_ptr gainmapItem = m_all_images.find(image_references[1])->second; + + baseItem->set_gain_map(gainmapItem); + + baseItem->add_metadata(metadata); // gain map metadata + + auto ipma = m_heif_file->get_ipma_box(); + auto ipco = m_heif_file->get_ipco_box(); + auto derived_image_colr = ipco->get_property_for_item_ID(id, ipma, fourcc("colr")); + auto colr = std::dynamic_pointer_cast(derived_image_colr); + auto nclx = + std::dynamic_pointer_cast(colr->get_color_profile()); + if (nclx) { + baseItem->set_derived_img_color_profile(nclx); + } + auto raw = std::dynamic_pointer_cast(colr->get_color_profile()); + if (raw) { + baseItem->set_derived_img_color_profile(raw); + } + } + } +#endif } } @@ -1170,6 +1213,29 @@ create_alpha_image_from_image_alpha_channel(const std::shared_ptr& data, heif_item_id& item_id) { + auto tmap_infe = m_heif_file->add_new_infe_box(fourcc("tmap")); // gain map metadata + tmap_infe->set_item_name("GMap"); + item_id = tmap_infe->get_item_ID(); + + m_heif_file->append_iloc_data(item_id, data, 0); + + return Error::Ok; +} + + +Error HeifContext::link_gain_map(const std::shared_ptr& primary_image, + const std::shared_ptr& gain_map_image, + const heif_item_id tmap_id) { + m_heif_file->add_iref_reference(tmap_id, fourcc("dimg"), + {primary_image->get_id(), gain_map_image->get_id()}); + + return Error::Ok; +} +#endif + + Result> HeifContext::encode_image(const std::shared_ptr& pixel_image, struct heif_encoder* encoder, const struct heif_encoding_options& in_options, diff --git a/libheif/context.h b/libheif/context.h index 3f4f0bc3..48e86c8a 100644 --- a/libheif/context.h +++ b/libheif/context.h @@ -47,6 +47,7 @@ class StreamWriter; class ImageItem; +class ImageMetadata; // This is a higher-level view than HeifFile. // Images are grouped logically into main images and their thumbnails. @@ -147,6 +148,14 @@ public: Result add_pyramid_group(const std::vector& layers); +#if WITH_EXPERIMENTAL_GAIN_MAP + Error add_tmap_item(const std::vector& metadata, heif_item_id& item_id); + + Error link_gain_map(const std::shared_ptr& primary_image, + const std::shared_ptr& gain_map_image, const heif_item_id tmap_id); +#endif + + // --- region items void add_region_item(std::shared_ptr region_item) diff --git a/libheif/file.cc b/libheif/file.cc index 8a678bf9..e725f4db 100644 --- a/libheif/file.cc +++ b/libheif/file.cc @@ -192,6 +192,9 @@ void HeifFile::set_brand(heif_compression_format format, bool miaf_compatible) m_ftyp_box->set_minor_version(0); m_ftyp_box->add_compatible_brand(heif_brand2_mif1); m_ftyp_box->add_compatible_brand(heif_brand2_heic); +#if WITH_EXPERIMENTAL_GAIN_MAP + m_ftyp_box->add_compatible_brand(heif_brand2_tmap); +#endif break; case heif_compression_AV1: @@ -199,6 +202,9 @@ void HeifFile::set_brand(heif_compression_format format, bool miaf_compatible) m_ftyp_box->set_minor_version(0); m_ftyp_box->add_compatible_brand(heif_brand2_avif); m_ftyp_box->add_compatible_brand(heif_brand2_mif1); +#if WITH_EXPERIMENTAL_GAIN_MAP + m_ftyp_box->add_compatible_brand(heif_brand2_tmap); +#endif break; case heif_compression_VVC: @@ -352,6 +358,9 @@ Error HeifFile::parse_heif_file() !m_ftyp_box->has_compatible_brand(heif_brand2_1pic) && #if ENABLE_EXPERIMENTAL_MINI_FORMAT !(m_ftyp_box->get_major_brand() == heif_brand2_mif3) && +#endif +#if WITH_EXPERIMENTAL_GAIN_MAP + !(m_ftyp_box->get_major_brand() == heif_brand2_tmap) && #endif !m_ftyp_box->has_compatible_brand(heif_brand2_jpeg)) { std::stringstream sstr; diff --git a/libheif/image-items/image_item.cc b/libheif/image-items/image_item.cc index 549880b6..76dbaf2e 100644 --- a/libheif/image-items/image_item.cc +++ b/libheif/image-items/image_item.cc @@ -386,6 +386,17 @@ Error ImageItem::encode_to_item(HeifContext* ctx, auto infe_box = ctx->get_heif_file()->add_new_infe_box(get_infe_type()); heif_item_id image_id = infe_box->get_item_ID(); set_id(image_id); +#if WITH_EXPERIMENTAL_GAIN_MAP + if (input_class == heif_image_input_class_gain_map) { + if (encoder->plugin->compression_format != heif_compression_HEVC && + encoder->plugin->compression_format != heif_compression_AV1) { + return Error(heif_error_Encoder_plugin_error, heif_suberror_Unsupported_codec); + } + infe_box->set_item_name("GMap"); + infe_box->set_hidden_item(true); + } +#endif + ctx->get_heif_file()->append_iloc_data(image_id, codedImage.bitstream, 0); @@ -646,7 +657,13 @@ void ImageItem::add_color_profile(const std::shared_ptr& image, const heif_color_profile_nclx* target_heif_nclx, ImageItem::CodedImageData& inout_codedImage) { +#if WITH_EXPERIMENTAL_GAIN_MAP + if (input_class == heif_image_input_class_normal || + input_class == heif_image_input_class_thumbnail || + input_class == heif_image_input_class_gain_map) { +#else if (input_class == heif_image_input_class_normal || input_class == heif_image_input_class_thumbnail) { +#endif auto icc_profile = image->get_color_profile_icc(); if (icc_profile) { auto colr = std::make_shared(); diff --git a/libheif/image-items/image_item.h b/libheif/image-items/image_item.h index 5f0d2b2b..8a9fdb32 100644 --- a/libheif/image-items/image_item.h +++ b/libheif/image-items/image_item.h @@ -82,12 +82,14 @@ public: virtual Result> read_bitstream_configuration_data() const { return std::vector{}; } - void clear() - { + void clear() { m_thumbnails.clear(); m_alpha_channel.reset(); m_depth_channel.reset(); m_aux_images.clear(); +#if WITH_EXPERIMENTAL_GAIN_MAP + m_gain_map_image.reset(); +#endif } HeifContext* get_context() { return m_heif_context; } @@ -264,6 +266,44 @@ public: } } +#if WITH_EXPERIMENTAL_GAIN_MAP + // --- gain map + + const std::shared_ptr& get_gain_map() const { return m_gain_map_image; } + + std::shared_ptr get_gain_map_metadata() { + if (m_gain_map_image != nullptr) { + for (auto it : m_metadata) { + if (it->item_type == "tmap") { + return it; + } + } + } + return nullptr; + } + + const std::shared_ptr& get_derived_img_color_profile_nclx() const { + return m_derived_img_color_profile_nclx; + } + + const std::shared_ptr& get_derived_img_color_profile_icc() const { + return m_derived_img_color_profile_icc; + } + + void set_gain_map(std::shared_ptr img) { m_gain_map_image = std::move(img); } + + void set_derived_img_color_profile(const std::shared_ptr& profile) { + auto icc = std::dynamic_pointer_cast(profile); + if (icc) { + m_derived_img_color_profile_icc = std::move(icc); + } + + auto nclx = std::dynamic_pointer_cast(profile); + if (nclx) { + m_derived_img_color_profile_nclx = std::move(nclx); + } + }; +#endif // --- metadata @@ -417,10 +457,18 @@ private: std::string m_aux_image_type; std::vector> m_aux_images; +#if WITH_EXPERIMENTAL_GAIN_MAP + std::shared_ptr m_gain_map_image; +#endif + std::vector> m_metadata; std::shared_ptr m_color_profile_nclx; std::shared_ptr m_color_profile_icc; +#if WITH_EXPERIMENTAL_GAIN_MAP + std::shared_ptr m_derived_img_color_profile_nclx; + std::shared_ptr m_derived_img_color_profile_icc; +#endif bool m_miaf_compatible = true; diff --git a/libheif/plugins/encoder_aom.cc b/libheif/plugins/encoder_aom.cc index 9c01a50e..c16ea30b 100644 --- a/libheif/plugins/encoder_aom.cc +++ b/libheif/plugins/encoder_aom.cc @@ -1073,7 +1073,12 @@ struct heif_error aom_encode_image(void* encoder_raw, const struct heif_image* i if (nclx && (input_class == heif_image_input_class_normal || +#if WITH_EXPERIMENTAL_GAIN_MAP + input_class == heif_image_input_class_thumbnail || + input_class == heif_image_input_class_gain_map)) { +#else input_class == heif_image_input_class_thumbnail)) { +#endif aom_error = aom_codec_control(&codec, AV1E_SET_COLOR_PRIMARIES, nclx->color_primaries); CHECK_ERROR aom_error = aom_codec_control(&codec, AV1E_SET_MATRIX_COEFFICIENTS, nclx->matrix_coefficients); CHECK_ERROR; aom_error = aom_codec_control(&codec, AV1E_SET_TRANSFER_CHARACTERISTICS, nclx->transfer_characteristics); CHECK_ERROR; diff --git a/libheif/plugins/encoder_kvazaar.cc b/libheif/plugins/encoder_kvazaar.cc index 9db79a6c..6acb35b7 100644 --- a/libheif/plugins/encoder_kvazaar.cc +++ b/libheif/plugins/encoder_kvazaar.cc @@ -567,7 +567,12 @@ static struct heif_error kvazaar_encode_image(void* encoder_raw, const struct he if (nclx && (input_class == heif_image_input_class_normal || +#if WITH_EXPERIMENTAL_GAIN_MAP + input_class == heif_image_input_class_thumbnail || + input_class == heif_image_input_class_gain_map)) { +#else input_class == heif_image_input_class_thumbnail)) { +#endif config->vui.colorprim = nclx->color_primaries; config->vui.transfer = nclx->transfer_characteristics; config->vui.colormatrix = nclx->matrix_coefficients; diff --git a/libheif/plugins/encoder_rav1e.cc b/libheif/plugins/encoder_rav1e.cc index 6a0ca759..7e0fbac8 100644 --- a/libheif/plugins/encoder_rav1e.cc +++ b/libheif/plugins/encoder_rav1e.cc @@ -550,7 +550,12 @@ struct heif_error rav1e_encode_image(void* encoder_raw, const struct heif_image* if (nclx && (input_class == heif_image_input_class_normal || +#if WITH_EXPERIMENTAL_GAIN_MAP + input_class == heif_image_input_class_thumbnail || + input_class == heif_image_input_class_gain_map)) { +#else input_class == heif_image_input_class_thumbnail)) { +#endif if (rav1e_config_set_color_description(rav1eConfig.get(), (RaMatrixCoefficients) nclx->matrix_coefficients, (RaColorPrimaries) nclx->color_primaries, diff --git a/libheif/plugins/encoder_x265.cc b/libheif/plugins/encoder_x265.cc index d4e1b016..2544873d 100644 --- a/libheif/plugins/encoder_x265.cc +++ b/libheif/plugins/encoder_x265.cc @@ -806,7 +806,12 @@ static struct heif_error x265_encode_image(void* encoder_raw, const struct heif_ if (nclx && (input_class == heif_image_input_class_normal || +#if WITH_EXPERIMENTAL_GAIN_MAP + input_class == heif_image_input_class_thumbnail || + input_class == heif_image_input_class_gain_map)) { +#else input_class == heif_image_input_class_thumbnail)) { +#endif { std::stringstream sstr; diff --git a/libheif/security_limits.cc b/libheif/security_limits.cc index bba4bb85..eb318166 100644 --- a/libheif/security_limits.cc +++ b/libheif/security_limits.cc @@ -23,30 +23,30 @@ struct heif_security_limits global_security_limits { - .version = 1, + 1, // --- version 1 // Artificial limit to avoid allocating too much memory. // 32768^2 = 1.5 GB as YUV-4:2:0 or 4 GB as RGB32 - .max_image_size_pixels = 32768 * 32768, - .max_number_of_tiles = 4096 * 4096, - .max_bayer_pattern_pixels = 16*16, - .max_items = 1000, + 32768ULL * 32768ULL, + 4096ULL * 4096ULL, + 16 * 16, + 1000, - .max_color_profile_size = 100 * 1024 * 1024, // 100 MB - .max_memory_block_size = 512 * 1024 * 1024, // 512 MB + 100 * 1024 * 1024, // 100 MB + 512 * 1024 * 1024, // 512 MB - .max_components = 256, - .max_iloc_extents_per_item = 32, - .max_size_entity_group = 64, + 256, + 32, + 64, - .max_children_per_box = 100 + 100 }; struct heif_security_limits disabled_security_limits{ - .version = 1 + 1 }; libultrahdr-2.0.2/cmake/toolchains/000077500000000000000000000000001523721266400172625ustar00rootroot00000000000000libultrahdr-2.0.2/cmake/toolchains/aarch64-linux-gnu.cmake000066400000000000000000000017671523721266400234530ustar00rootroot00000000000000# # Copyright (C) 2023 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 or the MIT license # , at your # option. This file may not be copied, modified, or distributed # except according to those terms. # if(UHDR_BUILD_CMAKE_TOOLCHAINS_AARCH64_LINUX_GNU_CMAKE_) return() endif() set(UHDR_BUILD_CMAKE_TOOLCHAINS_AARCH64_LINUX_GNU_CMAKE_ 1) set(CMAKE_SYSTEM_NAME "Linux") set(CMAKE_SYSTEM_PROCESSOR "aarch64") if("${CROSS}" STREQUAL "") set(CROSS aarch64-linux-gnu-) endif() if(NOT CMAKE_C_COMPILER) set(CMAKE_C_COMPILER ${CROSS}gcc) endif() if(NOT CMAKE_CXX_COMPILER) set(CMAKE_CXX_COMPILER ${CROSS}g++) endif() if(NOT AS_EXECUTABLE) set(AS_EXECUTABLE ${CROSS}as) endif() set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) libultrahdr-2.0.2/cmake/toolchains/android.cmake000066400000000000000000000023421523721266400217050ustar00rootroot00000000000000# # Copyright (C) 2023 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 or the MIT license # , at your # option. This file may not be copied, modified, or distributed # except according to those terms. # if(UHDR_BUILD_CMAKE_TOOLCHAINS_ANDROID_CMAKE_) return() endif() set(UHDR_BUILD_CMAKE_TOOLCHAINS_ANDROID_CMAKE_ 1) if(NOT ANDROID_PLATFORM) set(ANDROID_PLATFORM android-23) endif() # Choose target architecture with: # -DANDROID_ABI={armeabi-v7a, arm64-v8a, x86, x86_64} if(NOT ANDROID_ABI) set(ANDROID_ABI arm64-v8a) endif() # Toolchain files don't have access to cached variables: # https://gitlab.kitware.com/cmake/cmake/issues/16170. Set an intermediate # environment variable when loaded the first time. if(UHDR_ANDROID_NDK_PATH) set(ENV{UHDR_ANDROID_NDK_PATH} "${UHDR_ANDROID_NDK_PATH}") else() set(UHDR_ANDROID_NDK_PATH "$ENV{UHDR_ANDROID_NDK_PATH}") endif() if(NOT UHDR_ANDROID_NDK_PATH) message(FATAL_ERROR "UHDR_ANDROID_NDK_PATH not set.") return() endif() include("${UHDR_ANDROID_NDK_PATH}/build/cmake/android.toolchain.cmake") set(CMAKE_SYSTEM_NAME "Android") libultrahdr-2.0.2/cmake/toolchains/arm-linux-gnueabihf.cmake000066400000000000000000000017731523721266400241360ustar00rootroot00000000000000# # Copyright (C) 2023 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 or the MIT license # , at your # option. This file may not be copied, modified, or distributed # except according to those terms. # if(UHDR_BUILD_CMAKE_TOOLCHAINS_ARM_LINUX_GNUEABIHF_CMAKE_) return() endif() set(UHDR_BUILD_CMAKE_TOOLCHAINS_ARM_LINUX_GNUEABIHF_CMAKE_ 1) set(CMAKE_SYSTEM_NAME "Linux") set(CMAKE_SYSTEM_PROCESSOR "armv7") if("${CROSS}" STREQUAL "") set(CROSS arm-linux-gnueabihf-) endif() if(NOT CMAKE_C_COMPILER) set(CMAKE_C_COMPILER ${CROSS}gcc) endif() if(NOT CMAKE_CXX_COMPILER) set(CMAKE_CXX_COMPILER ${CROSS}g++) endif() if(NOT AS_EXECUTABLE) set(AS_EXECUTABLE ${CROSS}as) endif() set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) libultrahdr-2.0.2/cmake/toolchains/loong64-linux-gnu.cmake000066400000000000000000000017771523721266400235140ustar00rootroot00000000000000# # Copyright (C) 2023 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 or the MIT license # , at your # option. This file may not be copied, modified, or distributed # except according to those terms. # if(UHDR_BUILD_CMAKE_TOOLCHAINS_LOONG64_LINUX_GNU_CMAKE_) return() endif() set(UHDR_BUILD_CMAKE_TOOLCHAINS_LOONG64_LINUX_GNU_CMAKE_ 1) set(CMAKE_SYSTEM_NAME "Linux") set(CMAKE_SYSTEM_PROCESSOR "loongarch64") if("${CROSS}" STREQUAL "") set(CROSS loongarch64-linux-gnu-) endif() if(NOT CMAKE_C_COMPILER) set(CMAKE_C_COMPILER ${CROSS}gcc) endif() if(NOT CMAKE_CXX_COMPILER) set(CMAKE_CXX_COMPILER ${CROSS}g++) endif() if(NOT AS_EXECUTABLE) set(AS_EXECUTABLE ${CROSS}as) endif() set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) libultrahdr-2.0.2/cmake/toolchains/riscv32-linux-gnu.cmake000066400000000000000000000017671523721266400235160ustar00rootroot00000000000000# # Copyright (C) 2023 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 or the MIT license # , at your # option. This file may not be copied, modified, or distributed # except according to those terms. # if(UHDR_BUILD_CMAKE_TOOLCHAINS_RISCV32_LINUX_GNU_CMAKE_) return() endif() set(UHDR_BUILD_CMAKE_TOOLCHAINS_RISCV32_LINUX_GNU_CMAKE_ 1) set(CMAKE_SYSTEM_NAME "Linux") set(CMAKE_SYSTEM_PROCESSOR "riscv32") if("${CROSS}" STREQUAL "") set(CROSS riscv32-linux-gnu-) endif() if(NOT CMAKE_C_COMPILER) set(CMAKE_C_COMPILER ${CROSS}gcc) endif() if(NOT CMAKE_CXX_COMPILER) set(CMAKE_CXX_COMPILER ${CROSS}g++) endif() if(NOT AS_EXECUTABLE) set(AS_EXECUTABLE ${CROSS}as) endif() set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) libultrahdr-2.0.2/cmake/toolchains/riscv64-linux-gnu.cmake000066400000000000000000000017671523721266400235230ustar00rootroot00000000000000# # Copyright (C) 2023 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 or the MIT license # , at your # option. This file may not be copied, modified, or distributed # except according to those terms. # if(UHDR_BUILD_CMAKE_TOOLCHAINS_RISCV64_LINUX_GNU_CMAKE_) return() endif() set(UHDR_BUILD_CMAKE_TOOLCHAINS_RISCV64_LINUX_GNU_CMAKE_ 1) set(CMAKE_SYSTEM_NAME "Linux") set(CMAKE_SYSTEM_PROCESSOR "riscv64") if("${CROSS}" STREQUAL "") set(CROSS riscv64-linux-gnu-) endif() if(NOT CMAKE_C_COMPILER) set(CMAKE_C_COMPILER ${CROSS}gcc) endif() if(NOT CMAKE_CXX_COMPILER) set(CMAKE_CXX_COMPILER ${CROSS}g++) endif() if(NOT AS_EXECUTABLE) set(AS_EXECUTABLE ${CROSS}as) endif() set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) libultrahdr-2.0.2/docs/000077500000000000000000000000001523721266400147675ustar00rootroot00000000000000libultrahdr-2.0.2/docs/api_changes_v1.5_to_v2.0.md000066400000000000000000000146131523721266400215770ustar00rootroot00000000000000# UltraHDR API Changes (v1.5.0 to Potential v2.0.0) This document details the architectural and API changes planned for the transition from **UltraHDR v1.5.0** (and v1.5.1) to **Potential v2.0.0**, focusing on multi-format container support (HEIF & AVIF), C++ class hierarchy reorganization, C-API extensions, and `libheif` integration. --- ## 1. Executive Summary | Feature / Aspect | UltraHDR v1.5.0 / v1.5.1 | Potential UltraHDR v2.0.0 | | :--- | :--- | :--- | | **Supported Formats** | JPEG (`.jpg`, `.jpeg`) only | JPEG, HEIF/HEIC (`.heic`), and AVIF (`.avif`) | | **C-API Codec Enums** | `UHDR_CODEC_HEIF` & `UHDR_CODEC_AVIF` reserved | `UHDR_CODEC_HEIF` & `UHDR_CODEC_AVIF` fully implemented | | **C++ Class Architecture** | Monolithic `JpegR` class for all operations | Refactored `UltraHdr` base class with `JpegR`, `HeifUltraHdr`, and `AvifUltraHdr` subclasses | | **Decoder Transparency** | Probes JPEG markers only | Format-agnostic probe and decode across JPEG, HEIF, and AVIF | | **Underlying HEIF Engine** | None | `libheif` with ISO 21496-1 tone map (`tmap`) item support | | **Conditional Build** | N/A | `UHDR_ENABLE_HEIF` compile-time flag (zero overhead when disabled) | --- ## 2. C-API (`ultrahdr_api.h`) Updates The public C-ABI interface remains backwards-compatible, while activating multi-format encoding and decoding. ### 2.1. Encoder Format Selection Callers can configure the target output container using `uhdr_enc_set_output_format()`: ```c // JPEG Output (Default / Existing v1.x behavior) uhdr_enc_set_output_format(enc_handle, UHDR_CODEC_JPG); // HEIF / HEIC Output (HEVC base + Gain map in ISO BMFF container) uhdr_enc_set_output_format(enc_handle, UHDR_CODEC_HEIF); // AVIF Output (AV1 base + Gain map in ISO BMFF container) uhdr_enc_set_output_format(enc_handle, UHDR_CODEC_AVIF); ``` ### 2.2. Supported Encode Intent Modes for HEIF/AVIF * **Encode API-0 (HDR Raw Intent)**: * Input: Raw HDR image (`uhdr_enc_set_raw_image(..., UHDR_HDR_IMG)`). * Automatically tone-maps to SDR base, computes gain map, compresses both via `libheif`, and encapsulates metadata. * **Encode API-1 (HDR + SDR Raw Intents)**: * Input: Raw HDR image + Raw SDR image. * Generates gain map from the difference, compresses SDR and gain map into HEIF/AVIF container. * **Encode API-2 / 3 / 4 (Pre-compressed Intents)**: * Pre-compressed JPEG intents are specific to JPEG workflows. For HEIF/AVIF, pre-compressed intent transcoding is not supported and returns `UHDR_CODEC_UNSUPPORTED_FEATURE`. ### 2.3. Unified Transparent Decoding The decoder APIs (`uhdr_dec_probe()` and `uhdr_decode()`) automatically detect container type from the stream header: * Checks magic bytes: JPEG markers (`0xFF 0xD8 0xFF`) vs ISO BMFF `ftyp` box (`heic`, `avif`, `mif1`, etc.). * Transparently routes decoding to `JpegR`, `HeifUltraHdr`, or `AvifUltraHdr`. * Extracts primary image, gain map auxiliary track, ISO 21496-1 metadata, and color profiles seamlessly into the destination buffer. --- ## 3. C++ Class Hierarchy Reorganization To support multiple container formats without duplicating gain map math, the monolithic `JpegR` class has been modularized: ``` ┌────────────────────────� │ UltraHdr │ │ (ultrahdrcommon.h) │ └───────────┬────────────┘ │ ┌────────────────────┼────────────────────� │ │ │ ▼ ▼ ▼ ┌───────────� ┌───────────────� ┌───────────────� │ JpegR │ │ HeifUltraHdr │ │ AvifUltraHdr │ │ (jpegr.h) │ │(heifultrahdr.h│ │(avifultrahdr.h│ └───────────┘ └───────────────┘ └───────────────┘ ``` ### 3.1. `UltraHdr` Base Class (``) Contains all shared, format-independent algorithms: * Gain map generation (`generateGainMap()`). * Gain map application and tone mapping math (`applyGainMap()`). * Color space transformations, primaries/matrix conversions, and ICC profile handling. * Metadata encoding and decoding (`uhdr_gainmap_metadata_ext_t`). ### 3.2. Format-Specific Derived Classes * **`JpegR`** (``): * Handles JPEG MPF (Multi-Picture Format), EXIF, and XMP marker parsing and packing. * Methods: `encodeJpegR()`, `decodeJpegR()`. * **`HeifUltraHdr`** (``): * Handles HEVC bitstream encoding/decoding via `libheif`. * Methods: `encodeHeicUltraHdr()`, `decodeHeicUltraHdr()`. * **`AvifUltraHdr`** (``): * Handles AV1 bitstream encoding/decoding via `libheif`. * Methods: `encodeAvifUltraHdr()`, `decodeAvifUltraHdr()`. --- ## 4. `libheif` & ISO 21496-1 Container Encapsulation Under v2.0.0, HEIF and AVIF Ultra HDR images adhere to ISO/IEC 23008-12 and ISO 21496-1: 1. **Derived Tone Map Item (`tmap`)**: The gain map metadata is stored as an uncompressed `tmap` item with type `fourcc("tmap")`. 2. **Item References (`dimg`)**: The `tmap` item references the base image (index 0) and the gain map image (index 1) via a `dimg` (derived image) reference box. 3. **Alternate Entity Grouping (`altr`)**: An `altr` box groups the tone-mapped item with the base image to signal alternative presentations. 4. **Color Profiles**: NCLX (`colr`) and ICC profile boxes are attached directly to the derived item and gain map image item. --- ## 5. Build System & Portability ### 5.1. Compile-Time Guard (`UHDR_ENABLE_HEIF`) * When **enabled**: Links `libheif`, enabling HEIC and AVIF support. * When **disabled**: `libultrahdr` builds as a standalone, zero-dependency JPEG-only library. ### 5.2. Build Configurations * **Google3 (`BUILD`)**: Linked via `//third_party/libheif:libheif` with `UHDR_ENABLE_HEIF`. * **Android (`Android.bp`)**: Standard AOSP build configuration (kept untouched on the staging branch). * **Upstream CMake (`CMakeLists.txt`)**: Enabled via `-DUHDR_ENABLE_HEIF=ON` using `find_package(libheif)` or `pkg-config`. Fully compatible with Linux and macOS (Apple Silicon & Intel). libultrahdr-2.0.2/docs/building.md000066400000000000000000000432471523721266400171200ustar00rootroot00000000000000## libultrahdr CMake Build Instructions [![Build Status](https://github.com/google/libultrahdr/actions/workflows/cmake_linux.yml/badge.svg?event=push)](https://github.com/google/libultrahdr/actions/workflows/cmake_linux.yml?query=event%3Apush) [![Build Status](https://github.com/google/libultrahdr/actions/workflows/cmake_mac.yml/badge.svg?event=push)](https://github.com/google/libultrahdr/actions/workflows/cmake_mac.yml?query=event%3Apush) [![Build Status](https://github.com/google/libultrahdr/actions/workflows/cmake_win.yml/badge.svg?event=push)](https://github.com/google/libultrahdr/actions/workflows/cmake_win.yml?query=event%3Apush) [![Build Status](https://github.com/google/libultrahdr/actions/workflows/cmake_android.yml/badge.svg?event=push)](https://github.com/google/libultrahdr/actions/workflows/cmake_android.yml?query=event%3Apush) [![Fuzz Status](https://oss-fuzz-build-logs.storage.googleapis.com/badges/libultrahdr.svg)](https://introspector.oss-fuzz.com/project-profile?project=libultrahdr) ### Requirements - [CMake](http://www.cmake.org) v3.15 or later - C++ compiler, supporting at least C++17. - libultrahdr uses JPEG compression format to store SDR images and gainmap quotients. So, libjpeg or any other JPEG codec that is ABI and API compatible with libjpeg is required for JPEG support. - libultrahdr supports ISO 21496-1 gain map encoding and decoding in HEIF and AVIF containers via [libheif](https://github.com/strukturag/libheif). The library offers a way to skip installing dependencies manually by passing `UHDR_BUILD_DEPS=1` at configure time. That is, `cmake -DUHDR_BUILD_DEPS=1` will clone [libjpeg-turbo](https://github.com/libjpeg-turbo/libjpeg-turbo.git) and [libheif](https://github.com/strukturag/libheif.git) (with the ISO 21496-1 gain map patch applied) and build them statically. If jpeg is included in the build process then, - C compiler - For building x86/x86_64 SIMD optimizations, [NASM](http://www.nasm.us) or [Yasm](http://yasm.tortall.net). * If using NASM, 2.13 or later is required. * If using Yasm, 1.2.0 or later is required. ### CMake Options There are a few options that can be passed to CMake to modify how the code is built.
To set these options and parameters, use `-D=`. All CMake options are passed at configure time, i.e., by running `cmake -DOPTION_ONE=1 -DOPTION_TWO=0 ...`
before running `cmake --build ...`
For example, to build unit tests in a new subdirectory called 'build', run: ```sh cmake -G "Unix Makefiles" -S. -Bbuild -DUHDR_BUILD_TESTS=1 ../ ``` and then build with: ```sh cmake --build build ``` Following is a list of available options: | CMake Option | Default Value | Notes | |:-------------|:--------------|:-----| | `CMAKE_BUILD_TYPE` | Release | See CMake documentation [here](https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html). | | `BUILD_SHARED_LIBS` | ON | See CMake documentation [here](https://cmake.org/cmake/help/latest/variable/BUILD_SHARED_LIBS.html).
  • If `BUILD_SHARED_LIBS` is **OFF**, in the linking phase, static versions of dependencies are chosen. However, the executable targets are not purely static because the system libraries used are still dynamic.
| | `UHDR_BUILD_EXAMPLES` | ON | Build sample application. This application demonstrates how to use [ultrahdr_api.h](../ultrahdr_api.h). | | `UHDR_BUILD_TESTS` | OFF | Build Unit Tests. Mostly for Devs. During development, different modules of libuhdr library are validated using GoogleTest framework. Developers after making changes to library are expected to run these tests to ensure every thing is functional. | | `UHDR_BUILD_BENCHMARK` | OFF | Build Benchmark Tests. These are for profiling libuhdr encode/decode API. Resources used by benchmark tests are shared [here](https://storage.googleapis.com/android_media/external/libultrahdr/benchmark/UltrahdrBenchmarkTestRes-1.1.zip). These are downloaded and extracted automatically during the build process for later benchmarking. | | `UHDR_BUILD_FUZZERS` | OFF | Build Fuzz Test Applications. Mostly for Devs.
  • Fuzz applications are built by instrumenting the entire software suite. This includes dependency libraries. This is done by forcing `UHDR_BUILD_DEPS` to **ON** internally.
| | `UHDR_BUILD_DEPS` | OFF | Clone and Build project dependencies (libjpeg-turbo, libheif with ISO 21496-1 patch, googletest, benchmark) and not use pre-installed packages. | | `UHDR_BUILD_JAVA` | OFF | Build JNI wrapper, Java front-end classes and Java sample application. | | `UHDR_ENABLE_LOGS` | OFF | Build with verbose logging. | | `UHDR_ENABLE_INSTALL` | ON | Enable install and uninstall targets for libuhdr package.