aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDichenZhang1 <140119224+DichenZhang1@users.noreply.github.com>2023-11-07 20:42:34 -0800
committerGitHub <noreply@github.com>2023-11-07 20:42:34 -0800
commit77dfb1591c60b4ec9b763a98c683287ec39b43a9 (patch)
tree2ea862ae068c50ccb972b062dcb2c02e6816b424
parentd1ad9a00076ec1ae593d0957f87a2d5921fc852d (diff)
parentd4fa59c473584ba9a5ee7de861d093a464896ca0 (diff)
downloadlibultrahdr-77dfb1591c60b4ec9b763a98c683287ec39b43a9.tar.gz
Merge pull request #36 from ittiam-systems/updates
Updates
-rw-r--r--.clang-format7
-rw-r--r--.github/workflows/cmake.yml6
-rw-r--r--Android.bp22
-rw-r--r--CMakeLists.txt374
-rw-r--r--README.md77
-rw-r--r--examples/Android.bp41
-rw-r--r--examples/ultrahdr_app.cpp991
-rw-r--r--fuzzer/README.md145
-rwxr-xr-xfuzzer/ossfuzz.sh2
-rw-r--r--fuzzer/ultrahdr_dec_fuzzer.cpp64
-rw-r--r--fuzzer/ultrahdr_enc_fuzzer.cpp490
-rw-r--r--icc.cpp688
-rw-r--r--include/ultrahdr/icc.h263
-rw-r--r--include/ultrahdr/jpegdecoderhelper.h155
-rw-r--r--include/ultrahdr/jpegencoderhelper.h106
-rw-r--r--include/ultrahdr/jpegr.h465
-rw-r--r--include/ultrahdr/ultrahdrcommon.h64
-rw-r--r--jpegdecoderhelper.cpp567
-rw-r--r--jpegencoderhelper.cpp294
-rw-r--r--jpegrutils.cpp601
-rw-r--r--lib/gainmapmath.cpp (renamed from gainmapmath.cpp)325
-rw-r--r--lib/gainmapmath.h (renamed from include/ultrahdr/gainmapmath.h)51
-rw-r--r--lib/icc.cpp680
-rw-r--r--lib/icc.h259
-rw-r--r--lib/jpegdecoderhelper.cpp537
-rw-r--r--lib/jpegdecoderhelper.h154
-rw-r--r--lib/jpegencoderhelper.cpp287
-rw-r--r--lib/jpegencoderhelper.h106
-rw-r--r--lib/jpegr.cpp (renamed from jpegr.cpp)408
-rw-r--r--lib/jpegr.h464
-rw-r--r--lib/jpegrutils.cpp583
-rw-r--r--lib/jpegrutils.h (renamed from include/ultrahdr/jpegrutils.h)49
-rw-r--r--lib/multipictureformat.cpp92
-rw-r--r--lib/multipictureformat.h (renamed from include/ultrahdr/multipictureformat.h)18
-rw-r--r--lib/ultrahdr.h (renamed from include/ultrahdr/ultrahdr.h)10
-rw-r--r--lib/ultrahdrcommon.h64
-rw-r--r--multipictureformat.cpp94
-rw-r--r--tests/Android.bp21
-rw-r--r--tests/data/LICENSE (renamed from third_party/data/LICENSE)0
-rw-r--r--tests/data/jpeg_image.jpg (renamed from third_party/data/jpeg_image.jpg)bin24430 -> 24430 bytes
-rw-r--r--tests/data/minnie-318x240.yu12 (renamed from third_party/data/minnie-318x240.yu12)0
-rw-r--r--tests/data/minnie-320x240-y.jpg (renamed from third_party/data/minnie-320x240-y.jpg)bin20193 -> 20193 bytes
-rw-r--r--tests/data/minnie-320x240-yuv-icc.jpg (renamed from third_party/data/minnie-320x240-yuv-icc.jpg)bin34266 -> 34266 bytes
-rw-r--r--tests/data/minnie-320x240-yuv.jpg (renamed from third_party/data/minnie-320x240-yuv.jpg)bin20193 -> 20193 bytes
-rw-r--r--tests/data/minnie-320x240.y (renamed from third_party/data/minnie-320x240.y)0
-rw-r--r--tests/data/minnie-320x240.yu12 (renamed from third_party/data/minnie-320x240.yu12)0
-rw-r--r--tests/data/raw_p010_image.p010 (renamed from third_party/data/raw_p010_image.p010)bin2764800 -> 2764800 bytes
-rw-r--r--tests/data/raw_yuv420_image.yuv420 (renamed from third_party/data/raw_yuv420_image.yuv420)0
-rw-r--r--tests/gainmapmath_test.cpp627
-rw-r--r--tests/icchelper_test.cpp73
-rw-r--r--tests/jpegdecoderhelper_test.cpp159
-rw-r--r--tests/jpegencoderhelper_test.cpp142
-rw-r--r--tests/jpegr_test.cpp1109
-rw-r--r--tests/ultrahdr_app.cpp921
-rw-r--r--third_party/image_io/CMakeLists.txt (renamed from third_party/cmake/image_io/CMakeLists.txt)20
-rw-r--r--third_party/image_io/includes/image_io/base/data_range.h4
-rw-r--r--third_party/image_io/src/utils/file_utils.cc6
-rw-r--r--utils.cmake84
58 files changed, 6402 insertions, 6367 deletions
diff --git a/.clang-format b/.clang-format
new file mode 100644
index 0000000..0153825
--- /dev/null
+++ b/.clang-format
@@ -0,0 +1,7 @@
+# Run manually to reformat a file:
+# clang-format -i --style=file <file>
+
+Language: Cpp
+BasedOnStyle: Google
+ColumnLimit: 100
+SortIncludes: false
diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml
index e45ccda..a69b73a 100644
--- a/.github/workflows/cmake.yml
+++ b/.github/workflows/cmake.yml
@@ -17,21 +17,21 @@ jobs:
cc: gcc
cxx: g++
build-system: cmake
- cmake-opts: '-DENABLE_FUZZERS=OFF'
+ cmake-opts: '-DUHDR_BUILD_TESTS=1 -DUHDR_BUILD_FUZZERS=0'
- name: ubuntu-latest-clang-cmake
os: ubuntu-latest
cc: clang
cxx: clang++
build-system: cmake
- cmake-opts: '-DENABLE_FUZZERS=ON'
+ cmake-opts: '-DUHDR_BUILD_TESTS=1 -DUHDR_BUILD_FUZZERS=1'
- name: macos-latest-clang-cmake
os: macos-latest
cc: clang
cxx: clang++
build-system: cmake
- cmake-opts: '-DENABLE_FUZZERS=OFF'
+ cmake-opts: '-DUHDR_BUILD_TESTS=1 -DUHDR_BUILD_FUZZERS=0'
runs-on: ${{ matrix.os }}
diff --git a/Android.bp b/Android.bp
index b229e0f..3717e79 100644
--- a/Android.bp
+++ b/Android.bp
@@ -35,15 +35,15 @@ cc_library {
name: "libultrahdr",
host_supported: true,
vendor_available: true,
- export_include_dirs: ["include"],
- local_include_dirs: ["include"],
+ export_include_dirs: ["lib"],
+ local_include_dirs: ["lib"],
srcs: [
- "icc.cpp",
- "jpegr.cpp",
- "gainmapmath.cpp",
- "jpegrutils.cpp",
- "multipictureformat.cpp",
+ "lib/icc.cpp",
+ "lib/jpegr.cpp",
+ "lib/gainmapmath.cpp",
+ "lib/jpegrutils.cpp",
+ "lib/multipictureformat.cpp",
],
shared_libs: [
@@ -65,10 +65,10 @@ cc_library {
"liblog",
],
- export_include_dirs: ["include"],
+ export_include_dirs: ["lib"],
srcs: [
- "jpegencoderhelper.cpp",
+ "lib/jpegencoderhelper.cpp",
],
}
@@ -82,9 +82,9 @@ cc_library {
"liblog",
],
- export_include_dirs: ["include"],
+ export_include_dirs: ["lib"],
srcs: [
- "jpegdecoderhelper.cpp",
+ "lib/jpegdecoderhelper.cpp",
],
}
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 5b316d5..86f378a 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -14,197 +14,287 @@
# the License.
#
-cmake_minimum_required(VERSION 3.5)
+cmake_minimum_required(VERSION 3.13)
-project(ULTRAHDR)
+project(UltraHdr C CXX)
-if(NOT CMAKE_BUILD_TYPE)
- message(STATUS "No build type selected, defaulting to release")
- set(CMAKE_BUILD_TYPE "Release" CACHE STRING "" FORCE)
+###########################################################
+# Detect system
+###########################################################
+if(${CMAKE_SYSTEM_NAME} MATCHES "Linux")
+elseif(WIN32)
+elseif(APPLE)
+else()
+ message(FATAL_ERROR "Platform not supported")
endif()
-set(ULTRAHDR_VERSION_MAJOR 1)
-set(ULTRAHDR_VERSION_MINOR 0)
-set(ULTRAHDR_VERSION_PATCH 0)
-set(ULTRAHDR_VERSION ${ULTRAHDR_VERSION_MAJOR}.${ULTRAHDR_VERSION_MINOR}.${ULTRAHDR_VERSION_PATCH})
-
-option(ENABLE_FUZZERS "Enable building fuzzer apps" OFF)
-# Add -fuzzer-no-link to sanitize argument if fuzzer build is enabled
-if(${ENABLE_FUZZERS})
- message(STATUS "Building fuzzer applications enabled")
- if(DEFINED SANITIZE)
- set(SANITIZE "${SANITIZE},fuzzer-no-link")
+if(CMAKE_SYSTEM_PROCESSOR MATCHES "arm" OR CMAKE_SYSTEM_PROCESSOR MATCHES "aarch")
+ if(CMAKE_SIZEOF_VOID_P EQUAL 8)
+ set(ARCH "aarch64")
else()
- set(SANITIZE "fuzzer-no-link")
+ set(ARCH "arm")
endif()
+elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^mips.*")
+ message(FATAL_ERROR "Architecture not supported")
+elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^ppc.*")
+ message(FATAL_ERROR "Architecture not supported")
else()
- message(STATUS "Building fuzzer applications disabled")
+ if(CMAKE_SIZEOF_VOID_P EQUAL 8)
+ set(ARCH "x86_64")
+ else()
+ set(ARCH "x86")
+ endif()
endif()
-option(ENABLE_TESTS "Enable unit tests" OFF)
-if(${ENABLE_TESTS})
- message(STATUS "Building unit tests enabled")
- include(CTest)
-else()
- message(STATUS "Building unit tests disabled")
+###########################################################
+# Directories
+###########################################################
+set(SOURCE_DIR ${CMAKE_SOURCE_DIR}/lib)
+set(THIRD_PARTY_DIR ${CMAKE_SOURCE_DIR}/third_party)
+set(TESTS_DIR ${CMAKE_SOURCE_DIR}/tests)
+set(FUZZERS_DIR ${CMAKE_SOURCE_DIR}/fuzzer)
+set(EXAMPLES_DIR ${CMAKE_SOURCE_DIR}/examples)
+
+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(UHDR_BUILD_EXAMPLES "Build examples " TRUE)
+option_if_not_defined(UHDR_BUILD_TESTS "Build unit tests " FALSE)
+option_if_not_defined(UHDR_BUILD_FUZZERS "Build fuzzers " FALSE)
+option_if_not_defined(UHDR_ENABLE_LOGS "Build with verbose logging " FALSE)
+
+###########################################################
+# Compile flags
+###########################################################
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
-set(CMAKE_THREAD_PREFER_PTHREAD ON)
-set(THREADS_PREFER_PTHREAD_FLAG ON)
-find_package(Threads REQUIRED)
+if(MSVC)
+ if(DEFINED UHDR_SANITIZE_OPTIONS)
+ message(FATAL_ERROR "Building with Sanitizer options not supported in MSVC path")
+ endif()
+ if(UHDR_BUILD_FUZZERS)
+ message(FATAL_ERROR "Building fuzzers not supported in MSVC path")
+ endif()
+ add_compile_options($<$<CONFIG:>:/MT>
+ $<$<CONFIG:Debug>:/MTd>
+ $<$<CONFIG:MinSizeRel>:/MT>
+ $<$<CONFIG:Release>:/MT>
+ $<$<CONFIG:RelWithDebInfo>:/MT>)
+ 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
+else()
+ add_compile_options(-ffunction-sections)
+ add_compile_options(-fdata-sections)
+ add_compile_options(-fomit-frame-pointer)
+ if(ARCH STREQUAL "x86")
+ add_compile_options(-m32)
+ add_compile_options(-march=pentium4)
+ add_compile_options(-mtune=generic)
+ endif()
+ if(ARCH STREQUAL "x86_64")
+ add_compile_options(-m64)
+ add_compile_options(-march=x86-64)
+ add_compile_options(-mtune=generic)
+ endif()
-set(SRC_DIR "${CMAKE_CURRENT_SOURCE_DIR}")
+ 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()
+endif()
-include("${SRC_DIR}/utils.cmake")
+if(UHDR_ENABLE_LOGS)
+ add_compile_options(-DLOG_NDEBUG)
+endif()
-libultrahdr_add_compile_options()
+###########################################################
+# Dependencies
+###########################################################
-ADD_SUBDIRECTORY("${SRC_DIR}/third_party/cmake/image_io")
+# Threads
+set(CMAKE_THREAD_PREFER_PTHREAD ON)
+set(THREADS_PREFER_PTHREAD_FLAG ON)
+find_package(Threads REQUIRED)
-get_directory_property(ULTRA_HDR_FLAGS COMPILE_OPTIONS)
-string (REPLACE ";" " " ULTRA_HDR_FLAGS_STR "${ULTRA_HDR_FLAGS}")
-set(ULTRA_HDR_CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${ULTRA_HDR_FLAGS_STR}")
-set(ULTRA_HDR_CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${ULTRA_HDR_FLAGS_STR}")
+# ImageIO
+add_subdirectory("${THIRD_PARTY_DIR}/image_io")
include(ExternalProject)
-function(fetch_libjpegturbo)
- ExternalProject_Add(libjpeg-turbo
- GIT_REPOSITORY https://github.com/libjpeg-turbo/libjpeg-turbo.git
- GIT_TAG 3.0.1
- PREFIX ${CMAKE_CURRENT_BINARY_DIR}/libjpeg-turbo
- SOURCE_DIR ${SRC_DIR}/third_party/libjpeg-turbo
- BUILD_COMMAND ${CMAKE_COMMAND} --build <BINARY_DIR> --target jpeg-static
- CMAKE_ARGS -DCMAKE_C_COMPILER=${CMAKE_C_COMPILER}
- -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}
- -DCMAKE_C_FLAGS=${ULTRA_HDR_CMAKE_C_FLAGS}
- INSTALL_COMMAND ""
- )
- set(JPEG_INCLUDE_DIRS
- ${SRC_DIR}/third_party/libjpeg-turbo/
- ${CMAKE_CURRENT_BINARY_DIR}/libjpeg-turbo/src/libjpeg-turbo-build PARENT_SCOPE)
+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}")
+
+# libjpeg-turbo
+ExternalProject_Add(libjpeg-turbo
+ GIT_REPOSITORY https://github.com/libjpeg-turbo/libjpeg-turbo.git
+ GIT_TAG 3.0.1
+ PREFIX ${CMAKE_CURRENT_BINARY_DIR}/libjpeg-turbo
+ SOURCE_DIR ${THIRD_PARTY_DIR}/libjpeg-turbo
+ BUILD_COMMAND ${CMAKE_COMMAND} --build <BINARY_DIR> --config $<CONFIG> --target jpeg-static
+ CMAKE_ARGS -DCMAKE_C_COMPILER=${CMAKE_C_COMPILER}
+ -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}
+ -DCMAKE_C_FLAGS=${UHDR_CMAKE_C_FLAGS}
+ INSTALL_COMMAND ""
+)
+set(JPEG_INCLUDE_DIRS
+ ${THIRD_PARTY_DIR}/libjpeg-turbo/
+ ${CMAKE_CURRENT_BINARY_DIR}/libjpeg-turbo/src/libjpeg-turbo-build)
+
+if(IS_MULTI)
set(JPEG_LIBRARIES
- ${CMAKE_CURRENT_BINARY_DIR}/libjpeg-turbo/src/libjpeg-turbo-build/libjpeg.a PARENT_SCOPE)
-endfunction()
+ ${CMAKE_CURRENT_BINARY_DIR}/libjpeg-turbo/src/libjpeg-turbo-build/$<CONFIG>/jpeg-static.lib)
+else()
+ set(JPEG_LIBRARIES
+ ${CMAKE_CURRENT_BINARY_DIR}/libjpeg-turbo/src/libjpeg-turbo-build/libjpeg.a)
+endif()
-function(fetch_googletest)
+if(UHDR_BUILD_TESTS)
+ # gtest and gmock
ExternalProject_Add(googletest
GIT_REPOSITORY https://github.com/google/googletest
- GIT_TAG v1.13.0
+ GIT_TAG v1.14.0
PREFIX ${CMAKE_CURRENT_BINARY_DIR}/googletest
- SOURCE_DIR ${SRC_DIR}/third_party/googletest
+ SOURCE_DIR ${THIRD_PARTY_DIR}/googletest
CMAKE_ARGS -DCMAKE_C_COMPILER=${CMAKE_C_COMPILER}
-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}
-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}
- -DCMAKE_C_FLAGS=${ULTRA_HDR_CMAKE_C_FLAGS}
- -DCMAKE_CXX_FLAGS=${ULTRA_HDR_CMAKE_CXX_FLAGS}
+ -DCMAKE_C_FLAGS=${UHDR_CMAKE_C_FLAGS}
+ -DCMAKE_CXX_FLAGS=${UHDR_CMAKE_CXX_FLAGS}
INSTALL_COMMAND ""
)
set(GTEST_INCLUDE_DIRS
- ${CMAKE_CURRENT_BINARY_DIR}/googletest/googletest/include
- ${CMAKE_CURRENT_BINARY_DIR}/googletest/googlemock/include PARENT_SCOPE)
- set(GTEST_BOTH_LIBRARIES
- ${CMAKE_CURRENT_BINARY_DIR}/googletest/src/googletest-build/lib/libgtest.a
- ${CMAKE_CURRENT_BINARY_DIR}/googletest/src/googletest-build/lib/libgtest_main.a PARENT_SCOPE)
-endfunction()
+ ${THIRD_PARTY_DIR}/googletest/googletest/include
+ ${THIRD_PARTY_DIR}/googletest/googlemock/include)
+ if(IS_MULTI)
+ set(GTEST_BOTH_LIBRARIES
+ ${CMAKE_CURRENT_BINARY_DIR}/googletest/src/googletest-build/lib/$<CONFIG>/gtest.lib
+ ${CMAKE_CURRENT_BINARY_DIR}/googletest/src/googletest-build/lib/$<CONFIG>/gtest_main.lib)
+ else()
+ set(GTEST_BOTH_LIBRARIES
+ ${CMAKE_CURRENT_BINARY_DIR}/googletest/src/googletest-build/lib/libgtest.a
+ ${CMAKE_CURRENT_BINARY_DIR}/googletest/src/googletest-build/lib/libgtest_main.a)
+ endif()
+endif()
set_property(DIRECTORY PROPERTY ADDITIONAL_MAKE_CLEAN_FILES
${CMAKE_CURRENT_BINARY_DIR}/libjpeg-turbo/src/libjpeg-turbo-build
${CMAKE_CURRENT_BINARY_DIR}/googletest/src/googletest-build)
-fetch_libjpegturbo()
+###########################################################
+# File Lists
+###########################################################
+file(GLOB UHDR_LIB_LIST "${SOURCE_DIR}/*.cpp")
+file(GLOB UHDR_TEST_LIST "${TESTS_DIR}/*.cpp")
+set(COMMON_INCLUDE_LIST ${SOURCE_DIR} ${JPEG_INCLUDE_DIRS})
+set(COMMON_LIBS_LIST ${JPEG_LIBRARIES} Threads::Threads)
-if(${ENABLE_TESTS})
- find_package(GTest)
- if(NOT GTest_FOUND)
- fetch_googletest()
- endif()
-endif()
-
-add_library(ultrahdr STATIC
- "${SRC_DIR}/gainmapmath.cpp"
- "${SRC_DIR}/icc.cpp"
- "${SRC_DIR}/jpegr.cpp"
- "${SRC_DIR}/jpegrutils.cpp"
- "${SRC_DIR}/jpegencoderhelper.cpp"
- "${SRC_DIR}/jpegdecoderhelper.cpp"
- "${SRC_DIR}/multipictureformat.cpp"
-)
+###########################################################
+# Targets
+###########################################################
+add_library(ultrahdr STATIC ${UHDR_LIB_LIST})
add_dependencies(ultrahdr libjpeg-turbo)
-
+#target_compile_options(ultrahdr PRIVATE -Wall -Wextra -Wpedantic)
target_include_directories(ultrahdr PRIVATE
- ${JPEG_INCLUDE_DIRS}
- "${SRC_DIR}/include"
- "${SRC_DIR}/third_party/image_io/includes/"
-)
-
-target_link_libraries(ultrahdr PRIVATE
- ${JPEG_LIBRARIES}
- image_io
- Threads::Threads
+ ${COMMON_INCLUDE_LIST}
+ "${THIRD_PARTY_DIR}/image_io/includes/"
)
+target_link_libraries(ultrahdr PRIVATE ${COMMON_LIBS_LIST} image_io)
-libultrahdr_add_executable(ultrahdr_app
- ultrahdr
- SOURCES
- "${SRC_DIR}/tests/ultrahdr_app.cpp"
- INCLUDES
- ${JPEG_INCLUDE_DIRS}
- "${SRC_DIR}/include"
-)
-add_dependencies(ultrahdr_app libjpeg-turbo)
-
-if (${ENABLE_FUZZERS})
- libultrahdr_add_fuzzer(ultrahdr_enc_fuzzer ultrahdr
- SOURCES
- ${SRC_DIR}/fuzzer/ultrahdr_enc_fuzzer.cpp
- INCLUDES
- ${JPEG_INCLUDE_DIRS}
- "${SRC_DIR}/include"
- )
- add_dependencies(ultrahdr_enc_fuzzer libjpeg-turbo)
-
- libultrahdr_add_fuzzer(ultrahdr_dec_fuzzer ultrahdr
- SOURCES
- ${SRC_DIR}/fuzzer/ultrahdr_dec_fuzzer.cpp
- INCLUDES
- ${JPEG_INCLUDE_DIRS}
- "${SRC_DIR}/include"
- )
- add_dependencies(ultrahdr_dec_fuzzer libjpeg-turbo)
+if(UHDR_BUILD_EXAMPLES)
+ add_executable(ultrahdr_app "${EXAMPLES_DIR}/ultrahdr_app.cpp")
+ add_dependencies(ultrahdr_app ultrahdr)
+ target_include_directories(ultrahdr_app PRIVATE ${COMMON_INCLUDE_LIST})
+ if(UHDR_BUILD_FUZZERS)
+ target_link_options(ultrahdr_app PRIVATE -fsanitize=fuzzer-no-link)
+ endif()
+ target_link_libraries(ultrahdr_app PRIVATE ultrahdr)
endif()
-if(${ENABLE_TESTS})
- libultrahdr_add_executable(ultrahdr_unit_test
- ultrahdr
- SOURCES
- "${SRC_DIR}/tests/jpegr_test.cpp"
- "${SRC_DIR}/tests/gainmapmath_test.cpp"
- "${SRC_DIR}/tests/icchelper_test.cpp"
- "${SRC_DIR}/tests/jpegencoderhelper_test.cpp"
- "${SRC_DIR}/tests/jpegdecoderhelper_test.cpp"
- "${SRC_DIR}/tests/icchelper_test.cpp"
- INCLUDES
- ${JPEG_INCLUDE_DIRS}
- ${GTEST_INCLUDE_DIRS}
- "${SRC_DIR}/include"
+if(UHDR_BUILD_TESTS)
+ include(CTest)
+ add_executable(ultrahdr_unit_test ${UHDR_TEST_LIST})
+ add_dependencies(ultrahdr_unit_test googletest ultrahdr)
+ target_include_directories(ultrahdr_unit_test PRIVATE
+ ${COMMON_INCLUDE_LIST}
+ ${GTEST_INCLUDE_DIRS}
)
- if(GTest_FOUND)
- add_dependencies(ultrahdr_unit_test libjpeg-turbo)
+ if(UHDR_BUILD_FUZZERS)
+ target_link_options(ultrahdr_unit_test PRIVATE -fsanitize=fuzzer-no-link)
+ endif()
+ target_link_libraries(ultrahdr_unit_test ultrahdr ${GTEST_BOTH_LIBRARIES})
+ if(WIN32)
+ file(COPY "${TESTS_DIR}/data/" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/data")
else()
- add_dependencies(ultrahdr_unit_test googletest libjpeg-turbo)
+ execute_process(COMMAND cmake -E create_symlink
+ "${TESTS_DIR}/data/"
+ "${CMAKE_CURRENT_BINARY_DIR}/data"
+ )
endif()
+ add_test(NAME UHDRUnitTests, COMMAND ultrahdr_unit_test)
+endif()
- target_link_libraries(ultrahdr_unit_test ${GTEST_BOTH_LIBRARIES})
-
- execute_process(COMMAND cmake -E create_symlink
- "${SRC_DIR}/third_party/data/"
- "${CMAKE_CURRENT_BINARY_DIR}/data"
- )
+if(UHDR_BUILD_FUZZERS)
+ add_executable(ultrahdr_enc_fuzzer ${FUZZERS_DIR}/ultrahdr_enc_fuzzer.cpp)
+ add_dependencies(ultrahdr_enc_fuzzer ultrahdr)
+ target_include_directories(ultrahdr_enc_fuzzer PRIVATE ${COMMON_INCLUDE_LIST})
+ 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 ultrahdr)
- add_test(NAME UltraHdrUnitTests, COMMAND ultrahdr_unit_test)
+ add_executable(ultrahdr_dec_fuzzer ${FUZZERS_DIR}/ultrahdr_dec_fuzzer.cpp)
+ add_dependencies(ultrahdr_dec_fuzzer ultrahdr)
+ target_include_directories(ultrahdr_dec_fuzzer PRIVATE ${COMMON_INCLUDE_LIST})
+ 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 ultrahdr)
endif()
diff --git a/README.md b/README.md
index 3fda451..b7bc27b 100644
--- a/README.md
+++ b/README.md
@@ -23,33 +23,34 @@ For this libjpeg-turbo is used. This is cloned from
<https://github.com/libjpeg-turbo/libjpeg-turbo.git> and included in the
build process.
-### Requirements
+Requirements
+--------------
-- [CMake](http://www.cmake.org) v3.5 or later
+- [CMake](http://www.cmake.org) v3.13 or later
- [NASM](http://www.nasm.us) or [Yasm](http://yasm.tortall.net)
- (If libjpeg-turbo is building on x86 or x86-64 with SIMD extensions)
+ (If libjpeg-turbo needs to be built with SIMD extensions)
* If using NASM, 2.13 or later is required.
* If using Yasm, 1.2.0 or later is required.
* If building on macOS, NASM or Yasm can be obtained from
[MacPorts](http://www.macports.org/) or [Homebrew](http://brew.sh/).
-Tested with GCC v11.4 and Clang 14.0.0 on Linux and Mac Platforms.
+- Compilers with support for C++17
-### Building Commands
+Should work with GCC v7 (or later) and Clang 5 (or later) on Linux and Mac Platforms.
-To build libultrahdr and sample application:
+Should work with Microsoft Visual C++ 2019 (or later) on Windows Platforms.
- mkdir {build_directory}
- cd {build_directory}
- cmake ../ -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++
- make
+Build Procedure
+---------------
+
+To build libultrahdr, examples, unit tests:
-To build unit tests:
+### Un*x (including Linux, Mac)
mkdir {build_directory}
cd {build_directory}
- cmake ../ -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DENABLE_TESTS=1
+ cmake -G "Unix Makefiles" -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DUHDR_BUILD_TESTS=1 ../
make
ctest
@@ -61,11 +62,59 @@ This will generate the following files under *{build_directory}*:
**ultrahdr_unit_test**<br> Unit tests
+### Visual C++ (IDE)
+
+ mkdir {build_directory}
+ cd {build_directory}
+ cmake -G "Visual Studio 16 2019" -DUHDR_BUILD_TESTS=1 ../
+ cmake --build ./ --config=Release
+ ctest -C Release
+
+This will generate the following files under *{build_directory/Release}*:
+
+**ultrahdr.lib**<br> Static link library for the ultrahdr API
+
+**ultrahdr_app.exe**<br> Sample application demonstrating ultrahdr API
+
+**ultrahdr_unit_test.exe**<br> Unit tests
+
+### MinGW
+
+NOTE: This assumes that you are building on a Windows machine using the MSYS
+environment.
+
+ mkdir {build_directory}
+ cd {build_directory}
+ cmake -G "MSYS Makefiles" -DUHDR_BUILD_TESTS=1 ../
+ cmake --build ./
+ ctest
+
+ mkdir {build_directory}
+ cd {build_directory}
+ cmake -G "MinGW Makefiles" -DUHDR_BUILD_TESTS=1 ../
+ cmake --build ./
+ ctest
+
+This will generate the following files under *{build_directory}*:
+
+**libultrahdr.a**<br> Static link library for the ultrahdr API
+
+**ultrahdr_app.exe**<br> Sample application demonstrating ultrahdr API
+
+**ultrahdr_unit_test.exe**<br> Unit tests
+
+
+NOTE: To not build unit tests, skip passing -DUHDR_BUILD_TESTS=1
+
+### Building Fuzzers
+
+Refer to [README.md](fuzzer/README.md) for complete instructions.
+
Using libultrahdr
===================
libultrahdr includes two classes of APIs, one to compress and the other to
decompress HDR images:
-- Refer to [jpegr.h](include/ultrahdr/jpegr.h) for detailed description of various encode and decode api.
-- Refer to [ultrahdr_app.cpp](tests/ultrahdr_app.cpp) for examples of its usage.
+- Refer to [jpegr.h](lib/jpegr.h) for detailed description of various encode and decode api.
+- Refer to [ultrahdr_app.cpp](examples/ultrahdr_app.cpp) for examples of its usage.
diff --git a/examples/Android.bp b/examples/Android.bp
new file mode 100644
index 0000000..0ede18b
--- /dev/null
+++ b/examples/Android.bp
@@ -0,0 +1,41 @@
+// Copyright 2023 The Android Open Source Project
+//
+// 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.
+
+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_binary {
+ name: "ultrahdr_sample_app",
+ host_supported: true,
+ srcs: [
+ "ultrahdr_app.cpp",
+ ],
+ shared_libs: [
+ "libbase",
+ "libimage_io",
+ "libjpeg",
+ "liblog",
+ ],
+ static_libs: [
+ "libjpegdecoder",
+ "libjpegencoder",
+ "libultrahdr",
+ ],
+}
diff --git a/examples/ultrahdr_app.cpp b/examples/ultrahdr_app.cpp
new file mode 100644
index 0000000..f3fd372
--- /dev/null
+++ b/examples/ultrahdr_app.cpp
@@ -0,0 +1,991 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * 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.
+ */
+
+#ifdef _WIN32
+#include <Windows.h>
+#else
+#include <sys/time.h>
+#endif
+
+#include <string.h>
+
+#include <algorithm>
+#include <cmath>
+#include <fstream>
+#include <iostream>
+
+#include "ultrahdrcommon.h"
+#include "gainmapmath.h"
+#include "jpegr.h"
+
+using namespace ultrahdr;
+
+const float BT601YUVtoRGBMatrix[9] = {
+ 1, 0, 1.402, 1, (-0.202008 / 0.587), (-0.419198 / 0.587), 1.0, 1.772, 0.0};
+const float BT709YUVtoRGBMatrix[9] = {
+ 1, 0, 1.5748, 1, (-0.13397432 / 0.7152), (-0.33480248 / 0.7152), 1.0, 1.8556, 0.0};
+const float BT2020YUVtoRGBMatrix[9] = {
+ 1, 0, 1.4746, 1, (-0.11156702 / 0.6780), (-0.38737742 / 0.6780), 1, 1.8814, 0};
+
+const float BT601RGBtoYUVMatrix[9] = {
+ 0.299, 0.587, 0.114, (-0.299 / 1.772), (-0.587 / 1.772), 0.5, 0.5, (-0.587 / 1.402),
+ (-0.114 / 1.402)};
+const float BT709RGBtoYUVMatrix[9] = {0.2126,
+ 0.7152,
+ 0.0722,
+ (-0.2126 / 1.8556),
+ (-0.7152 / 1.8556),
+ 0.5,
+ 0.5,
+ (-0.7152 / 1.5748),
+ (-0.0722 / 1.5748)};
+const float BT2020RGBtoYUVMatrix[9] = {0.2627,
+ 0.6780,
+ 0.0593,
+ (-0.2627 / 1.8814),
+ (-0.6780 / 1.8814),
+ 0.5,
+ 0.5,
+ (-0.6780 / 1.4746),
+ (-0.0593 / 1.4746)};
+
+int optind_s = 1;
+int optopt_s = 0;
+char* optarg_s = nullptr;
+
+int getopt_s(int argc, char* const argv[], char* ostr) {
+ if (optind_s >= argc) return -1;
+
+ const char* arg = argv[optind_s];
+ if (arg[0] != '-' || !arg[1]) {
+ std::cerr << "invalid option " << arg << std::endl;
+ return '?';
+ }
+ optopt_s = arg[1];
+ char* oindex = strchr(ostr, optopt_s);
+ if (!oindex) {
+ std::cerr << "unsupported option " << arg << std::endl;
+ return '?';
+ }
+ if (oindex[1] != ':') {
+ optarg_s = nullptr;
+ return optopt_s;
+ }
+
+ if (argc > ++optind_s) {
+ optarg_s = (char*)argv[optind_s++];
+ } else {
+ std::cerr << "option " << arg << " requires an argument" << std::endl;
+ optarg_s = nullptr;
+ return '?';
+ }
+ return optopt_s;
+}
+
+// #define PROFILE_ENABLE 1
+#ifdef _WIN32
+class Profiler {
+ public:
+ void timerStart() { QueryPerformanceCounter(&mStartingTime); }
+
+ void timerStop() { QueryPerformanceCounter(&mEndingTime); }
+
+ int64_t elapsedTime() {
+ LARGE_INTEGER frequency;
+ LARGE_INTEGER elapsedMicroseconds;
+ QueryPerformanceFrequency(&frequency);
+ elapsedMicroseconds.QuadPart = mEndingTime.QuadPart - mStartingTime.QuadPart;
+ return (double)elapsedMicroseconds.QuadPart / (double)frequency.QuadPart * 1000000;
+ }
+
+ private:
+ LARGE_INTEGER mStartingTime;
+ LARGE_INTEGER mEndingTime;
+};
+#else
+class Profiler {
+ public:
+ void timerStart() { gettimeofday(&mStartingTime, nullptr); }
+
+ void timerStop() { gettimeofday(&mEndingTime, nullptr); }
+
+ int64_t elapsedTime() {
+ struct timeval elapsedMicroseconds;
+ elapsedMicroseconds.tv_sec = mEndingTime.tv_sec - mStartingTime.tv_sec;
+ elapsedMicroseconds.tv_usec = mEndingTime.tv_usec - mStartingTime.tv_usec;
+ return elapsedMicroseconds.tv_sec * 1000000 + elapsedMicroseconds.tv_usec;
+ }
+
+ private:
+ struct timeval mStartingTime;
+ struct timeval mEndingTime;
+};
+#endif
+
+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) {
+ std::cerr << "requested to read " << length << " bytes from file : " << filename
+ << ", file contains only " << size << " bytes" << std::endl;
+ return false;
+ }
+ ifd.seekg(0, std::ios::beg);
+ result = malloc(length);
+ if (result == nullptr) {
+ std::cerr << "failed to allocate memory to store contents of file : " << filename
+ << std::endl;
+ return false;
+ }
+ ifd.read(static_cast<char*>(result), length);
+ return true;
+ }
+ std::cerr << "unable to open file : " << filename << std::endl;
+ return false;
+}
+
+static bool writeFile(const char* filename, void*& result, int length) {
+ std::ofstream ofd(filename, std::ios::binary);
+ if (ofd.is_open()) {
+ ofd.write(static_cast<char*>(result), length);
+ return true;
+ }
+ std::cerr << "unable to write to file : " << filename << std::endl;
+ return false;
+}
+
+class UltraHdrAppInput {
+ public:
+ UltraHdrAppInput(const char* p010File, const char* yuv420File, const char* yuv420JpegFile,
+ size_t width, size_t height,
+ ultrahdr_color_gamut p010Cg = ULTRAHDR_COLORGAMUT_BT709,
+ ultrahdr_color_gamut yuv420Cg = ULTRAHDR_COLORGAMUT_BT709,
+ ultrahdr_transfer_function tf = ULTRAHDR_TF_HLG, int quality = 100,
+ ultrahdr_output_format of = ULTRAHDR_OUTPUT_HDR_HLG)
+ : mP010File(p010File),
+ mYuv420File(yuv420File),
+ mYuv420JpegFile(yuv420JpegFile),
+ mJpegRFile(nullptr),
+ mWidth(width),
+ mHeight(height),
+ mP010Cg(p010Cg),
+ mYuv420Cg(yuv420Cg),
+ mTf(tf),
+ mQuality(quality),
+ mOf(of),
+ mMode(0){};
+
+ UltraHdrAppInput(const char* jpegRFile, ultrahdr_output_format of = ULTRAHDR_OUTPUT_HDR_HLG)
+ : mP010File(nullptr),
+ mYuv420File(nullptr),
+ mJpegRFile(jpegRFile),
+ mWidth(0),
+ mHeight(0),
+ mP010Cg(ULTRAHDR_COLORGAMUT_UNSPECIFIED),
+ mYuv420Cg(ULTRAHDR_COLORGAMUT_UNSPECIFIED),
+ mTf(ULTRAHDR_TF_UNSPECIFIED),
+ mQuality(100),
+ mOf(of),
+ mMode(1){};
+
+ ~UltraHdrAppInput() {
+ if (mRawP010Image.data) free(mRawP010Image.data);
+ if (mRawP010Image.chroma_data) free(mRawP010Image.chroma_data);
+ if (mRawRgba1010102Image.data) free(mRawRgba1010102Image.data);
+ if (mRawRgba1010102Image.chroma_data) free(mRawRgba1010102Image.chroma_data);
+ if (mRawYuv420Image.data) free(mRawYuv420Image.data);
+ if (mRawYuv420Image.chroma_data) free(mRawYuv420Image.chroma_data);
+ if (mRawRgba8888Image.data) free(mRawRgba8888Image.data);
+ if (mRawRgba8888Image.chroma_data) free(mRawRgba8888Image.chroma_data);
+ if (mJpegImgR.data) free(mJpegImgR.data);
+ if (mDestImage.data) free(mDestImage.data);
+ if (mDestImage.chroma_data) free(mDestImage.chroma_data);
+ if (mDestYUV444Image.data) free(mDestYUV444Image.data);
+ if (mDestYUV444Image.chroma_data) free(mDestYUV444Image.chroma_data);
+ }
+
+ bool fillJpegRImageHandle();
+ bool fillP010ImageHandle();
+ bool convertP010ToRGBImage();
+ bool fillYuv420ImageHandle();
+ bool fillYuv420JpegImageHandle();
+ bool convertYuv420ToRGBImage();
+ bool convertRgba8888ToYUV444Image();
+ bool convertRgba1010102ToYUV444Image();
+ bool encode();
+ bool decode();
+ void computeRGBHdrPSNR();
+ void computeRGBSdrPSNR();
+ void computeYUVHdrPSNR();
+ void computeYUVSdrPSNR();
+
+ const char* mP010File;
+ const char* mYuv420File;
+ const char* mYuv420JpegFile;
+ const char* mJpegRFile;
+ const int mWidth;
+ const int mHeight;
+ const ultrahdr_color_gamut mP010Cg;
+ const ultrahdr_color_gamut mYuv420Cg;
+ const ultrahdr_transfer_function mTf;
+ const int mQuality;
+ const ultrahdr_output_format mOf;
+ const int mMode;
+ jpegr_uncompressed_struct mRawP010Image{};
+ jpegr_uncompressed_struct mRawRgba1010102Image{};
+ jpegr_uncompressed_struct mRawYuv420Image{};
+ jpegr_compressed_struct mYuv420JpegImage{};
+ jpegr_uncompressed_struct mRawRgba8888Image{};
+ jpegr_compressed_struct mJpegImgR{};
+ jpegr_uncompressed_struct mDestImage{};
+ jpegr_uncompressed_struct mDestYUV444Image{};
+ double mPsnr[3]{};
+};
+
+bool UltraHdrAppInput::fillP010ImageHandle() {
+ const int bpp = 2;
+ int p010Size = mWidth * mHeight * bpp * 1.5;
+ mRawP010Image.width = mWidth;
+ mRawP010Image.height = mHeight;
+ mRawP010Image.colorGamut = mP010Cg;
+ return loadFile(mP010File, mRawP010Image.data, p010Size);
+}
+
+bool UltraHdrAppInput::fillYuv420ImageHandle() {
+ int yuv420Size = mWidth * mHeight * 1.5;
+ mRawYuv420Image.width = mWidth;
+ mRawYuv420Image.height = mHeight;
+ mRawYuv420Image.colorGamut = mYuv420Cg;
+ return loadFile(mYuv420File, mRawYuv420Image.data, yuv420Size);
+}
+
+bool UltraHdrAppInput::fillYuv420JpegImageHandle() {
+ std::ifstream ifd(mYuv420JpegFile, std::ios::binary | std::ios::ate);
+ if (ifd.good()) {
+ int size = ifd.tellg();
+ mYuv420JpegImage.length = size;
+ mYuv420JpegImage.maxLength = size;
+ mYuv420JpegImage.data = nullptr;
+ mYuv420JpegImage.colorGamut = mYuv420Cg;
+ ifd.close();
+ return loadFile(mYuv420JpegFile, mYuv420JpegImage.data, size);
+ }
+ return false;
+}
+
+bool UltraHdrAppInput::fillJpegRImageHandle() {
+ std::ifstream ifd(mJpegRFile, std::ios::binary | std::ios::ate);
+ if (ifd.good()) {
+ int size = ifd.tellg();
+ mJpegImgR.length = size;
+ mJpegImgR.maxLength = size;
+ mJpegImgR.data = nullptr;
+ mJpegImgR.colorGamut = mYuv420Cg;
+ ifd.close();
+ return loadFile(mJpegRFile, mJpegImgR.data, size);
+ }
+ return false;
+}
+
+bool UltraHdrAppInput::encode() {
+ if (!fillP010ImageHandle()) return false;
+ if (mYuv420File != nullptr && !fillYuv420ImageHandle()) return false;
+ if (mYuv420JpegFile != nullptr && !fillYuv420JpegImageHandle()) return false;
+
+ mJpegImgR.maxLength = (std::max)(static_cast<size_t>(8 * 1024) /* min size 8kb */,
+ mRawP010Image.width * mRawP010Image.height * 3 * 2);
+ mJpegImgR.data = malloc(mJpegImgR.maxLength);
+ if (mJpegImgR.data == nullptr) {
+ std::cerr << "unable to allocate memory to store compressed image" << std::endl;
+ return false;
+ }
+
+ JpegR jpegHdr;
+ status_t status = JPEGR_UNKNOWN_ERROR;
+#ifdef PROFILE_ENABLE
+ const int profileCount = 10;
+ Profiler profileEncode;
+ profileEncode.timerStart();
+ for (auto i = 0; i < profileCount; i++) {
+#endif
+ if (mYuv420File == nullptr && mYuv420JpegFile == nullptr) { // api-0
+ status = jpegHdr.encodeJPEGR(&mRawP010Image, mTf, &mJpegImgR, mQuality, nullptr);
+ if (JPEGR_NO_ERROR != status) {
+ std::cerr << "Encountered error during encodeJPEGR call, error code " << status
+ << std::endl;
+ return false;
+ }
+ } else if (mYuv420File != nullptr && mYuv420JpegFile == nullptr) { // api-1
+ status =
+ jpegHdr.encodeJPEGR(&mRawP010Image, &mRawYuv420Image, mTf, &mJpegImgR, mQuality, nullptr);
+ if (JPEGR_NO_ERROR != status) {
+ std::cerr << "Encountered error during encodeJPEGR call, error code " << status
+ << std::endl;
+ return false;
+ }
+ } else if (mYuv420File != nullptr && mYuv420JpegFile != nullptr) { // api-2
+ status =
+ jpegHdr.encodeJPEGR(&mRawP010Image, &mRawYuv420Image, &mYuv420JpegImage, mTf, &mJpegImgR);
+ if (JPEGR_NO_ERROR != status) {
+ std::cerr << "Encountered error during encodeJPEGR call, error code " << status
+ << std::endl;
+ return false;
+ }
+ } else if (mYuv420File == nullptr && mYuv420JpegFile != nullptr) { // api-3
+ status = jpegHdr.encodeJPEGR(&mRawP010Image, &mYuv420JpegImage, mTf, &mJpegImgR);
+ if (JPEGR_NO_ERROR != status) {
+ std::cerr << "Encountered error during encodeJPEGR call, error code " << status
+ << std::endl;
+ return false;
+ }
+ }
+#ifdef PROFILE_ENABLE
+ }
+ profileEncode.timerStop();
+ auto avgEncTime = profileEncode.elapsedTime() / (profileCount * 1000.f);
+ printf("Average encode time for res %d x %d is %f ms \n", mWidth, mHeight, avgEncTime);
+#endif
+ writeFile("out.jpeg", mJpegImgR.data, mJpegImgR.length);
+ return true;
+}
+
+bool UltraHdrAppInput::decode() {
+ if (mMode == 1 && !fillJpegRImageHandle()) return false;
+ std::vector<uint8_t> iccData(0);
+ std::vector<uint8_t> exifData(0);
+ jpegr_info_struct info{0, 0, &iccData, &exifData};
+ JpegR jpegHdr;
+ status_t status = jpegHdr.getJPEGRInfo(&mJpegImgR, &info);
+ if (JPEGR_NO_ERROR == status) {
+ size_t outSize = info.width * info.height * ((mOf == ULTRAHDR_OUTPUT_HDR_LINEAR) ? 8 : 4);
+ mDestImage.data = malloc(outSize);
+ if (mDestImage.data == nullptr) {
+ std::cerr << "failed to allocate memory to store decoded output" << std::endl;
+ return false;
+ }
+#ifdef PROFILE_ENABLE
+ const int profileCount = 10;
+ Profiler profileDecode;
+ profileDecode.timerStart();
+ for (auto i = 0; i < profileCount; i++) {
+#endif
+ status =
+ jpegHdr.decodeJPEGR(&mJpegImgR, &mDestImage, FLT_MAX, nullptr, mOf, nullptr, nullptr);
+ if (JPEGR_NO_ERROR != status) {
+ std::cerr << "Encountered error during decodeJPEGR call, error code " << status
+ << std::endl;
+ return false;
+ }
+#ifdef PROFILE_ENABLE
+ }
+ profileDecode.timerStop();
+ auto avgDecTime = profileDecode.elapsedTime() / (profileCount * 1000.f);
+ printf("Average decode time for res %ld x %ld is %f ms \n", info.width, info.height,
+ avgDecTime);
+#endif
+ writeFile("outrgb.raw", mDestImage.data, outSize);
+ } else {
+ std::cerr << "Encountered error during getJPEGRInfo call, error code " << status << std::endl;
+ return false;
+ }
+ return true;
+}
+
+bool UltraHdrAppInput::convertP010ToRGBImage() {
+ const float* coeffs = BT2020YUVtoRGBMatrix;
+ if (mP010Cg == ULTRAHDR_COLORGAMUT_BT709) {
+ coeffs = BT709YUVtoRGBMatrix;
+ } else if (mP010Cg == ULTRAHDR_COLORGAMUT_BT2100) {
+ coeffs = BT2020YUVtoRGBMatrix;
+ } else if (mP010Cg == ULTRAHDR_COLORGAMUT_P3) {
+ coeffs = BT601YUVtoRGBMatrix;
+ } else {
+ std::cerr << "color matrix not present for gamut " << mP010Cg << " using BT2020Matrix"
+ << std::endl;
+ }
+
+ mRawRgba1010102Image.data = malloc(mRawP010Image.width * mRawP010Image.height * 4);
+ if (mRawRgba1010102Image.data == nullptr) {
+ std::cerr << "failed to allocate memory to store Rgba1010102" << std::endl;
+ return false;
+ }
+ mRawRgba1010102Image.width = mRawP010Image.width;
+ mRawRgba1010102Image.height = mRawP010Image.height;
+ mRawRgba1010102Image.colorGamut = mRawP010Image.colorGamut;
+ uint32_t* rgbData = static_cast<uint32_t*>(mRawRgba1010102Image.data);
+ uint16_t* y = static_cast<uint16_t*>(mRawP010Image.data);
+ uint16_t* u = y + mRawP010Image.width * mRawP010Image.height;
+ uint16_t* v = u + 1;
+
+ for (size_t i = 0; i < mRawP010Image.height; i++) {
+ for (size_t j = 0; j < mRawP010Image.width; j++) {
+ float y0 = float(y[mRawP010Image.width * i + j] >> 6);
+ float u0 = float(u[mRawP010Image.width * (i / 2) + (j / 2) * 2] >> 6);
+ float v0 = float(v[mRawP010Image.width * (i / 2) + (j / 2) * 2] >> 6);
+
+ y0 = CLIP3(y0, 64.0f, 940.0f);
+ u0 = CLIP3(u0, 64.0f, 960.0f);
+ v0 = CLIP3(v0, 64.0f, 960.0f);
+
+ y0 = (y0 - 64.0f) / 876.0f;
+ u0 = (u0 - 64.0f) / 896.0f - 0.5f;
+ v0 = (v0 - 64.0f) / 896.0f - 0.5f;
+
+ float r = coeffs[0] * y0 + coeffs[1] * u0 + coeffs[2] * v0;
+ float g = coeffs[3] * y0 + coeffs[4] * u0 + coeffs[5] * v0;
+ float b = coeffs[6] * y0 + coeffs[7] * u0 + coeffs[8] * v0;
+
+ r = CLIP3(r * 1023.0f + 0.5f, 0.0f, 1023.0f);
+ g = CLIP3(g * 1023.0f + 0.5f, 0.0f, 1023.0f);
+ b = CLIP3(b * 1023.0f + 0.5f, 0.0f, 1023.0f);
+
+ int32_t r0 = int32_t(r);
+ int32_t g0 = int32_t(g);
+ int32_t b0 = int32_t(b);
+ *rgbData = (0x3ff & r0) | ((0x3ff & g0) << 10) | ((0x3ff & b0) << 20) |
+ (0x3 << 30); // Set alpha to 1.0
+
+ rgbData++;
+ }
+ }
+ writeFile("inRgba1010102.raw", mRawRgba1010102Image.data,
+ mRawP010Image.width * mRawP010Image.height * 4);
+ return true;
+}
+
+bool UltraHdrAppInput::convertYuv420ToRGBImage() {
+ mRawRgba8888Image.data = malloc(mRawYuv420Image.width * mRawYuv420Image.height * 4);
+ if (mRawRgba8888Image.data == nullptr) {
+ std::cerr << "failed to allocate memory to store rgba888" << std::endl;
+ return false;
+ }
+ mRawRgba8888Image.width = mRawYuv420Image.width;
+ mRawRgba8888Image.height = mRawYuv420Image.height;
+ mRawRgba8888Image.colorGamut = mRawYuv420Image.colorGamut;
+ uint32_t* rgbData = static_cast<uint32_t*>(mRawRgba8888Image.data);
+ uint8_t* y = static_cast<uint8_t*>(mRawYuv420Image.data);
+ uint8_t* u = y + (mRawYuv420Image.width * mRawYuv420Image.height);
+ uint8_t* v = u + (mRawYuv420Image.width * mRawYuv420Image.height / 4);
+
+ const float* coeffs = BT601YUVtoRGBMatrix;
+ for (size_t i = 0; i < mRawYuv420Image.height; i++) {
+ for (size_t j = 0; j < mRawYuv420Image.width; j++) {
+ float y0 = float(y[mRawYuv420Image.width * i + j]);
+ float u0 = float(u[mRawYuv420Image.width / 2 * (i / 2) + (j / 2)] - 128);
+ float v0 = float(v[mRawYuv420Image.width / 2 * (i / 2) + (j / 2)] - 128);
+
+ y0 /= 255.0f;
+ u0 /= 255.0f;
+ v0 /= 255.0f;
+
+ float r = coeffs[0] * y0 + coeffs[1] * u0 + coeffs[2] * v0;
+ float g = coeffs[3] * y0 + coeffs[4] * u0 + coeffs[5] * v0;
+ float b = coeffs[6] * y0 + coeffs[7] * u0 + coeffs[8] * v0;
+
+ r = r * 255.0f + 0.5f;
+ g = g * 255.0f + 0.5f;
+ b = b * 255.0f + 0.5f;
+
+ r = CLIP3(r, 0.0f, 255.0f);
+ g = CLIP3(g, 0.0f, 255.0f);
+ b = CLIP3(b, 0.0f, 255.0f);
+
+ int32_t r0 = int32_t(r);
+ int32_t g0 = int32_t(g);
+ int32_t b0 = int32_t(b);
+ *rgbData = r0 | (g0 << 8) | (b0 << 16) | (255 << 24); // Set alpha to 1.0
+
+ rgbData++;
+ }
+ }
+ writeFile("inRgba8888.raw", mRawRgba8888Image.data,
+ mRawYuv420Image.width * mRawYuv420Image.height * 4);
+ return true;
+}
+
+bool UltraHdrAppInput::convertRgba8888ToYUV444Image() {
+ mDestYUV444Image.data = malloc(mDestImage.width * mDestImage.height * 3);
+ if (mDestYUV444Image.data == nullptr) {
+ std::cerr << "failed to allocate memory to store yuv444" << std::endl;
+ return false;
+ }
+ mDestYUV444Image.width = mDestImage.width;
+ mDestYUV444Image.height = mDestImage.height;
+ mDestYUV444Image.colorGamut = mDestImage.colorGamut;
+
+ uint32_t* rgbData = static_cast<uint32_t*>(mDestImage.data);
+
+ uint8_t* yData = static_cast<uint8_t*>(mDestYUV444Image.data);
+ uint8_t* uData = yData + (mDestYUV444Image.width * mDestYUV444Image.height);
+ uint8_t* vData = uData + (mDestYUV444Image.width * mDestYUV444Image.height);
+
+ const float* coeffs = BT601RGBtoYUVMatrix;
+ for (size_t i = 0; i < mDestImage.height; i++) {
+ for (size_t j = 0; j < mDestImage.width; j++) {
+ float r0 = float(rgbData[mDestImage.width * i + j] & 0xff);
+ float g0 = float((rgbData[mDestImage.width * i + j] >> 8) & 0xff);
+ float b0 = float((rgbData[mDestImage.width * i + j] >> 16) & 0xff);
+
+ r0 /= 255.0f;
+ g0 /= 255.0f;
+ b0 /= 255.0f;
+
+ float y = coeffs[0] * r0 + coeffs[1] * g0 + coeffs[2] * b0;
+ float u = coeffs[3] * r0 + coeffs[4] * g0 + coeffs[5] * b0;
+ float v = coeffs[6] * r0 + coeffs[7] * g0 + coeffs[8] * b0;
+
+ y = y * 255.0f + 0.5f;
+ u = u * 255.0f + 0.5f + 128.0f;
+ v = v * 255.0f + 0.5f + 128.0f;
+
+ y = CLIP3(y, 0.0f, 255.0f);
+ u = CLIP3(u, 0.0f, 255.0f);
+ v = CLIP3(v, 0.0f, 255.0f);
+
+ yData[mDestYUV444Image.width * i + j] = uint8_t(y);
+ uData[mDestYUV444Image.width * i + j] = uint8_t(u);
+ vData[mDestYUV444Image.width * i + j] = uint8_t(v);
+ }
+ }
+ writeFile("outyuv444.yuv", mDestYUV444Image.data,
+ mDestYUV444Image.width * mDestYUV444Image.height * 3);
+ return true;
+}
+
+bool UltraHdrAppInput::convertRgba1010102ToYUV444Image() {
+ const float* coeffs = BT2020RGBtoYUVMatrix;
+ if (mP010Cg == ULTRAHDR_COLORGAMUT_BT709) {
+ coeffs = BT709RGBtoYUVMatrix;
+ } else if (mP010Cg == ULTRAHDR_COLORGAMUT_BT2100) {
+ coeffs = BT2020RGBtoYUVMatrix;
+ } else if (mP010Cg == ULTRAHDR_COLORGAMUT_P3) {
+ coeffs = BT601RGBtoYUVMatrix;
+ } else {
+ std::cerr << "color matrix not present for gamut " << mP010Cg << " using BT2020Matrix"
+ << std::endl;
+ }
+
+ mDestYUV444Image.data = malloc(mDestImage.width * mDestImage.height * 3 * 2);
+ if (mDestYUV444Image.data == nullptr) {
+ std::cerr << "failed to allocate memory to store yuv444" << std::endl;
+ return false;
+ }
+ mDestYUV444Image.width = mDestImage.width;
+ mDestYUV444Image.height = mDestImage.height;
+ mDestYUV444Image.colorGamut = mDestImage.colorGamut;
+
+ uint32_t* rgbData = static_cast<uint32_t*>(mDestImage.data);
+
+ uint16_t* yData = static_cast<uint16_t*>(mDestYUV444Image.data);
+ uint16_t* uData = yData + (mDestYUV444Image.width * mDestYUV444Image.height);
+ uint16_t* vData = uData + (mDestYUV444Image.width * mDestYUV444Image.height);
+
+ for (size_t i = 0; i < mDestImage.height; i++) {
+ for (size_t j = 0; j < mDestImage.width; j++) {
+ float r0 = float(rgbData[mDestImage.width * i + j] & 0x3ff);
+ float g0 = float((rgbData[mDestImage.width * i + j] >> 10) & 0x3ff);
+ float b0 = float((rgbData[mDestImage.width * i + j] >> 20) & 0x3ff);
+
+ r0 /= 1023.0f;
+ g0 /= 1023.0f;
+ b0 /= 1023.0f;
+
+ float y = coeffs[0] * r0 + coeffs[1] * g0 + coeffs[2] * b0;
+ float u = coeffs[3] * r0 + coeffs[4] * g0 + coeffs[5] * b0;
+ float v = coeffs[6] * r0 + coeffs[7] * g0 + coeffs[8] * b0;
+
+ y = (y * 876.0f) + 64.0f + 0.5f;
+ u = (u * 896.0f) + 64.0f + 512.0f + 0.5f;
+ v = (v * 896.0f) + 64.0f + 512.0f + 0.5f;
+
+ y = CLIP3(y, 64.0f, 940.0f);
+ u = CLIP3(u, 64.0f, 960.0f);
+ v = CLIP3(v, 64.0f, 960.0f);
+
+ yData[mDestYUV444Image.width * i + j] = uint16_t(y);
+ uData[mDestYUV444Image.width * i + j] = uint16_t(u);
+ vData[mDestYUV444Image.width * i + j] = uint16_t(v);
+ }
+ }
+ writeFile("outyuv444.yuv", mDestYUV444Image.data,
+ mDestYUV444Image.width * mDestYUV444Image.height * 3 * 2);
+ return true;
+}
+
+void UltraHdrAppInput::computeRGBHdrPSNR() {
+ if (mOf == ULTRAHDR_OUTPUT_SDR || mOf == ULTRAHDR_OUTPUT_HDR_LINEAR) {
+ std::cout << "psnr not supported for output format " << mOf << std::endl;
+ return;
+ }
+ uint32_t* rgbDataSrc = static_cast<uint32_t*>(mRawRgba1010102Image.data);
+ uint32_t* rgbDataDst = static_cast<uint32_t*>(mDestImage.data);
+ if (rgbDataSrc == nullptr || rgbDataDst == nullptr) {
+ std::cerr << "invalid src or dst pointer for psnr computation " << std::endl;
+ return;
+ }
+ if ((mOf == ULTRAHDR_OUTPUT_HDR_PQ && mTf != ULTRAHDR_TF_PQ) ||
+ (mOf == ULTRAHDR_OUTPUT_HDR_HLG && mTf != ULTRAHDR_TF_HLG)) {
+ std::cout << "input transfer function and output format are not compatible, psnr results "
+ "may be unreliable"
+ << std::endl;
+ }
+ uint64_t rSqError = 0, gSqError = 0, bSqError = 0;
+ for (size_t i = 0; i < mRawP010Image.width * mRawP010Image.height; i++) {
+ int rSrc = *rgbDataSrc & 0x3ff;
+ int rDst = *rgbDataDst & 0x3ff;
+ rSqError += (rSrc - rDst) * (rSrc - rDst);
+
+ int gSrc = (*rgbDataSrc >> 10) & 0x3ff;
+ int gDst = (*rgbDataDst >> 10) & 0x3ff;
+ gSqError += (gSrc - gDst) * (gSrc - gDst);
+
+ int bSrc = (*rgbDataSrc >> 20) & 0x3ff;
+ int bDst = (*rgbDataDst >> 20) & 0x3ff;
+ bSqError += (bSrc - bDst) * (bSrc - bDst);
+
+ rgbDataSrc++;
+ rgbDataDst++;
+ }
+ double meanSquareError = (double)rSqError / (mRawP010Image.width * mRawP010Image.height);
+ mPsnr[0] = meanSquareError ? 10 * log10((double)1023 * 1023 / meanSquareError) : 100;
+
+ meanSquareError = (double)gSqError / (mRawP010Image.width * mRawP010Image.height);
+ mPsnr[1] = meanSquareError ? 10 * log10((double)1023 * 1023 / meanSquareError) : 100;
+
+ meanSquareError = (double)bSqError / (mRawP010Image.width * mRawP010Image.height);
+ mPsnr[2] = meanSquareError ? 10 * log10((double)1023 * 1023 / meanSquareError) : 100;
+
+ std::cout << "psnr r :: " << mPsnr[0] << " psnr g :: " << mPsnr[1] << " psnr b :: " << mPsnr[2]
+ << std::endl;
+}
+
+void UltraHdrAppInput::computeRGBSdrPSNR() {
+ if (mOf != ULTRAHDR_OUTPUT_SDR) {
+ std::cout << "psnr not supported for output format " << mOf << std::endl;
+ return;
+ }
+ uint32_t* rgbDataSrc = static_cast<uint32_t*>(mRawRgba8888Image.data);
+ uint32_t* rgbDataDst = static_cast<uint32_t*>(mDestImage.data);
+ if (rgbDataSrc == nullptr || rgbDataDst == nullptr) {
+ std::cerr << "invalid src or dst pointer for psnr computation " << std::endl;
+ return;
+ }
+
+ uint64_t rSqError = 0, gSqError = 0, bSqError = 0;
+ for (size_t i = 0; i < mRawYuv420Image.width * mRawYuv420Image.height; i++) {
+ int rSrc = *rgbDataSrc & 0xff;
+ int rDst = *rgbDataDst & 0xff;
+ rSqError += (rSrc - rDst) * (rSrc - rDst);
+
+ int gSrc = (*rgbDataSrc >> 8) & 0xff;
+ int gDst = (*rgbDataDst >> 8) & 0xff;
+ gSqError += (gSrc - gDst) * (gSrc - gDst);
+
+ int bSrc = (*rgbDataSrc >> 16) & 0xff;
+ int bDst = (*rgbDataDst >> 16) & 0xff;
+ bSqError += (bSrc - bDst) * (bSrc - bDst);
+
+ rgbDataSrc++;
+ rgbDataDst++;
+ }
+ double meanSquareError = (double)rSqError / (mRawYuv420Image.width * mRawYuv420Image.height);
+ mPsnr[0] = meanSquareError ? 10 * log10((double)255 * 255 / meanSquareError) : 100;
+
+ meanSquareError = (double)gSqError / (mRawYuv420Image.width * mRawYuv420Image.height);
+ mPsnr[1] = meanSquareError ? 10 * log10((double)255 * 255 / meanSquareError) : 100;
+
+ meanSquareError = (double)bSqError / (mRawYuv420Image.width * mRawYuv420Image.height);
+ mPsnr[2] = meanSquareError ? 10 * log10((double)255 * 255 / meanSquareError) : 100;
+
+ std::cout << "psnr r :: " << mPsnr[0] << " psnr g :: " << mPsnr[1] << " psnr b :: " << mPsnr[2]
+ << std::endl;
+}
+
+void UltraHdrAppInput::computeYUVHdrPSNR() {
+ if (mOf == ULTRAHDR_OUTPUT_SDR || mOf == ULTRAHDR_OUTPUT_HDR_LINEAR) {
+ std::cout << "psnr not supported for output format " << mOf << std::endl;
+ return;
+ }
+ uint16_t* yuvDataSrc = static_cast<uint16_t*>(mRawP010Image.data);
+ uint16_t* yuvDataDst = static_cast<uint16_t*>(mDestYUV444Image.data);
+ if (yuvDataSrc == nullptr || yuvDataDst == nullptr) {
+ std::cerr << "invalid src or dst pointer for psnr computation " << std::endl;
+ return;
+ }
+ if ((mOf == ULTRAHDR_OUTPUT_HDR_PQ && mTf != ULTRAHDR_TF_PQ) ||
+ (mOf == ULTRAHDR_OUTPUT_HDR_HLG && mTf != ULTRAHDR_TF_HLG)) {
+ std::cout << "input transfer function and output format are not compatible, psnr results "
+ "may be unreliable"
+ << std::endl;
+ }
+
+ uint16_t* yDataSrc = static_cast<uint16_t*>(mRawP010Image.data);
+ uint16_t* uDataSrc = yDataSrc + (mRawP010Image.width * mRawP010Image.height);
+ uint16_t* vDataSrc = uDataSrc + 1;
+
+ uint16_t* yDataDst = static_cast<uint16_t*>(mDestYUV444Image.data);
+ uint16_t* uDataDst = yDataDst + (mDestYUV444Image.width * mDestYUV444Image.height);
+ uint16_t* vDataDst = uDataDst + (mDestYUV444Image.width * mDestYUV444Image.height);
+
+ uint64_t ySqError = 0, uSqError = 0, vSqError = 0;
+ for (size_t i = 0; i < mDestYUV444Image.height; i++) {
+ for (size_t j = 0; j < mDestYUV444Image.width; j++) {
+ int ySrc = (yDataSrc[mRawP010Image.width * i + j] >> 6) & 0x3ff;
+ ySrc = CLIP3(ySrc, 64, 940);
+ int yDst = yDataDst[mDestYUV444Image.width * i + j] & 0x3ff;
+ ySqError += (ySrc - yDst) * (ySrc - yDst);
+
+ if (i % 2 == 0 && j % 2 == 0) {
+ int uSrc = (uDataSrc[mRawP010Image.width * (i / 2) + (j / 2) * 2] >> 6) & 0x3ff;
+ uSrc = CLIP3(uSrc, 64, 960);
+ int uDst = uDataDst[mDestYUV444Image.width * i + j] & 0x3ff;
+ uDst += uDataDst[mDestYUV444Image.width * i + j + 1] & 0x3ff;
+ uDst += uDataDst[mDestYUV444Image.width * (i + 1) + j + 1] & 0x3ff;
+ uDst += uDataDst[mDestYUV444Image.width * (i + 1) + j + 1] & 0x3ff;
+ uDst = (uDst + 2) >> 2;
+ uSqError += (uSrc - uDst) * (uSrc - uDst);
+
+ int vSrc = (vDataSrc[mRawP010Image.width * (i / 2) + (j / 2) * 2] >> 6) & 0x3ff;
+ vSrc = CLIP3(vSrc, 64, 960);
+ int vDst = vDataDst[mDestYUV444Image.width * i + j] & 0x3ff;
+ vDst += vDataDst[mDestYUV444Image.width * i + j + 1] & 0x3ff;
+ vDst += vDataDst[mDestYUV444Image.width * (i + 1) + j + 1] & 0x3ff;
+ vDst += vDataDst[mDestYUV444Image.width * (i + 1) + j + 1] & 0x3ff;
+ vDst = (vDst + 2) >> 2;
+ vSqError += (vSrc - vDst) * (vSrc - vDst);
+ }
+ }
+ }
+
+ double meanSquareError = (double)ySqError / (mDestYUV444Image.width * mDestYUV444Image.height);
+ mPsnr[0] = meanSquareError ? 10 * log10((double)1023 * 1023 / meanSquareError) : 100;
+
+ meanSquareError = (double)uSqError / (mDestYUV444Image.width * mDestYUV444Image.height / 4);
+ mPsnr[1] = meanSquareError ? 10 * log10((double)1023 * 1023 / meanSquareError) : 100;
+
+ meanSquareError = (double)vSqError / (mDestYUV444Image.width * mDestYUV444Image.height / 4);
+ mPsnr[2] = meanSquareError ? 10 * log10((double)1023 * 1023 / meanSquareError) : 100;
+
+ std::cout << "psnr y :: " << mPsnr[0] << " psnr u :: " << mPsnr[1] << " psnr v :: " << mPsnr[2]
+ << std::endl;
+}
+
+void UltraHdrAppInput::computeYUVSdrPSNR() {
+ if (mOf != ULTRAHDR_OUTPUT_SDR) {
+ std::cout << "psnr not supported for output format " << mOf << std::endl;
+ return;
+ }
+
+ uint8_t* yDataSrc = static_cast<uint8_t*>(mRawYuv420Image.data);
+ uint8_t* uDataSrc = yDataSrc + (mRawYuv420Image.width * mRawYuv420Image.height);
+ uint8_t* vDataSrc = uDataSrc + (mRawYuv420Image.width * mRawYuv420Image.height / 4);
+
+ uint8_t* yDataDst = static_cast<uint8_t*>(mDestYUV444Image.data);
+ uint8_t* uDataDst = yDataDst + (mDestYUV444Image.width * mDestYUV444Image.height);
+ uint8_t* vDataDst = uDataDst + (mDestYUV444Image.width * mDestYUV444Image.height);
+
+ uint64_t ySqError = 0, uSqError = 0, vSqError = 0;
+ for (size_t i = 0; i < mDestYUV444Image.height; i++) {
+ for (size_t j = 0; j < mDestYUV444Image.width; j++) {
+ int ySrc = yDataSrc[mRawYuv420Image.width * i + j];
+ int yDst = yDataDst[mDestYUV444Image.width * i + j];
+ ySqError += (ySrc - yDst) * (ySrc - yDst);
+
+ if (i % 2 == 0 && j % 2 == 0) {
+ int uSrc = uDataSrc[mRawYuv420Image.width / 2 * (i / 2) + j / 2];
+ int uDst = uDataDst[mDestYUV444Image.width * i + j];
+ uDst += uDataDst[mDestYUV444Image.width * i + j + 1];
+ uDst += uDataDst[mDestYUV444Image.width * (i + 1) + j];
+ uDst += uDataDst[mDestYUV444Image.width * (i + 1) + j + 1];
+ uDst = (uDst + 2) >> 2;
+ uSqError += (uSrc - uDst) * (uSrc - uDst);
+
+ int vSrc = vDataSrc[mRawYuv420Image.width / 2 * (i / 2) + j / 2];
+ int vDst = vDataDst[mDestYUV444Image.width * i + j];
+ vDst += vDataDst[mDestYUV444Image.width * i + j + 1];
+ vDst += vDataDst[mDestYUV444Image.width * (i + 1) + j];
+ vDst += vDataDst[mDestYUV444Image.width * (i + 1) + j + 1];
+ vDst = (vDst + 2) >> 2;
+ vSqError += (vSrc - vDst) * (vSrc - vDst);
+ }
+ }
+ }
+ double meanSquareError = (double)ySqError / (mDestYUV444Image.width * mDestYUV444Image.height);
+ mPsnr[0] = meanSquareError ? 10 * log10((double)255 * 255 / meanSquareError) : 100;
+
+ meanSquareError = (double)uSqError / (mDestYUV444Image.width * mDestYUV444Image.height / 4);
+ mPsnr[1] = meanSquareError ? 10 * log10((double)255 * 255 / meanSquareError) : 100;
+
+ meanSquareError = (double)vSqError / (mDestYUV444Image.width * mDestYUV444Image.height / 4);
+ mPsnr[2] = meanSquareError ? 10 * log10((double)255 * 255 / meanSquareError) : 100;
+
+ std::cout << "psnr y :: " << mPsnr[0] << " psnr u:: " << mPsnr[1] << " psnr v :: " << mPsnr[2]
+ << std::endl;
+}
+
+static void usage(const char* name) {
+ fprintf(stderr, "\n## ultra hdr demo application.\nUsage : %s \n", name);
+ fprintf(stderr, " -m mode of operation. [0: encode, 1:decode] \n");
+ fprintf(stderr, "\n## encoder options : \n");
+ fprintf(stderr, " -p raw 10 bit input resource in p010 color format, mandatory. \n");
+ fprintf(stderr,
+ " -y raw 8 bit input resource in yuv420, optional. \n"
+ " if not provided tonemapping happens internally. \n");
+ fprintf(stderr, " -i compressed 8 bit jpeg file path, optional \n");
+ fprintf(stderr, " -w input file width, mandatory. \n");
+ fprintf(stderr, " -h input file height, mandatory. \n");
+ fprintf(stderr, " -C 10 bit input color gamut, optional. [0:bt709, 1:p3, 2:bt2100] \n");
+ fprintf(stderr, " -c 8 bit input color gamut, optional. [0:bt709, 1:p3, 2:bt2100] \n");
+ fprintf(stderr, " -t input transfer function, optional. [0:linear, 1:hlg, 2:pq] \n");
+ fprintf(stderr,
+ " -q quality factor to be used while encoding 8 bit image, optional. [0-100].\n"
+ " gain map image does not use this quality factor. \n"
+ " for now gain map image quality factor is not configurable. \n");
+ fprintf(stderr, " -e compute psnr, optional. [0:yes, 1:no] \n");
+ fprintf(stderr, "\n## decoder options : \n");
+ fprintf(stderr, " -j ultra hdr input resource, mandatory in decode mode. \n");
+ fprintf(stderr,
+ " -o output transfer function, optional. [0:sdr, 1:hdr_linear, 2:hdr_pq, "
+ "3:hdr_hlg] \n");
+ fprintf(stderr, "\n## examples of usage :\n");
+ fprintf(stderr, "\n## encode api-0 :\n");
+ fprintf(stderr, " ultrahdr_app -m 0 -p cosmat_1920x1080_p010.yuv -w 1920 -h 1080 -q 97\n");
+ fprintf(stderr,
+ " ultrahdr_app -m 0 -p cosmat_1920x1080_p010.yuv -w 1920 -h 1080 -q 97 -C 2 -t 2\n");
+ fprintf(stderr, "\n## encode api-1 :\n");
+ fprintf(stderr,
+ " ultrahdr_app -m 0 -p cosmat_1920x1080_p010.yuv -y cosmat_1920x1080_420.yuv -w 1920 "
+ "-h 1080 -q 97\n");
+ fprintf(stderr,
+ " ultrahdr_app -m 0 -p cosmat_1920x1080_p010.yuv -y cosmat_1920x1080_420.yuv -w 1920 "
+ "-h 1080 -q 97\n");
+ fprintf(stderr,
+ " ultrahdr_app -m 0 -p cosmat_1920x1080_p010.yuv -y cosmat_1920x1080_420.yuv -w 1920 "
+ "-h 1080 -q 97 -C 2 -c 1 -t 1\n");
+ fprintf(stderr,
+ " ultrahdr_app -m 0 -p cosmat_1920x1080_p010.yuv -y cosmat_1920x1080_420.yuv -w 1920 "
+ "-h 1080 -q 97 -C 2 -c 1 -t 1 -e 1\n");
+ fprintf(stderr, "\n## encode api-2 :\n");
+ fprintf(stderr,
+ " ultrahdr_app -m 0 -p cosmat_1920x1080_p010.yuv -y cosmat_1920x1080_420.yuv -i "
+ "cosmat_1920x1080_420_8bit.jpg -w 1920 -h 1080 -t 1 -o 3 -e 1\n");
+ fprintf(stderr, "\n## encode api-3 :\n");
+ fprintf(stderr,
+ " ultrahdr_app -m 0 -p cosmat_1920x1080_p010.yuv -i cosmat_1920x1080_420_8bit.jpg -w "
+ "1920 -h 1080 -t 1 -o 3 -e 1\n");
+ fprintf(stderr, "\n## decode api :\n");
+ fprintf(stderr, " ultrahdr_app -m 1 -j cosmat_1920x1080_hdr.jpg \n");
+ fprintf(stderr, " ultrahdr_app -m 1 -j cosmat_1920x1080_hdr.jpg -o 2\n");
+ fprintf(stderr, "\n");
+}
+
+int main(int argc, char* argv[]) {
+ char opt_string[] = "p:y:i:w:h:C:c:t:q:o:m:j:e:";
+ char *p010_file = nullptr, *yuv420_file = nullptr, *jpegr_file = nullptr,
+ *yuv420_jpeg_file = nullptr;
+ int width = 0, height = 0;
+ ultrahdr_color_gamut p010Cg = ULTRAHDR_COLORGAMUT_BT709;
+ ultrahdr_color_gamut yuv420Cg = ULTRAHDR_COLORGAMUT_BT709;
+ ultrahdr_transfer_function tf = ULTRAHDR_TF_HLG;
+ int quality = 100;
+ ultrahdr_output_format of = ULTRAHDR_OUTPUT_HDR_HLG;
+ int mode = 0;
+ int compute_psnr = 0;
+ int ch;
+ while ((ch = getopt_s(argc, argv, opt_string)) != -1) {
+ switch (ch) {
+ case 'p':
+ p010_file = optarg_s;
+ break;
+ case 'y':
+ yuv420_file = optarg_s;
+ break;
+ case 'i':
+ yuv420_jpeg_file = optarg_s;
+ break;
+ case 'w':
+ width = atoi(optarg_s);
+ break;
+ case 'h':
+ height = atoi(optarg_s);
+ break;
+ case 'C':
+ p010Cg = static_cast<ultrahdr_color_gamut>(atoi(optarg_s));
+ break;
+ case 'c':
+ yuv420Cg = static_cast<ultrahdr_color_gamut>(atoi(optarg_s));
+ break;
+ case 't':
+ tf = static_cast<ultrahdr_transfer_function>(atoi(optarg_s));
+ break;
+ case 'q':
+ quality = atoi(optarg_s);
+ break;
+ case 'o':
+ of = static_cast<ultrahdr_output_format>(atoi(optarg_s));
+ break;
+ case 'm':
+ mode = atoi(optarg_s);
+ break;
+ case 'j':
+ jpegr_file = optarg_s;
+ break;
+ case 'e':
+ compute_psnr = atoi(optarg_s);
+ break;
+ default:
+ usage(argv[0]);
+ return -1;
+ }
+ }
+ if (mode == 0) {
+ if (width <= 0 || height <= 0 || p010_file == nullptr) {
+ usage(argv[0]);
+ return -1;
+ }
+ UltraHdrAppInput appInput(p010_file, yuv420_file, yuv420_jpeg_file, width, height, p010Cg,
+ yuv420Cg, tf, quality, of);
+ if (!appInput.encode()) return -1;
+ if (compute_psnr == 1) {
+ if (!appInput.decode()) return -1;
+ if (of == ULTRAHDR_OUTPUT_SDR && yuv420_file != nullptr) {
+ appInput.convertYuv420ToRGBImage();
+ appInput.computeRGBSdrPSNR();
+ appInput.convertRgba8888ToYUV444Image();
+ appInput.computeYUVSdrPSNR();
+ } else if (of == ULTRAHDR_OUTPUT_HDR_HLG || of == ULTRAHDR_OUTPUT_HDR_PQ) {
+ appInput.convertP010ToRGBImage();
+ appInput.computeRGBHdrPSNR();
+ appInput.convertRgba1010102ToYUV444Image();
+ appInput.computeYUVHdrPSNR();
+ }
+ }
+ } else if (mode == 1) {
+ if (jpegr_file == nullptr) {
+ usage(argv[0]);
+ return -1;
+ }
+ UltraHdrAppInput appInput(jpegr_file, of);
+ if (!appInput.decode()) return -1;
+ } else {
+ std::cerr << "unrecognized input mode " << mode << std::endl;
+ usage(argv[0]);
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/fuzzer/README.md b/fuzzer/README.md
index c029fc0..e48d859 100644
--- a/fuzzer/README.md
+++ b/fuzzer/README.md
@@ -1,73 +1,72 @@
-# Fuzzer for ultrahdr decoder and encoder
-
-This describes steps to build ultrahdr_dec_fuzzer and ultrahdr_enc_fuzzer.
-
-## Linux x86/x64
-
-### Requirements
-- cmake (3.5 or above)
-- make
-- clang (12.0 or above)
- needs to support -fsanitize=fuzzer, -fsanitize=fuzzer-no-link
-
-### Steps to build
-Create a directory inside libultrahdr and change directory
-```
- $ cd libultrahdr
- $ mkdir build
- $ cd build
-```
-Build fuzzer with required sanitizers
-Note: Using clang and setting -DENABLE_FUZZERS=ON is mandatory to enable fuzzers.
-```
- $ cmake .. -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ \
- -DCMAKE_BUILD_TYPE=Debug -DENABLE_FUZZERS=ON -DSANITIZE=address,\
- signed-integer-overflow,unsigned-integer-overflow
- $ make
- ```
-
-### Steps to run
-Create a directory CORPUS_DIR and copy some elementary ultrahdr files
-(for ultrahdr_dec_fuzzer) or yuv files (for ultrahdr_enc_fuzzer) to that directory
-
-To run the fuzzers
-```
-$ ./ultrahdr_dec_fuzzer CORPUS_DIR
-$ ./ultrahdr_enc_fuzzer CORPUS_DIR
-```
-
-## Android
-
-### Steps to build
-Build the fuzzers
-```
- $ mm -j$(nproc) ultrahdr_dec_fuzzer
- $ mm -j$(nproc) ultrahdr_enc_fuzzer
-```
-
-### Steps to run
-Create a directory CORPUS_DIR and copy some elementary ultrahdr files
-(for ultrahdr_dec_fuzzer) or yuv files (for ultrahdr_enc_fuzzer) to that folder
-Push this directory to device
-
-To run ultrahdr_dec_fuzzer on device
-```
- $ adb sync data
- $ adb shell /data/fuzz/arm64/ultrahdr_dec_fuzzer/ultrahdr_dec_fuzzer CORPUS_DIR
-```
-
-To run ultrahdr_enc_fuzzer on device
-```
- $ adb sync data
- $ adb shell /data/fuzz/arm64/ultrahdr_enc_fuzzer/ultrahdr_enc_fuzzer CORPUS_DIR
-```
-
-To run ultrahdr_dec_fuzzer on host
-```
- $ $ANDROID_HOST_OUT/fuzz/x86_64/ultrahdr_dec_fuzzer/ultrahdr_dec_fuzzer CORPUS_DIR
-```
-
-To run ultrahdr_enc_fuzzer on host
-```
- $ $ANDROID_HOST_OUT/fuzz/x86_64/ultrahdr_enc_fuzzer/ultrahdr_enc_fuzzer CORPUS_DIR
-```
+Building fuzzers for libultrahdr
+================================
+
+### Requirements
+
+- Refer [Requirements](../README.md#Requirements)
+
+- Additionally compilers are required to support options *-fsanitize=fuzzer, -fsanitize=fuzzer-no-link*.
+ For instance, clang 12 (or later)
+
+### Building Commands
+
+ mkdir {build_directory}
+ cd {build_directory}
+ cmake ../ -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DUHDR_BUILD_FUZZERS=1
+ make
+
+This will generate the following files under *{build_directory}*:
+
+**libultrahdr.a**<br> Instrumented ultrahdr library
+
+**ultrahdr_enc_fuzzer**<br> ultrahdr encoder fuzzer
+
+**ultrahdr_dec_fuzzer**<br> ultrahdr decoder fuzzer
+
+Additionally, while building fuzzers, user can enable sanitizers by providing desired
+sanitizer option(s) through UHDR_SANITIZE_OPTIONS.
+
+To enable ASan,
+
+ cmake ../ -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ \
+ -DUHDR_BUILD_FUZZERS=1 -DUHDR_SANITIZE_OPTIONS=address
+ make
+
+To enable MSan,
+
+ cmake ../ -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ \
+ -DUHDR_BUILD_FUZZERS=1 -DUHDR_SANITIZE_OPTIONS=memory
+ make
+
+To enable TSan,
+
+ cmake ../ -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ \
+ -DUHDR_BUILD_FUZZERS=1 -DUHDR_SANITIZE_OPTIONS=thread
+ make
+
+To enable UBSan,
+
+ cmake ../ -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ \
+ -DUHDR_BUILD_FUZZERS=1 -DUHDR_SANITIZE_OPTIONS=undefined
+ make
+
+UBSan can be grouped with ASan, MSan or TSan.
+
+For example, to enable ASan and UBSan,
+
+ cmake ../ -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ \
+ -DUHDR_BUILD_FUZZERS=1 -DUHDR_SANITIZE_OPTIONS=address,undefined
+ make
+
+### Running
+
+To run the fuzzer(s), first create a corpus directory that holds the initial
+"seed" sample inputs. For decoder fuzzer, ultrahdr jpeg images can be used and
+for encoder fuzzer, sample yuv files can be used.
+
+Then run the fuzzers on the corpus directory.
+
+ mkdir CORPUS_DIR
+ cp seeds/* CORPUS_DIR
+ ./ultrahdr_dec_fuzzer CORPUS_DIR
+ ./ultrahdr_enc_fuzzer CORPUS_DIR
diff --git a/fuzzer/ossfuzz.sh b/fuzzer/ossfuzz.sh
index b88aae5..262d629 100755
--- a/fuzzer/ossfuzz.sh
+++ b/fuzzer/ossfuzz.sh
@@ -23,7 +23,7 @@ rm -rf ${build_dir}
mkdir -p ${build_dir}
pushd ${build_dir}
-cmake $SRC/libultrahdr -DENABLE_FUZZERS=ON
+cmake $SRC/libultrahdr -DUHDR_BUILD_FUZZERS=1
make -j$(nproc) ultrahdr_dec_fuzzer ultrahdr_enc_fuzzer
cp ${build_dir}/ultrahdr_dec_fuzzer $OUT/
cp ${build_dir}/ultrahdr_enc_fuzzer $OUT/
diff --git a/fuzzer/ultrahdr_dec_fuzzer.cpp b/fuzzer/ultrahdr_dec_fuzzer.cpp
index 9be5e87..0e9c5d3 100644
--- a/fuzzer/ultrahdr_dec_fuzzer.cpp
+++ b/fuzzer/ultrahdr_dec_fuzzer.cpp
@@ -18,7 +18,7 @@
#include <iostream>
#include <memory>
-#include "ultrahdr/jpegr.h"
+#include "jpegr.h"
using namespace ultrahdr;
@@ -27,46 +27,46 @@ const int kOfMin = ULTRAHDR_OUTPUT_UNSPECIFIED + 1;
const int kOfMax = ULTRAHDR_OUTPUT_MAX;
class UltraHdrDecFuzzer {
-public:
- UltraHdrDecFuzzer(const uint8_t* data, size_t size) : mFdp(data, size){};
- void process();
+ public:
+ UltraHdrDecFuzzer(const uint8_t* data, size_t size) : mFdp(data, size){};
+ void process();
-private:
- FuzzedDataProvider mFdp;
+ private:
+ FuzzedDataProvider mFdp;
};
void UltraHdrDecFuzzer::process() {
- // hdr_of
- auto of = static_cast<ultrahdr_output_format>(mFdp.ConsumeIntegralInRange<int>(kOfMin, kOfMax));
- auto buffer = mFdp.ConsumeRemainingBytes<uint8_t>();
- jpegr_compressed_struct jpegImgR{buffer.data(), (int)buffer.size(), (int)buffer.size(),
- ULTRAHDR_COLORGAMUT_UNSPECIFIED};
+ // hdr_of
+ auto of = static_cast<ultrahdr_output_format>(mFdp.ConsumeIntegralInRange<int>(kOfMin, kOfMax));
+ auto buffer = mFdp.ConsumeRemainingBytes<uint8_t>();
+ jpegr_compressed_struct jpegImgR{buffer.data(), (int)buffer.size(), (int)buffer.size(),
+ ULTRAHDR_COLORGAMUT_UNSPECIFIED};
- std::vector<uint8_t> iccData(0);
- std::vector<uint8_t> exifData(0);
- jpegr_info_struct info{0, 0, &iccData, &exifData};
- JpegR jpegHdr;
- (void)jpegHdr.getJPEGRInfo(&jpegImgR, &info);
+ std::vector<uint8_t> iccData(0);
+ std::vector<uint8_t> exifData(0);
+ jpegr_info_struct info{0, 0, &iccData, &exifData};
+ JpegR jpegHdr;
+ (void)jpegHdr.getJPEGRInfo(&jpegImgR, &info);
//#define DUMP_PARAM
#ifdef DUMP_PARAM
- std::cout << "input buffer size " << jpegImgR.length << std::endl;
- std::cout << "image dimensions " << info.width << " x " << info.width << std::endl;
+ std::cout << "input buffer size " << jpegImgR.length << std::endl;
+ std::cout << "image dimensions " << info.width << " x " << info.width << std::endl;
#endif
- if (info.width > kMaxWidth || info.height > kMaxHeight) return;
- size_t outSize = info.width * info.height * ((of == ULTRAHDR_OUTPUT_HDR_LINEAR) ? 8 : 4);
- jpegr_uncompressed_struct decodedJpegR;
- auto decodedRaw = std::make_unique<uint8_t[]>(outSize);
- decodedJpegR.data = decodedRaw.get();
- ultrahdr_metadata_struct metadata;
- jpegr_uncompressed_struct decodedGainMap{};
- (void)jpegHdr.decodeJPEGR(&jpegImgR, &decodedJpegR,
- mFdp.ConsumeFloatingPointInRange<float>(1.0, FLT_MAX), nullptr, of,
- &decodedGainMap, &metadata);
- if (decodedGainMap.data) free(decodedGainMap.data);
+ if (info.width > kMaxWidth || info.height > kMaxHeight) return;
+ size_t outSize = info.width * info.height * ((of == ULTRAHDR_OUTPUT_HDR_LINEAR) ? 8 : 4);
+ jpegr_uncompressed_struct decodedJpegR;
+ auto decodedRaw = std::make_unique<uint8_t[]>(outSize);
+ decodedJpegR.data = decodedRaw.get();
+ ultrahdr_metadata_struct metadata;
+ jpegr_uncompressed_struct decodedGainMap{};
+ (void)jpegHdr.decodeJPEGR(&jpegImgR, &decodedJpegR,
+ mFdp.ConsumeFloatingPointInRange<float>(1.0, FLT_MAX), nullptr, of,
+ &decodedGainMap, &metadata);
+ if (decodedGainMap.data) free(decodedGainMap.data);
}
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
- UltraHdrDecFuzzer fuzzHandle(data, size);
- fuzzHandle.process();
- return 0;
+ UltraHdrDecFuzzer fuzzHandle(data, size);
+ fuzzHandle.process();
+ return 0;
}
diff --git a/fuzzer/ultrahdr_enc_fuzzer.cpp b/fuzzer/ultrahdr_enc_fuzzer.cpp
index d981934..db6021e 100644
--- a/fuzzer/ultrahdr_enc_fuzzer.cpp
+++ b/fuzzer/ultrahdr_enc_fuzzer.cpp
@@ -20,9 +20,9 @@
#include <memory>
#include <random>
-#include "ultrahdr/ultrahdrcommon.h"
-#include "ultrahdr/gainmapmath.h"
-#include "ultrahdr/jpegr.h"
+#include "ultrahdrcommon.h"
+#include "gainmapmath.h"
+#include "jpegr.h"
using namespace ultrahdr;
@@ -43,287 +43,281 @@ const int kQfMin = 0;
const int kQfMax = 100;
class UltraHdrEncFuzzer {
-public:
- UltraHdrEncFuzzer(const uint8_t* data, size_t size) : mFdp(data, size){};
- void process();
- void fillP010Buffer(uint16_t* data, int width, int height, int stride);
- void fill420Buffer(uint8_t* data, int width, int height, int stride);
+ public:
+ UltraHdrEncFuzzer(const uint8_t* data, size_t size) : mFdp(data, size){};
+ void process();
+ void fillP010Buffer(uint16_t* data, int width, int height, int stride);
+ void fill420Buffer(uint8_t* data, int width, int height, int stride);
-private:
- FuzzedDataProvider mFdp;
+ private:
+ FuzzedDataProvider mFdp;
};
void UltraHdrEncFuzzer::fillP010Buffer(uint16_t* data, int width, int height, int stride) {
- uint16_t* tmp = data;
- std::vector<uint16_t> buffer(16);
- for (int i = 0; i < buffer.size(); i++) {
- buffer[i] = (mFdp.ConsumeIntegralInRange<int>(0, (1 << 10) - 1)) << 6;
- }
- for (int j = 0; j < height; j++) {
- for (int i = 0; i < width; i += buffer.size()) {
- memcpy(tmp + i, buffer.data(),
- std::min((int)buffer.size(), (width - i)) * sizeof(*data));
- std::shuffle(buffer.begin(), buffer.end(),
- std::default_random_engine(std::random_device{}()));
- }
- tmp += stride;
+ uint16_t* tmp = data;
+ std::vector<uint16_t> buffer(16);
+ for (int i = 0; i < buffer.size(); i++) {
+ buffer[i] = (mFdp.ConsumeIntegralInRange<int>(0, (1 << 10) - 1)) << 6;
+ }
+ for (int j = 0; j < height; j++) {
+ for (int i = 0; i < width; i += buffer.size()) {
+ memcpy(tmp + i, buffer.data(), std::min((int)buffer.size(), (width - i)) * sizeof(*data));
+ std::shuffle(buffer.begin(), buffer.end(),
+ std::default_random_engine(std::random_device{}()));
}
+ tmp += stride;
+ }
}
void UltraHdrEncFuzzer::fill420Buffer(uint8_t* data, int width, int height, int stride) {
- uint8_t* tmp = data;
- std::vector<uint8_t> buffer(16);
- mFdp.ConsumeData(buffer.data(), buffer.size());
- for (int j = 0; j < height; j++) {
- for (int i = 0; i < width; i += buffer.size()) {
- memcpy(tmp + i, buffer.data(),
- std::min((int)buffer.size(), (width - i)) * sizeof(*data));
- std::shuffle(buffer.begin(), buffer.end(),
- std::default_random_engine(std::random_device{}()));
- }
- tmp += stride;
+ uint8_t* tmp = data;
+ std::vector<uint8_t> buffer(16);
+ mFdp.ConsumeData(buffer.data(), buffer.size());
+ for (int j = 0; j < height; j++) {
+ for (int i = 0; i < width; i += buffer.size()) {
+ memcpy(tmp + i, buffer.data(), std::min((int)buffer.size(), (width - i)) * sizeof(*data));
+ std::shuffle(buffer.begin(), buffer.end(),
+ std::default_random_engine(std::random_device{}()));
}
+ tmp += stride;
+ }
}
void UltraHdrEncFuzzer::process() {
- while (mFdp.remaining_bytes()) {
- struct jpegr_uncompressed_struct p010Img {};
- struct jpegr_uncompressed_struct yuv420Img {};
- struct jpegr_uncompressed_struct grayImg {};
- struct jpegr_compressed_struct jpegImgR {};
- struct jpegr_compressed_struct jpegImg {};
- struct jpegr_compressed_struct jpegGainMap {};
+ while (mFdp.remaining_bytes()) {
+ struct jpegr_uncompressed_struct p010Img {};
+ struct jpegr_uncompressed_struct yuv420Img {};
+ struct jpegr_uncompressed_struct grayImg {};
+ struct jpegr_compressed_struct jpegImgR {};
+ struct jpegr_compressed_struct jpegImg {};
+ struct jpegr_compressed_struct jpegGainMap {};
- // which encode api to select
- int muxSwitch = mFdp.ConsumeIntegralInRange<int>(0, 4);
+ // which encode api to select
+ int muxSwitch = mFdp.ConsumeIntegralInRange<int>(0, 4);
- // quality factor
- int quality = mFdp.ConsumeIntegralInRange<int>(kQfMin, kQfMax);
+ // quality factor
+ int quality = mFdp.ConsumeIntegralInRange<int>(kQfMin, kQfMax);
- // hdr_tf
- auto tf = static_cast<ultrahdr_transfer_function>(
- mFdp.ConsumeIntegralInRange<int>(kTfMin, kTfMax));
+ // hdr_tf
+ auto tf =
+ static_cast<ultrahdr_transfer_function>(mFdp.ConsumeIntegralInRange<int>(kTfMin, kTfMax));
- // p010 Cg
- auto p010Cg =
- static_cast<ultrahdr_color_gamut>(mFdp.ConsumeIntegralInRange<int>(kCgMin, kCgMax));
+ // p010 Cg
+ auto p010Cg =
+ static_cast<ultrahdr_color_gamut>(mFdp.ConsumeIntegralInRange<int>(kCgMin, kCgMax));
- // 420 Cg
- auto yuv420Cg =
- static_cast<ultrahdr_color_gamut>(mFdp.ConsumeIntegralInRange<int>(kCgMin, kCgMax));
+ // 420 Cg
+ auto yuv420Cg =
+ static_cast<ultrahdr_color_gamut>(mFdp.ConsumeIntegralInRange<int>(kCgMin, kCgMax));
- // hdr_of
- auto of = static_cast<ultrahdr_output_format>(
- mFdp.ConsumeIntegralInRange<int>(kOfMin, kOfMax));
+ // hdr_of
+ auto of = static_cast<ultrahdr_output_format>(mFdp.ConsumeIntegralInRange<int>(kOfMin, kOfMax));
- int width = mFdp.ConsumeIntegralInRange<int>(kMinWidth, kMaxWidth);
- width = (width >> 1) << 1;
+ int width = mFdp.ConsumeIntegralInRange<int>(kMinWidth, kMaxWidth);
+ width = (width >> 1) << 1;
- int height = mFdp.ConsumeIntegralInRange<int>(kMinHeight, kMaxHeight);
- height = (height >> 1) << 1;
+ int height = mFdp.ConsumeIntegralInRange<int>(kMinHeight, kMaxHeight);
+ height = (height >> 1) << 1;
- std::unique_ptr<uint16_t[]> bufferYHdr = nullptr;
- std::unique_ptr<uint16_t[]> bufferUVHdr = nullptr;
- std::unique_ptr<uint8_t[]> bufferYSdr = nullptr;
- std::unique_ptr<uint8_t[]> bufferUVSdr = nullptr;
- std::unique_ptr<uint8_t[]> grayImgRaw = nullptr;
- if (muxSwitch != 4) {
- // init p010 image
- bool isUVContiguous = mFdp.ConsumeBool();
- bool hasYStride = mFdp.ConsumeBool();
- int yStride = hasYStride ? mFdp.ConsumeIntegralInRange<int>(width, width + 128) : width;
- p010Img.width = width;
- p010Img.height = height;
- p010Img.colorGamut = p010Cg;
- p010Img.luma_stride = hasYStride ? yStride : 0;
- if (isUVContiguous) {
- size_t p010Size = yStride * height * 3 / 2;
- bufferYHdr = std::make_unique<uint16_t[]>(p010Size);
- p010Img.data = bufferYHdr.get();
- p010Img.chroma_data = nullptr;
- p010Img.chroma_stride = 0;
- fillP010Buffer(bufferYHdr.get(), width, height, yStride);
- fillP010Buffer(bufferYHdr.get() + yStride * height, width, height / 2, yStride);
- } else {
- int uvStride = mFdp.ConsumeIntegralInRange<int>(width, width + 128);
- size_t p010YSize = yStride * height;
- bufferYHdr = std::make_unique<uint16_t[]>(p010YSize);
- p010Img.data = bufferYHdr.get();
- fillP010Buffer(bufferYHdr.get(), width, height, yStride);
- size_t p010UVSize = uvStride * p010Img.height / 2;
- bufferUVHdr = std::make_unique<uint16_t[]>(p010UVSize);
- p010Img.chroma_data = bufferUVHdr.get();
- p010Img.chroma_stride = uvStride;
- fillP010Buffer(bufferUVHdr.get(), width, height / 2, uvStride);
- }
- } else {
- size_t map_width = width / kMapDimensionScaleFactor;
- size_t map_height = height / kMapDimensionScaleFactor;
- // init 400 image
- grayImg.width = map_width;
- grayImg.height = map_height;
- grayImg.colorGamut = ULTRAHDR_COLORGAMUT_UNSPECIFIED;
+ std::unique_ptr<uint16_t[]> bufferYHdr = nullptr;
+ std::unique_ptr<uint16_t[]> bufferUVHdr = nullptr;
+ std::unique_ptr<uint8_t[]> bufferYSdr = nullptr;
+ std::unique_ptr<uint8_t[]> bufferUVSdr = nullptr;
+ std::unique_ptr<uint8_t[]> grayImgRaw = nullptr;
+ if (muxSwitch != 4) {
+ // init p010 image
+ bool isUVContiguous = mFdp.ConsumeBool();
+ bool hasYStride = mFdp.ConsumeBool();
+ int yStride = hasYStride ? mFdp.ConsumeIntegralInRange<int>(width, width + 128) : width;
+ p010Img.width = width;
+ p010Img.height = height;
+ p010Img.colorGamut = p010Cg;
+ p010Img.luma_stride = hasYStride ? yStride : 0;
+ if (isUVContiguous) {
+ size_t p010Size = yStride * height * 3 / 2;
+ bufferYHdr = std::make_unique<uint16_t[]>(p010Size);
+ p010Img.data = bufferYHdr.get();
+ p010Img.chroma_data = nullptr;
+ p010Img.chroma_stride = 0;
+ fillP010Buffer(bufferYHdr.get(), width, height, yStride);
+ fillP010Buffer(bufferYHdr.get() + yStride * height, width, height / 2, yStride);
+ } else {
+ int uvStride = mFdp.ConsumeIntegralInRange<int>(width, width + 128);
+ size_t p010YSize = yStride * height;
+ bufferYHdr = std::make_unique<uint16_t[]>(p010YSize);
+ p010Img.data = bufferYHdr.get();
+ fillP010Buffer(bufferYHdr.get(), width, height, yStride);
+ size_t p010UVSize = uvStride * p010Img.height / 2;
+ bufferUVHdr = std::make_unique<uint16_t[]>(p010UVSize);
+ p010Img.chroma_data = bufferUVHdr.get();
+ p010Img.chroma_stride = uvStride;
+ fillP010Buffer(bufferUVHdr.get(), width, height / 2, uvStride);
+ }
+ } else {
+ size_t map_width = width / kMapDimensionScaleFactor;
+ size_t map_height = height / kMapDimensionScaleFactor;
+ // init 400 image
+ grayImg.width = map_width;
+ grayImg.height = map_height;
+ grayImg.colorGamut = ULTRAHDR_COLORGAMUT_UNSPECIFIED;
- const size_t graySize = map_width * map_height;
- grayImgRaw = std::make_unique<uint8_t[]>(graySize);
- grayImg.data = grayImgRaw.get();
- fill420Buffer(grayImgRaw.get(), map_width, map_height, map_width);
- grayImg.chroma_data = nullptr;
- grayImg.luma_stride = 0;
- grayImg.chroma_stride = 0;
- }
+ const size_t graySize = map_width * map_height;
+ grayImgRaw = std::make_unique<uint8_t[]>(graySize);
+ grayImg.data = grayImgRaw.get();
+ fill420Buffer(grayImgRaw.get(), map_width, map_height, map_width);
+ grayImg.chroma_data = nullptr;
+ grayImg.luma_stride = 0;
+ grayImg.chroma_stride = 0;
+ }
- if (muxSwitch > 0) {
- // init 420 image
- bool isUVContiguous = mFdp.ConsumeBool();
- bool hasYStride = mFdp.ConsumeBool();
- int yStride = hasYStride ? mFdp.ConsumeIntegralInRange<int>(width, width + 128) : width;
- yuv420Img.width = width;
- yuv420Img.height = height;
- yuv420Img.colorGamut = yuv420Cg;
- yuv420Img.luma_stride = hasYStride ? yStride : 0;
- if (isUVContiguous) {
- size_t yuv420Size = yStride * height * 3 / 2;
- bufferYSdr = std::make_unique<uint8_t[]>(yuv420Size);
- yuv420Img.data = bufferYSdr.get();
- yuv420Img.chroma_data = nullptr;
- yuv420Img.chroma_stride = 0;
- fill420Buffer(bufferYSdr.get(), width, height, yStride);
- fill420Buffer(bufferYSdr.get() + yStride * height, width / 2, height / 2,
- yStride / 2);
- fill420Buffer(bufferYSdr.get() + yStride * height * 5 / 4, width / 2, height / 2,
- yStride / 2);
- } else {
- int uvStride = mFdp.ConsumeIntegralInRange<int>(width / 2, width / 2 + 128);
- size_t yuv420YSize = yStride * height;
- bufferYSdr = std::make_unique<uint8_t[]>(yuv420YSize);
- yuv420Img.data = bufferYSdr.get();
- fill420Buffer(bufferYSdr.get(), width, height, yStride);
- size_t yuv420UVSize = uvStride * yuv420Img.height / 2 * 2;
- bufferUVSdr = std::make_unique<uint8_t[]>(yuv420UVSize);
- yuv420Img.chroma_data = bufferUVSdr.get();
- yuv420Img.chroma_stride = uvStride;
- fill420Buffer(bufferUVSdr.get(), width / 2, height / 2, uvStride);
- fill420Buffer(bufferUVSdr.get() + uvStride * height / 2, width / 2, height / 2,
- uvStride);
- }
- }
+ if (muxSwitch > 0) {
+ // init 420 image
+ bool isUVContiguous = mFdp.ConsumeBool();
+ bool hasYStride = mFdp.ConsumeBool();
+ int yStride = hasYStride ? mFdp.ConsumeIntegralInRange<int>(width, width + 128) : width;
+ yuv420Img.width = width;
+ yuv420Img.height = height;
+ yuv420Img.colorGamut = yuv420Cg;
+ yuv420Img.luma_stride = hasYStride ? yStride : 0;
+ if (isUVContiguous) {
+ size_t yuv420Size = yStride * height * 3 / 2;
+ bufferYSdr = std::make_unique<uint8_t[]>(yuv420Size);
+ yuv420Img.data = bufferYSdr.get();
+ yuv420Img.chroma_data = nullptr;
+ yuv420Img.chroma_stride = 0;
+ fill420Buffer(bufferYSdr.get(), width, height, yStride);
+ fill420Buffer(bufferYSdr.get() + yStride * height, width / 2, height / 2, yStride / 2);
+ fill420Buffer(bufferYSdr.get() + yStride * height * 5 / 4, width / 2, height / 2,
+ yStride / 2);
+ } else {
+ int uvStride = mFdp.ConsumeIntegralInRange<int>(width / 2, width / 2 + 128);
+ size_t yuv420YSize = yStride * height;
+ bufferYSdr = std::make_unique<uint8_t[]>(yuv420YSize);
+ yuv420Img.data = bufferYSdr.get();
+ fill420Buffer(bufferYSdr.get(), width, height, yStride);
+ size_t yuv420UVSize = uvStride * yuv420Img.height / 2 * 2;
+ bufferUVSdr = std::make_unique<uint8_t[]>(yuv420UVSize);
+ yuv420Img.chroma_data = bufferUVSdr.get();
+ yuv420Img.chroma_stride = uvStride;
+ fill420Buffer(bufferUVSdr.get(), width / 2, height / 2, uvStride);
+ fill420Buffer(bufferUVSdr.get() + uvStride * height / 2, width / 2, height / 2, uvStride);
+ }
+ }
- // dest
- // 2 * p010 size as input data is random, DCT compression might not behave as expected
- jpegImgR.maxLength = std::max(8 * 1024 /* min size 8kb */, width * height * 3 * 2);
- auto jpegImgRaw = std::make_unique<uint8_t[]>(jpegImgR.maxLength);
- jpegImgR.data = jpegImgRaw.get();
+ // dest
+ // 2 * p010 size as input data is random, DCT compression might not behave as expected
+ jpegImgR.maxLength = std::max(8 * 1024 /* min size 8kb */, width * height * 3 * 2);
+ auto jpegImgRaw = std::make_unique<uint8_t[]>(jpegImgR.maxLength);
+ jpegImgR.data = jpegImgRaw.get();
//#define DUMP_PARAM
#ifdef DUMP_PARAM
- std::cout << "Api Select " << muxSwitch << std::endl;
- std::cout << "image dimensions " << width << " x " << height << std::endl;
- std::cout << "p010 color gamut " << p010Img.colorGamut << std::endl;
- std::cout << "p010 luma stride " << p010Img.luma_stride << std::endl;
- std::cout << "p010 chroma stride " << p010Img.chroma_stride << std::endl;
- std::cout << "420 color gamut " << yuv420Img.colorGamut << std::endl;
- std::cout << "420 luma stride " << yuv420Img.luma_stride << std::endl;
- std::cout << "420 chroma stride " << yuv420Img.chroma_stride << std::endl;
- std::cout << "quality factor " << quality << std::endl;
+ std::cout << "Api Select " << muxSwitch << std::endl;
+ std::cout << "image dimensions " << width << " x " << height << std::endl;
+ std::cout << "p010 color gamut " << p010Img.colorGamut << std::endl;
+ std::cout << "p010 luma stride " << p010Img.luma_stride << std::endl;
+ std::cout << "p010 chroma stride " << p010Img.chroma_stride << std::endl;
+ std::cout << "420 color gamut " << yuv420Img.colorGamut << std::endl;
+ std::cout << "420 luma stride " << yuv420Img.luma_stride << std::endl;
+ std::cout << "420 chroma stride " << yuv420Img.chroma_stride << std::endl;
+ std::cout << "quality factor " << quality << std::endl;
#endif
- JpegR jpegHdr;
- status_t status = UNKNOWN_ERROR;
- if (muxSwitch == 0) { // api 0
- jpegImgR.length = 0;
- status = jpegHdr.encodeJPEGR(&p010Img, tf, &jpegImgR, quality, nullptr);
- } else if (muxSwitch == 1) { // api 1
- jpegImgR.length = 0;
- status = jpegHdr.encodeJPEGR(&p010Img, &yuv420Img, tf, &jpegImgR, quality, nullptr);
- } else {
- // compressed img
- JpegEncoderHelper encoder;
- struct jpegr_uncompressed_struct yuv420ImgCopy = yuv420Img;
- if (yuv420ImgCopy.luma_stride == 0) yuv420ImgCopy.luma_stride = yuv420Img.width;
- if (!yuv420ImgCopy.chroma_data) {
- uint8_t* data = reinterpret_cast<uint8_t*>(yuv420Img.data);
- yuv420ImgCopy.chroma_data = data + yuv420Img.luma_stride * yuv420Img.height;
- yuv420ImgCopy.chroma_stride = yuv420Img.luma_stride >> 1;
- }
+ JpegR jpegHdr;
+ status_t status = JPEGR_UNKNOWN_ERROR;
+ if (muxSwitch == 0) { // api 0
+ jpegImgR.length = 0;
+ status = jpegHdr.encodeJPEGR(&p010Img, tf, &jpegImgR, quality, nullptr);
+ } else if (muxSwitch == 1) { // api 1
+ jpegImgR.length = 0;
+ status = jpegHdr.encodeJPEGR(&p010Img, &yuv420Img, tf, &jpegImgR, quality, nullptr);
+ } else {
+ // compressed img
+ JpegEncoderHelper encoder;
+ struct jpegr_uncompressed_struct yuv420ImgCopy = yuv420Img;
+ if (yuv420ImgCopy.luma_stride == 0) yuv420ImgCopy.luma_stride = yuv420Img.width;
+ if (!yuv420ImgCopy.chroma_data) {
+ uint8_t* data = reinterpret_cast<uint8_t*>(yuv420Img.data);
+ yuv420ImgCopy.chroma_data = data + yuv420Img.luma_stride * yuv420Img.height;
+ yuv420ImgCopy.chroma_stride = yuv420Img.luma_stride >> 1;
+ }
- if (encoder.compressImage(reinterpret_cast<uint8_t*>(yuv420ImgCopy.data),
- reinterpret_cast<uint8_t*>(yuv420ImgCopy.chroma_data),
- yuv420ImgCopy.width, yuv420ImgCopy.height,
- yuv420ImgCopy.luma_stride, yuv420ImgCopy.chroma_stride,
- quality, nullptr, 0)) {
- jpegImg.length = encoder.getCompressedImageSize();
- jpegImg.maxLength = jpegImg.length;
- jpegImg.data = encoder.getCompressedImagePtr();
- jpegImg.colorGamut = yuv420Cg;
+ if (encoder.compressImage(reinterpret_cast<uint8_t*>(yuv420ImgCopy.data),
+ reinterpret_cast<uint8_t*>(yuv420ImgCopy.chroma_data),
+ yuv420ImgCopy.width, yuv420ImgCopy.height,
+ yuv420ImgCopy.luma_stride, yuv420ImgCopy.chroma_stride, quality,
+ nullptr, 0)) {
+ jpegImg.length = encoder.getCompressedImageSize();
+ jpegImg.maxLength = jpegImg.length;
+ jpegImg.data = encoder.getCompressedImagePtr();
+ jpegImg.colorGamut = yuv420Cg;
- if (muxSwitch == 2) { // api 2
- jpegImgR.length = 0;
- status = jpegHdr.encodeJPEGR(&p010Img, &yuv420Img, &jpegImg, tf, &jpegImgR);
- } else if (muxSwitch == 3) { // api 3
- jpegImgR.length = 0;
- status = jpegHdr.encodeJPEGR(&p010Img, &jpegImg, tf, &jpegImgR);
- } else if (muxSwitch == 4) { // api 4
- jpegImgR.length = 0;
- JpegEncoderHelper gainMapEncoder;
- if (gainMapEncoder.compressImage(reinterpret_cast<uint8_t*>(grayImg.data),
- nullptr, grayImg.width, grayImg.height,
- grayImg.width, 0, quality, nullptr, 0)) {
- jpegGainMap.length = gainMapEncoder.getCompressedImageSize();
- jpegGainMap.maxLength = jpegImg.length;
- jpegGainMap.data = gainMapEncoder.getCompressedImagePtr();
- jpegGainMap.colorGamut = ULTRAHDR_COLORGAMUT_UNSPECIFIED;
- ultrahdr_metadata_struct metadata;
- metadata.version = kJpegrVersion;
- if (tf == ULTRAHDR_TF_HLG) {
- metadata.maxContentBoost = kHlgMaxNits / kSdrWhiteNits;
- } else if (tf == ULTRAHDR_TF_PQ) {
- metadata.maxContentBoost = kPqMaxNits / kSdrWhiteNits;
- } else {
- metadata.maxContentBoost = 1.0f;
- }
- metadata.minContentBoost = 1.0f;
- metadata.gamma = 1.0f;
- metadata.offsetSdr = 0.0f;
- metadata.offsetHdr = 0.0f;
- metadata.hdrCapacityMin = 1.0f;
- metadata.hdrCapacityMax = metadata.maxContentBoost;
- status = jpegHdr.encodeJPEGR(&jpegImg, &jpegGainMap, &metadata, &jpegImgR);
- }
- }
- }
- }
- if (status == OK) {
- std::vector<uint8_t> iccData(0);
- std::vector<uint8_t> exifData(0);
- jpegr_info_struct info{0, 0, &iccData, &exifData};
- status = jpegHdr.getJPEGRInfo(&jpegImgR, &info);
- if (status == OK) {
- size_t outSize =
- info.width * info.height * ((of == ULTRAHDR_OUTPUT_HDR_LINEAR) ? 8 : 4);
- jpegr_uncompressed_struct decodedJpegR;
- auto decodedRaw = std::make_unique<uint8_t[]>(outSize);
- decodedJpegR.data = decodedRaw.get();
- ultrahdr_metadata_struct metadata;
- jpegr_uncompressed_struct decodedGainMap{};
- status = jpegHdr.decodeJPEGR(&jpegImgR, &decodedJpegR,
- mFdp.ConsumeFloatingPointInRange<float>(1.0, FLT_MAX),
- nullptr, of, &decodedGainMap, &metadata);
- if (status != OK) {
- ALOGE("encountered error during decoding %d", status);
- }
- if (decodedGainMap.data) free(decodedGainMap.data);
+ if (muxSwitch == 2) { // api 2
+ jpegImgR.length = 0;
+ status = jpegHdr.encodeJPEGR(&p010Img, &yuv420Img, &jpegImg, tf, &jpegImgR);
+ } else if (muxSwitch == 3) { // api 3
+ jpegImgR.length = 0;
+ status = jpegHdr.encodeJPEGR(&p010Img, &jpegImg, tf, &jpegImgR);
+ } else if (muxSwitch == 4) { // api 4
+ jpegImgR.length = 0;
+ JpegEncoderHelper gainMapEncoder;
+ if (gainMapEncoder.compressImage(reinterpret_cast<uint8_t*>(grayImg.data), nullptr,
+ grayImg.width, grayImg.height, grayImg.width, 0, quality,
+ nullptr, 0)) {
+ jpegGainMap.length = gainMapEncoder.getCompressedImageSize();
+ jpegGainMap.maxLength = jpegImg.length;
+ jpegGainMap.data = gainMapEncoder.getCompressedImagePtr();
+ jpegGainMap.colorGamut = ULTRAHDR_COLORGAMUT_UNSPECIFIED;
+ ultrahdr_metadata_struct metadata;
+ metadata.version = kJpegrVersion;
+ if (tf == ULTRAHDR_TF_HLG) {
+ metadata.maxContentBoost = kHlgMaxNits / kSdrWhiteNits;
+ } else if (tf == ULTRAHDR_TF_PQ) {
+ metadata.maxContentBoost = kPqMaxNits / kSdrWhiteNits;
} else {
- ALOGE("encountered error during get jpeg info %d", status);
+ metadata.maxContentBoost = 1.0f;
}
- } else {
- ALOGE("encountered error during encoding %d", status);
+ metadata.minContentBoost = 1.0f;
+ metadata.gamma = 1.0f;
+ metadata.offsetSdr = 0.0f;
+ metadata.offsetHdr = 0.0f;
+ metadata.hdrCapacityMin = 1.0f;
+ metadata.hdrCapacityMax = metadata.maxContentBoost;
+ status = jpegHdr.encodeJPEGR(&jpegImg, &jpegGainMap, &metadata, &jpegImgR);
+ }
+ }
+ }
+ }
+ if (status == JPEGR_NO_ERROR) {
+ std::vector<uint8_t> iccData(0);
+ std::vector<uint8_t> exifData(0);
+ jpegr_info_struct info{0, 0, &iccData, &exifData};
+ status = jpegHdr.getJPEGRInfo(&jpegImgR, &info);
+ if (status == JPEGR_NO_ERROR) {
+ size_t outSize = info.width * info.height * ((of == ULTRAHDR_OUTPUT_HDR_LINEAR) ? 8 : 4);
+ jpegr_uncompressed_struct decodedJpegR;
+ auto decodedRaw = std::make_unique<uint8_t[]>(outSize);
+ decodedJpegR.data = decodedRaw.get();
+ ultrahdr_metadata_struct metadata;
+ jpegr_uncompressed_struct decodedGainMap{};
+ status = jpegHdr.decodeJPEGR(&jpegImgR, &decodedJpegR,
+ mFdp.ConsumeFloatingPointInRange<float>(1.0, FLT_MAX), nullptr,
+ of, &decodedGainMap, &metadata);
+ if (status != JPEGR_NO_ERROR) {
+ ALOGE("encountered error during decoding %d", status);
}
+ if (decodedGainMap.data) free(decodedGainMap.data);
+ } else {
+ ALOGE("encountered error during get jpeg info %d", status);
+ }
+ } else {
+ ALOGE("encountered error during encoding %d", status);
}
+ }
}
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
- UltraHdrEncFuzzer fuzzHandle(data, size);
- fuzzHandle.process();
- return 0;
+ UltraHdrEncFuzzer fuzzHandle(data, size);
+ fuzzHandle.process();
+ return 0;
}
diff --git a/icc.cpp b/icc.cpp
deleted file mode 100644
index 6de711f..0000000
--- a/icc.cpp
+++ /dev/null
@@ -1,688 +0,0 @@
-/*
- * Copyright 2022 The Android Open Source Project
- *
- * 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.
- */
-
-#include <cstring>
-
-#include "ultrahdr/ultrahdrcommon.h"
-#include "ultrahdr/icc.h"
-
-namespace ultrahdr {
-
-static void Matrix3x3_apply(const Matrix3x3* m, float* x) {
- float y0 = x[0] * m->vals[0][0] + x[1] * m->vals[0][1] + x[2] * m->vals[0][2];
- float y1 = x[0] * m->vals[1][0] + x[1] * m->vals[1][1] + x[2] * m->vals[1][2];
- float y2 = x[0] * m->vals[2][0] + x[1] * m->vals[2][1] + x[2] * m->vals[2][2];
- x[0] = y0;
- x[1] = y1;
- x[2] = y2;
-}
-
-bool Matrix3x3_invert(const Matrix3x3* src, Matrix3x3* dst) {
- double a00 = src->vals[0][0],
- a01 = src->vals[1][0],
- a02 = src->vals[2][0],
- a10 = src->vals[0][1],
- a11 = src->vals[1][1],
- a12 = src->vals[2][1],
- a20 = src->vals[0][2],
- a21 = src->vals[1][2],
- a22 = src->vals[2][2];
-
- double b0 = a00*a11 - a01*a10,
- b1 = a00*a12 - a02*a10,
- b2 = a01*a12 - a02*a11,
- b3 = a20,
- b4 = a21,
- b5 = a22;
-
- double determinant = b0*b5
- - b1*b4
- + b2*b3;
-
- if (determinant == 0) {
- return false;
- }
-
- double invdet = 1.0 / determinant;
- if (invdet > +FLT_MAX || invdet < -FLT_MAX || !isfinitef_((float)invdet)) {
- return false;
- }
-
- b0 *= invdet;
- b1 *= invdet;
- b2 *= invdet;
- b3 *= invdet;
- b4 *= invdet;
- b5 *= invdet;
-
- dst->vals[0][0] = (float)( a11*b5 - a12*b4 );
- dst->vals[1][0] = (float)( a02*b4 - a01*b5 );
- dst->vals[2][0] = (float)( + b2 );
- dst->vals[0][1] = (float)( a12*b3 - a10*b5 );
- dst->vals[1][1] = (float)( a00*b5 - a02*b3 );
- dst->vals[2][1] = (float)( - b1 );
- dst->vals[0][2] = (float)( a10*b4 - a11*b3 );
- dst->vals[1][2] = (float)( a01*b3 - a00*b4 );
- dst->vals[2][2] = (float)( + b0 );
-
- for (int r = 0; r < 3; ++r)
- for (int c = 0; c < 3; ++c) {
- if (!isfinitef_(dst->vals[r][c])) {
- return false;
- }
- }
- return true;
-}
-
-static Matrix3x3 Matrix3x3_concat(const Matrix3x3* A, const Matrix3x3* B) {
- Matrix3x3 m = { { { 0,0,0 },{ 0,0,0 },{ 0,0,0 } } };
- for (int r = 0; r < 3; r++)
- for (int c = 0; c < 3; c++) {
- m.vals[r][c] = A->vals[r][0] * B->vals[0][c]
- + A->vals[r][1] * B->vals[1][c]
- + A->vals[r][2] * B->vals[2][c];
- }
- return m;
-}
-
-static void float_XYZD50_to_grid16_lab(const float* xyz_float, uint8_t* grid16_lab) {
- float v[3] = {
- xyz_float[0] / kD50_x,
- xyz_float[1] / kD50_y,
- xyz_float[2] / kD50_z,
- };
- for (size_t i = 0; i < 3; ++i) {
- v[i] = v[i] > 0.008856f ? cbrtf(v[i]) : v[i] * 7.787f + (16 / 116.0f);
- }
- const float L = v[1] * 116.0f - 16.0f;
- const float a = (v[0] - v[1]) * 500.0f;
- const float b = (v[1] - v[2]) * 200.0f;
- const float Lab_unorm[3] = {
- L * (1 / 100.f),
- (a + 128.0f) * (1 / 255.0f),
- (b + 128.0f) * (1 / 255.0f),
- };
- // This will encode L=1 as 0xFFFF. This matches how skcms will interpret the
- // table, but the spec appears to indicate that the value should be 0xFF00.
- // https://crbug.com/skia/13807
- for (size_t i = 0; i < 3; ++i) {
- reinterpret_cast<uint16_t*>(grid16_lab)[i] =
- Endian_SwapBE16(float_round_to_unorm16(Lab_unorm[i]));
- }
-}
-
-std::string IccHelper::get_desc_string(const ultrahdr_transfer_function tf,
- const ultrahdr_color_gamut gamut) {
- std::string result;
- switch (gamut) {
- case ULTRAHDR_COLORGAMUT_BT709:
- result += "sRGB";
- break;
- case ULTRAHDR_COLORGAMUT_P3:
- result += "Display P3";
- break;
- case ULTRAHDR_COLORGAMUT_BT2100:
- result += "Rec2020";
- break;
- default:
- result += "Unknown";
- break;
- }
- result += " Gamut with ";
- switch (tf) {
- case ULTRAHDR_TF_SRGB:
- result += "sRGB";
- break;
- case ULTRAHDR_TF_LINEAR:
- result += "Linear";
- break;
- case ULTRAHDR_TF_PQ:
- result += "PQ";
- break;
- case ULTRAHDR_TF_HLG:
- result += "HLG";
- break;
- default:
- result += "Unknown";
- break;
- }
- result += " Transfer";
- return result;
-}
-
-std::shared_ptr<DataStruct> IccHelper::write_text_tag(const char* text) {
- uint32_t text_length = strlen(text);
- uint32_t header[] = {
- Endian_SwapBE32(kTAG_TextType), // Type signature
- 0, // Reserved
- Endian_SwapBE32(1), // Number of records
- Endian_SwapBE32(12), // Record size (must be 12)
- Endian_SwapBE32(SetFourByteTag('e', 'n', 'U', 'S')), // English USA
- Endian_SwapBE32(2 * text_length), // Length of string in bytes
- Endian_SwapBE32(28), // Offset of string
- };
-
- uint32_t total_length = text_length * 2 + sizeof(header);
- total_length = (((total_length + 2) >> 2) << 2); // 4 aligned
- std::shared_ptr<DataStruct> dataStruct = std::make_shared<DataStruct>(total_length);
-
- if (!dataStruct->write(header, sizeof(header))) {
- ALOGE("write_text_tag(): error in writing data");
- return dataStruct;
- }
-
- for (size_t i = 0; i < text_length; i++) {
- // Convert ASCII to big-endian UTF-16.
- dataStruct->write8(0);
- dataStruct->write8(text[i]);
- }
-
- return dataStruct;
-}
-
-std::shared_ptr<DataStruct> IccHelper::write_xyz_tag(float x, float y, float z) {
- uint32_t data[] = {
- Endian_SwapBE32(kXYZ_PCSSpace),
- 0,
- static_cast<uint32_t>(Endian_SwapBE32(float_round_to_fixed(x))),
- static_cast<uint32_t>(Endian_SwapBE32(float_round_to_fixed(y))),
- static_cast<uint32_t>(Endian_SwapBE32(float_round_to_fixed(z))),
- };
- std::shared_ptr<DataStruct> dataStruct = std::make_shared<DataStruct>(sizeof(data));
- dataStruct->write(&data, sizeof(data));
- return dataStruct;
-}
-
-std::shared_ptr<DataStruct> IccHelper::write_trc_tag(const int table_entries,
- const void* table_16) {
- int total_length = 4 + 4 + 4 + table_entries * 2;
- total_length = (((total_length + 2) >> 2) << 2); // 4 aligned
- std::shared_ptr<DataStruct> dataStruct = std::make_shared<DataStruct>(total_length);
- dataStruct->write32(Endian_SwapBE32(kTAG_CurveType)); // Type
- dataStruct->write32(0); // Reserved
- dataStruct->write32(Endian_SwapBE32(table_entries)); // Value count
- for (int i = 0; i < table_entries; ++i) {
- uint16_t value = reinterpret_cast<const uint16_t*>(table_16)[i];
- dataStruct->write16(value);
- }
- return dataStruct;
-}
-
-std::shared_ptr<DataStruct> IccHelper::write_trc_tag(const TransferFunction& fn) {
- if (fn.a == 1.f && fn.b == 0.f && fn.c == 0.f
- && fn.d == 0.f && fn.e == 0.f && fn.f == 0.f) {
- int total_length = 16;
- std::shared_ptr<DataStruct> dataStruct = std::make_shared<DataStruct>(total_length);
- dataStruct->write32(Endian_SwapBE32(kTAG_ParaCurveType)); // Type
- dataStruct->write32(0); // Reserved
- dataStruct->write32(Endian_SwapBE16(kExponential_ParaCurveType));
- dataStruct->write32(Endian_SwapBE32(float_round_to_fixed(fn.g)));
- return dataStruct;
- }
-
- int total_length = 40;
- std::shared_ptr<DataStruct> dataStruct = std::make_shared<DataStruct>(total_length);
- dataStruct->write32(Endian_SwapBE32(kTAG_ParaCurveType)); // Type
- dataStruct->write32(0); // Reserved
- dataStruct->write32(Endian_SwapBE16(kGABCDEF_ParaCurveType));
- dataStruct->write32(Endian_SwapBE32(float_round_to_fixed(fn.g)));
- dataStruct->write32(Endian_SwapBE32(float_round_to_fixed(fn.a)));
- dataStruct->write32(Endian_SwapBE32(float_round_to_fixed(fn.b)));
- dataStruct->write32(Endian_SwapBE32(float_round_to_fixed(fn.c)));
- dataStruct->write32(Endian_SwapBE32(float_round_to_fixed(fn.d)));
- dataStruct->write32(Endian_SwapBE32(float_round_to_fixed(fn.e)));
- dataStruct->write32(Endian_SwapBE32(float_round_to_fixed(fn.f)));
- return dataStruct;
-}
-
-float IccHelper::compute_tone_map_gain(const ultrahdr_transfer_function tf, float L) {
- if (L <= 0.f) {
- return 1.f;
- }
- if (tf == ULTRAHDR_TF_PQ) {
- // The PQ transfer function will map to the range [0, 1]. Linearly scale
- // it up to the range [0, 10,000/203]. We will then tone map that back
- // down to [0, 1].
- constexpr float kInputMaxLuminance = 10000 / 203.f;
- constexpr float kOutputMaxLuminance = 1.0;
- L *= kInputMaxLuminance;
-
- // Compute the tone map gain which will tone map from 10,000/203 to 1.0.
- constexpr float kToneMapA = kOutputMaxLuminance / (kInputMaxLuminance * kInputMaxLuminance);
- constexpr float kToneMapB = 1.f / kOutputMaxLuminance;
- return kInputMaxLuminance * (1.f + kToneMapA * L) / (1.f + kToneMapB * L);
- }
- if (tf == ULTRAHDR_TF_HLG) {
- // Let Lw be the brightness of the display in nits.
- constexpr float Lw = 203.f;
- const float gamma = 1.2f + 0.42f * std::log(Lw / 1000.f) / std::log(10.f);
- return std::pow(L, gamma - 1.f);
- }
- return 1.f;
-}
-
-std::shared_ptr<DataStruct> IccHelper::write_cicp_tag(uint32_t color_primaries,
- uint32_t transfer_characteristics) {
- int total_length = 12; // 4 + 4 + 1 + 1 + 1 + 1
- std::shared_ptr<DataStruct> dataStruct = std::make_shared<DataStruct>(total_length);
- dataStruct->write32(Endian_SwapBE32(kTAG_cicp)); // Type signature
- dataStruct->write32(0); // Reserved
- dataStruct->write8(color_primaries); // Color primaries
- dataStruct->write8(transfer_characteristics); // Transfer characteristics
- dataStruct->write8(0); // RGB matrix
- dataStruct->write8(1); // Full range
- return dataStruct;
-}
-
-void IccHelper::compute_lut_entry(const Matrix3x3& src_to_XYZD50, float rgb[3]) {
- // Compute the matrices to convert from source to Rec2020, and from Rec2020 to XYZD50.
- Matrix3x3 src_to_rec2020;
- const Matrix3x3 rec2020_to_XYZD50 = kRec2020;
- {
- Matrix3x3 XYZD50_to_rec2020;
- Matrix3x3_invert(&rec2020_to_XYZD50, &XYZD50_to_rec2020);
- src_to_rec2020 = Matrix3x3_concat(&XYZD50_to_rec2020, &src_to_XYZD50);
- }
-
- // Convert the source signal to linear.
- for (size_t i = 0; i < kNumChannels; ++i) {
- rgb[i] = pqOetf(rgb[i]);
- }
-
- // Convert source gamut to Rec2020.
- Matrix3x3_apply(&src_to_rec2020, rgb);
-
- // Compute the luminance of the signal.
- float L = bt2100Luminance({{{rgb[0], rgb[1], rgb[2]}}});
-
- // Compute the tone map gain based on the luminance.
- float tone_map_gain = compute_tone_map_gain(ULTRAHDR_TF_PQ, L);
-
- // Apply the tone map gain.
- for (size_t i = 0; i < kNumChannels; ++i) {
- rgb[i] *= tone_map_gain;
- }
-
- // Convert from Rec2020-linear to XYZD50.
- Matrix3x3_apply(&rec2020_to_XYZD50, rgb);
-}
-
-std::shared_ptr<DataStruct> IccHelper::write_clut(const uint8_t* grid_points,
- const uint8_t* grid_16) {
- uint32_t value_count = kNumChannels;
- for (uint32_t i = 0; i < kNumChannels; ++i) {
- value_count *= grid_points[i];
- }
-
- int total_length = 20 + 2 * value_count;
- total_length = (((total_length + 2) >> 2) << 2); // 4 aligned
- std::shared_ptr<DataStruct> dataStruct = std::make_shared<DataStruct>(total_length);
-
- for (size_t i = 0; i < 16; ++i) {
- dataStruct->write8(i < kNumChannels ? grid_points[i] : 0); // Grid size
- }
- dataStruct->write8(2); // Grid byte width (always 16-bit)
- dataStruct->write8(0); // Reserved
- dataStruct->write8(0); // Reserved
- dataStruct->write8(0); // Reserved
-
- for (uint32_t i = 0; i < value_count; ++i) {
- uint16_t value = reinterpret_cast<const uint16_t*>(grid_16)[i];
- dataStruct->write16(value);
- }
-
- return dataStruct;
-}
-
-std::shared_ptr<DataStruct> IccHelper::write_mAB_or_mBA_tag(uint32_t type, bool has_a_curves,
- const uint8_t* grid_points,
- const uint8_t* grid_16) {
- const size_t b_curves_offset = 32;
- std::shared_ptr<DataStruct> b_curves_data[kNumChannels];
- std::shared_ptr<DataStruct> a_curves_data[kNumChannels];
- size_t clut_offset = 0;
- std::shared_ptr<DataStruct> clut;
- size_t a_curves_offset = 0;
-
- // The "B" curve is required.
- for (size_t i = 0; i < kNumChannels; ++i) {
- b_curves_data[i] = write_trc_tag(kLinear_TransFun);
- }
-
- // The "A" curve and CLUT are optional.
- if (has_a_curves) {
- clut_offset = b_curves_offset;
- for (size_t i = 0; i < kNumChannels; ++i) {
- clut_offset += b_curves_data[i]->getLength();
- }
- clut = write_clut(grid_points, grid_16);
-
- a_curves_offset = clut_offset + clut->getLength();
- for (size_t i = 0; i < kNumChannels; ++i) {
- a_curves_data[i] = write_trc_tag(kLinear_TransFun);
- }
- }
-
- int total_length = b_curves_offset;
- for (size_t i = 0; i < kNumChannels; ++i) {
- total_length += b_curves_data[i]->getLength();
- }
- if (has_a_curves) {
- total_length += clut->getLength();
- for (size_t i = 0; i < kNumChannels; ++i) {
- total_length += a_curves_data[i]->getLength();
- }
- }
- std::shared_ptr<DataStruct> dataStruct = std::make_shared<DataStruct>(total_length);
- dataStruct->write32(Endian_SwapBE32(type)); // Type signature
- dataStruct->write32(0); // Reserved
- dataStruct->write8(kNumChannels); // Input channels
- dataStruct->write8(kNumChannels); // Output channels
- dataStruct->write16(0); // Reserved
- dataStruct->write32(Endian_SwapBE32(b_curves_offset)); // B curve offset
- dataStruct->write32(Endian_SwapBE32(0)); // Matrix offset (ignored)
- dataStruct->write32(Endian_SwapBE32(0)); // M curve offset (ignored)
- dataStruct->write32(Endian_SwapBE32(clut_offset)); // CLUT offset
- dataStruct->write32(Endian_SwapBE32(a_curves_offset)); // A curve offset
- for (size_t i = 0; i < kNumChannels; ++i) {
- if (dataStruct->write(b_curves_data[i]->getData(), b_curves_data[i]->getLength())) {
- return dataStruct;
- }
- }
- if (has_a_curves) {
- dataStruct->write(clut->getData(), clut->getLength());
- for (size_t i = 0; i < kNumChannels; ++i) {
- dataStruct->write(a_curves_data[i]->getData(), a_curves_data[i]->getLength());
- }
- }
- return dataStruct;
-}
-
-std::shared_ptr<DataStruct> IccHelper::writeIccProfile(ultrahdr_transfer_function tf,
- ultrahdr_color_gamut gamut) {
- ICCHeader header;
-
- std::vector<std::pair<uint32_t, std::shared_ptr<DataStruct>>> tags;
-
- // Compute profile description tag
- std::string desc = get_desc_string(tf, gamut);
-
- tags.emplace_back(kTAG_desc, write_text_tag(desc.c_str()));
-
- Matrix3x3 toXYZD50;
- switch (gamut) {
- case ULTRAHDR_COLORGAMUT_BT709:
- toXYZD50 = kSRGB;
- break;
- case ULTRAHDR_COLORGAMUT_P3:
- toXYZD50 = kDisplayP3;
- break;
- case ULTRAHDR_COLORGAMUT_BT2100:
- toXYZD50 = kRec2020;
- break;
- default:
- // Should not fall here.
- return nullptr;
- }
-
- // Compute primaries.
- {
- tags.emplace_back(kTAG_rXYZ,
- write_xyz_tag(toXYZD50.vals[0][0], toXYZD50.vals[1][0], toXYZD50.vals[2][0]));
- tags.emplace_back(kTAG_gXYZ,
- write_xyz_tag(toXYZD50.vals[0][1], toXYZD50.vals[1][1], toXYZD50.vals[2][1]));
- tags.emplace_back(kTAG_bXYZ,
- write_xyz_tag(toXYZD50.vals[0][2], toXYZD50.vals[1][2], toXYZD50.vals[2][2]));
- }
-
- // Compute white point tag (must be D50)
- tags.emplace_back(kTAG_wtpt, write_xyz_tag(kD50_x, kD50_y, kD50_z));
-
- // Compute transfer curves.
- if (tf != ULTRAHDR_TF_PQ) {
- if (tf == ULTRAHDR_TF_HLG) {
- std::vector<uint8_t> trc_table;
- trc_table.resize(kTrcTableSize * 2);
- for (uint32_t i = 0; i < kTrcTableSize; ++i) {
- float x = i / (kTrcTableSize - 1.f);
- float y = hlgOetf(x);
- y *= compute_tone_map_gain(tf, y);
- float_to_table16(y, &trc_table[2 * i]);
- }
-
- tags.emplace_back(kTAG_rTRC,
- write_trc_tag(kTrcTableSize, reinterpret_cast<uint8_t*>(trc_table.data())));
- tags.emplace_back(kTAG_gTRC,
- write_trc_tag(kTrcTableSize, reinterpret_cast<uint8_t*>(trc_table.data())));
- tags.emplace_back(kTAG_bTRC,
- write_trc_tag(kTrcTableSize, reinterpret_cast<uint8_t*>(trc_table.data())));
- } else {
- tags.emplace_back(kTAG_rTRC, write_trc_tag(kSRGB_TransFun));
- tags.emplace_back(kTAG_gTRC, write_trc_tag(kSRGB_TransFun));
- tags.emplace_back(kTAG_bTRC, write_trc_tag(kSRGB_TransFun));
- }
- }
-
- // Compute CICP.
- if (tf == ULTRAHDR_TF_HLG || tf == ULTRAHDR_TF_PQ) {
- // The CICP tag is present in ICC 4.4, so update the header's version.
- header.version = Endian_SwapBE32(0x04400000);
-
- uint32_t color_primaries = 0;
- if (gamut == ULTRAHDR_COLORGAMUT_BT709) {
- color_primaries = kCICPPrimariesSRGB;
- } else if (gamut == ULTRAHDR_COLORGAMUT_P3) {
- color_primaries = kCICPPrimariesP3;
- }
-
- uint32_t transfer_characteristics = 0;
- if (tf == ULTRAHDR_TF_SRGB) {
- transfer_characteristics = kCICPTrfnSRGB;
- } else if (tf == ULTRAHDR_TF_LINEAR) {
- transfer_characteristics = kCICPTrfnLinear;
- } else if (tf == ULTRAHDR_TF_PQ) {
- transfer_characteristics = kCICPTrfnPQ;
- } else if (tf == ULTRAHDR_TF_HLG) {
- transfer_characteristics = kCICPTrfnHLG;
- }
- tags.emplace_back(kTAG_cicp, write_cicp_tag(color_primaries, transfer_characteristics));
- }
-
- // Compute A2B0.
- if (tf == ULTRAHDR_TF_PQ) {
- std::vector<uint8_t> a2b_grid;
- a2b_grid.resize(kGridSize * kGridSize * kGridSize * kNumChannels * 2);
- size_t a2b_grid_index = 0;
- for (uint32_t r_index = 0; r_index < kGridSize; ++r_index) {
- for (uint32_t g_index = 0; g_index < kGridSize; ++g_index) {
- for (uint32_t b_index = 0; b_index < kGridSize; ++b_index) {
- float rgb[3] = {
- r_index / (kGridSize - 1.f),
- g_index / (kGridSize - 1.f),
- b_index / (kGridSize - 1.f),
- };
- compute_lut_entry(toXYZD50, rgb);
- float_XYZD50_to_grid16_lab(rgb, &a2b_grid[a2b_grid_index]);
- a2b_grid_index += 6;
- }
- }
- }
- const uint8_t* grid_16 = reinterpret_cast<const uint8_t*>(a2b_grid.data());
-
- uint8_t grid_points[kNumChannels];
- for (size_t i = 0; i < kNumChannels; ++i) {
- grid_points[i] = kGridSize;
- }
-
- auto a2b_data = write_mAB_or_mBA_tag(kTAG_mABType,
- /* has_a_curves */ true,
- grid_points,
- grid_16);
- tags.emplace_back(kTAG_A2B0, std::move(a2b_data));
- }
-
- // Compute B2A0.
- if (tf == ULTRAHDR_TF_PQ) {
- auto b2a_data = write_mAB_or_mBA_tag(kTAG_mBAType,
- /* has_a_curves */ false,
- /* grid_points */ nullptr,
- /* grid_16 */ nullptr);
- tags.emplace_back(kTAG_B2A0, std::move(b2a_data));
- }
-
- // Compute copyright tag
- tags.emplace_back(kTAG_cprt, write_text_tag("Google Inc. 2022"));
-
- // Compute the size of the profile.
- size_t tag_data_size = 0;
- for (const auto& tag : tags) {
- tag_data_size += tag.second->getLength();
- }
- size_t tag_table_size = kICCTagTableEntrySize * tags.size();
- size_t profile_size = kICCHeaderSize + tag_table_size + tag_data_size;
-
- std::shared_ptr<DataStruct> dataStruct =
- std::make_shared<DataStruct>(profile_size + kICCIdentifierSize);
-
- // Write identifier, chunk count, and chunk ID
- if (!dataStruct->write(kICCIdentifier, sizeof(kICCIdentifier)) ||
- !dataStruct->write8(1) || !dataStruct->write8(1)) {
- ALOGE("writeIccProfile(): error in identifier");
- return dataStruct;
- }
-
- // Write the header.
- header.data_color_space = Endian_SwapBE32(Signature_RGB);
- header.pcs = Endian_SwapBE32(tf == ULTRAHDR_TF_PQ ? Signature_Lab : Signature_XYZ);
- header.size = Endian_SwapBE32(profile_size);
- header.tag_count = Endian_SwapBE32(tags.size());
-
- if (!dataStruct->write(&header, sizeof(header))) {
- ALOGE("writeIccProfile(): error in header");
- return dataStruct;
- }
-
- // Write the tag table. Track the offset and size of the previous tag to
- // compute each tag's offset. An empty SkData indicates that the previous
- // tag is to be reused.
- uint32_t last_tag_offset = sizeof(header) + tag_table_size;
- uint32_t last_tag_size = 0;
- for (const auto& tag : tags) {
- last_tag_offset = last_tag_offset + last_tag_size;
- last_tag_size = tag.second->getLength();
- uint32_t tag_table_entry[3] = {
- Endian_SwapBE32(tag.first),
- Endian_SwapBE32(last_tag_offset),
- Endian_SwapBE32(last_tag_size),
- };
- if (!dataStruct->write(tag_table_entry, sizeof(tag_table_entry))) {
- ALOGE("writeIccProfile(): error in writing tag table");
- return dataStruct;
- }
- }
-
- // Write the tags.
- for (const auto& tag : tags) {
- if (!dataStruct->write(tag.second->getData(), tag.second->getLength())) {
- ALOGE("writeIccProfile(): error in writing tags");
- return dataStruct;
- }
- }
-
- return dataStruct;
-}
-
-bool IccHelper::tagsEqualToMatrix(const Matrix3x3& matrix,
- const uint8_t* red_tag,
- const uint8_t* green_tag,
- const uint8_t* blue_tag) {
- std::shared_ptr<DataStruct> red_tag_test =
- write_xyz_tag(matrix.vals[0][0], matrix.vals[1][0], matrix.vals[2][0]);
- std::shared_ptr<DataStruct> green_tag_test =
- write_xyz_tag(matrix.vals[0][1], matrix.vals[1][1], matrix.vals[2][1]);
- std::shared_ptr<DataStruct> blue_tag_test =
- write_xyz_tag(matrix.vals[0][2], matrix.vals[1][2], matrix.vals[2][2]);
- return memcmp(red_tag, red_tag_test->getData(), kColorantTagSize) == 0 &&
- memcmp(green_tag, green_tag_test->getData(), kColorantTagSize) == 0 &&
- memcmp(blue_tag, blue_tag_test->getData(), kColorantTagSize) == 0;
-}
-
-ultrahdr_color_gamut IccHelper::readIccColorGamut(void* icc_data, size_t icc_size) {
- // Each tag table entry consists of 3 fields of 4 bytes each.
- static const size_t kTagTableEntrySize = 12;
-
- if (icc_data == nullptr || icc_size < sizeof(ICCHeader) + kICCIdentifierSize) {
- return ULTRAHDR_COLORGAMUT_UNSPECIFIED;
- }
-
- if (memcmp(icc_data, kICCIdentifier, sizeof(kICCIdentifier)) != 0) {
- return ULTRAHDR_COLORGAMUT_UNSPECIFIED;
- }
-
- uint8_t* icc_bytes = reinterpret_cast<uint8_t*>(icc_data) + kICCIdentifierSize;
-
- ICCHeader* header = reinterpret_cast<ICCHeader*>(icc_bytes);
-
- // Use 0 to indicate not found, since offsets are always relative to start
- // of ICC data and therefore a tag offset of zero would never be valid.
- size_t red_primary_offset = 0, green_primary_offset = 0, blue_primary_offset = 0;
- size_t red_primary_size = 0, green_primary_size = 0, blue_primary_size = 0;
- for (size_t tag_idx = 0; tag_idx < Endian_SwapBE32(header->tag_count); ++tag_idx) {
- uint32_t* tag_entry_start = reinterpret_cast<uint32_t*>(
- icc_bytes + sizeof(ICCHeader) + tag_idx * kTagTableEntrySize);
- // first 4 bytes are the tag signature, next 4 bytes are the tag offset,
- // last 4 bytes are the tag length in bytes.
- if (red_primary_offset == 0 && *tag_entry_start == Endian_SwapBE32(kTAG_rXYZ)) {
- red_primary_offset = Endian_SwapBE32(*(tag_entry_start+1));
- red_primary_size = Endian_SwapBE32(*(tag_entry_start+2));
- } else if (green_primary_offset == 0 && *tag_entry_start == Endian_SwapBE32(kTAG_gXYZ)) {
- green_primary_offset = Endian_SwapBE32(*(tag_entry_start+1));
- green_primary_size = Endian_SwapBE32(*(tag_entry_start+2));
- } else if (blue_primary_offset == 0 && *tag_entry_start == Endian_SwapBE32(kTAG_bXYZ)) {
- blue_primary_offset = Endian_SwapBE32(*(tag_entry_start+1));
- blue_primary_size = Endian_SwapBE32(*(tag_entry_start+2));
- }
- }
-
- if (red_primary_offset == 0 || red_primary_size != kColorantTagSize ||
- kICCIdentifierSize + red_primary_offset + red_primary_size > icc_size ||
- green_primary_offset == 0 || green_primary_size != kColorantTagSize ||
- kICCIdentifierSize + green_primary_offset + green_primary_size > icc_size ||
- blue_primary_offset == 0 || blue_primary_size != kColorantTagSize ||
- kICCIdentifierSize + blue_primary_offset + blue_primary_size > icc_size) {
- return ULTRAHDR_COLORGAMUT_UNSPECIFIED;
- }
-
- uint8_t* red_tag = icc_bytes + red_primary_offset;
- uint8_t* green_tag = icc_bytes + green_primary_offset;
- uint8_t* blue_tag = icc_bytes + blue_primary_offset;
-
- // Serialize tags as we do on encode and compare what we find to that to
- // determine the gamut (since we don't have a need yet for full deserialize).
- if (tagsEqualToMatrix(kSRGB, red_tag, green_tag, blue_tag)) {
- return ULTRAHDR_COLORGAMUT_BT709;
- } else if (tagsEqualToMatrix(kDisplayP3, red_tag, green_tag, blue_tag)) {
- return ULTRAHDR_COLORGAMUT_P3;
- } else if (tagsEqualToMatrix(kRec2020, red_tag, green_tag, blue_tag)) {
- return ULTRAHDR_COLORGAMUT_BT2100;
- }
-
- // Didn't find a match to one of the profiles we write; indicate the gamut
- // is unspecified since we don't understand it.
- return ULTRAHDR_COLORGAMUT_UNSPECIFIED;
-}
-
-} // namespace ultrahdr
diff --git a/include/ultrahdr/icc.h b/include/ultrahdr/icc.h
deleted file mode 100644
index 7a17321..0000000
--- a/include/ultrahdr/icc.h
+++ /dev/null
@@ -1,263 +0,0 @@
-/*
- * Copyright 2022 The Android Open Source Project
- *
- * 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.
- */
-
-#ifndef ULTRAHDR_ICC_H
-#define ULTRAHDR_ICC_H
-
-#include <memory>
-
-#ifndef USE_BIG_ENDIAN_IN_ICC
-#define USE_BIG_ENDIAN_IN_ICC true
-#endif
-
-#if USE_BIG_ENDIAN_IN_ICC
- #define Endian_SwapBE32(n) EndianSwap32(n)
- #define Endian_SwapBE16(n) EndianSwap16(n)
-#else
- #define Endian_SwapBE32(n) (n)
- #define Endian_SwapBE16(n) (n)
-#endif
-
-#include "ultrahdr/ultrahdr.h"
-#include "ultrahdr/jpegr.h"
-#include "ultrahdr/gainmapmath.h"
-#include "ultrahdr/jpegrutils.h"
-
-namespace ultrahdr {
-
-typedef int32_t Fixed;
-#define Fixed1 (1 << 16)
-#define MaxS32FitsInFloat 2147483520
-#define MinS32FitsInFloat (-MaxS32FitsInFloat)
-#define FixedToFloat(x) ((x) * 1.52587890625e-5f)
-
-typedef struct Matrix3x3 {
- float vals[3][3];
-} Matrix3x3;
-
-// The D50 illuminant.
-constexpr float kD50_x = 0.9642f;
-constexpr float kD50_y = 1.0000f;
-constexpr float kD50_z = 0.8249f;
-
-enum {
- // data_color_space
- Signature_CMYK = 0x434D594B,
- Signature_Gray = 0x47524159,
- Signature_RGB = 0x52474220,
-
- // pcs
- Signature_Lab = 0x4C616220,
- Signature_XYZ = 0x58595A20,
-};
-
-typedef uint32_t FourByteTag;
-static inline constexpr FourByteTag SetFourByteTag(char a, char b, char c, char d) {
- return (((uint32_t)a << 24) | ((uint32_t)b << 16) | ((uint32_t)c << 8) | (uint32_t)d);
-}
-
-static constexpr char kICCIdentifier[] = "ICC_PROFILE";
-// 12 for the actual identifier, +2 for the chunk count and chunk index which
-// will always follow.
-static constexpr size_t kICCIdentifierSize = 14;
-
-// This is equal to the header size according to the ICC specification (128)
-// plus the size of the tag count (4). We include the tag count since we
-// always require it to be present anyway.
-static constexpr size_t kICCHeaderSize = 132;
-
-// Contains a signature (4), offset (4), and size (4).
-static constexpr size_t kICCTagTableEntrySize = 12;
-
-// size should be 20; 4 bytes for type descriptor, 4 bytes reserved, 12
-// bytes for a single XYZ number type (4 bytes per coordinate).
-static constexpr size_t kColorantTagSize = 20;
-
-static constexpr uint32_t kDisplay_Profile = SetFourByteTag('m', 'n', 't', 'r');
-static constexpr uint32_t kRGB_ColorSpace = SetFourByteTag('R', 'G', 'B', ' ');
-static constexpr uint32_t kXYZ_PCSSpace = SetFourByteTag('X', 'Y', 'Z', ' ');
-static constexpr uint32_t kACSP_Signature = SetFourByteTag('a', 'c', 's', 'p');
-
-static constexpr uint32_t kTAG_desc = SetFourByteTag('d', 'e', 's', 'c');
-static constexpr uint32_t kTAG_TextType = SetFourByteTag('m', 'l', 'u', 'c');
-static constexpr uint32_t kTAG_rXYZ = SetFourByteTag('r', 'X', 'Y', 'Z');
-static constexpr uint32_t kTAG_gXYZ = SetFourByteTag('g', 'X', 'Y', 'Z');
-static constexpr uint32_t kTAG_bXYZ = SetFourByteTag('b', 'X', 'Y', 'Z');
-static constexpr uint32_t kTAG_wtpt = SetFourByteTag('w', 't', 'p', 't');
-static constexpr uint32_t kTAG_rTRC = SetFourByteTag('r', 'T', 'R', 'C');
-static constexpr uint32_t kTAG_gTRC = SetFourByteTag('g', 'T', 'R', 'C');
-static constexpr uint32_t kTAG_bTRC = SetFourByteTag('b', 'T', 'R', 'C');
-static constexpr uint32_t kTAG_cicp = SetFourByteTag('c', 'i', 'c', 'p');
-static constexpr uint32_t kTAG_cprt = SetFourByteTag('c', 'p', 'r', 't');
-static constexpr uint32_t kTAG_A2B0 = SetFourByteTag('A', '2', 'B', '0');
-static constexpr uint32_t kTAG_B2A0 = SetFourByteTag('B', '2', 'A', '0');
-
-static constexpr uint32_t kTAG_CurveType = SetFourByteTag('c', 'u', 'r', 'v');
-static constexpr uint32_t kTAG_mABType = SetFourByteTag('m', 'A', 'B', ' ');
-static constexpr uint32_t kTAG_mBAType = SetFourByteTag('m', 'B', 'A', ' ');
-static constexpr uint32_t kTAG_ParaCurveType = SetFourByteTag('p', 'a', 'r', 'a');
-
-
-static constexpr Matrix3x3 kSRGB = {{
- // ICC fixed-point (16.16) representation, taken from skcms. Please keep them exactly in sync.
- // 0.436065674f, 0.385147095f, 0.143066406f,
- // 0.222488403f, 0.716873169f, 0.060607910f,
- // 0.013916016f, 0.097076416f, 0.714096069f,
- { FixedToFloat(0x6FA2), FixedToFloat(0x6299), FixedToFloat(0x24A0) },
- { FixedToFloat(0x38F5), FixedToFloat(0xB785), FixedToFloat(0x0F84) },
- { FixedToFloat(0x0390), FixedToFloat(0x18DA), FixedToFloat(0xB6CF) },
-}};
-
-static constexpr Matrix3x3 kDisplayP3 = {{
- { 0.515102f, 0.291965f, 0.157153f },
- { 0.241182f, 0.692236f, 0.0665819f },
- { -0.00104941f, 0.0418818f, 0.784378f },
-}};
-
-static constexpr Matrix3x3 kRec2020 = {{
- { 0.673459f, 0.165661f, 0.125100f },
- { 0.279033f, 0.675338f, 0.0456288f },
- { -0.00193139f, 0.0299794f, 0.797162f },
-}};
-
-static constexpr uint32_t kCICPPrimariesSRGB = 1;
-static constexpr uint32_t kCICPPrimariesP3 = 12;
-static constexpr uint32_t kCICPPrimariesRec2020 = 9;
-
-static constexpr uint32_t kCICPTrfnSRGB = 1;
-static constexpr uint32_t kCICPTrfnLinear = 8;
-static constexpr uint32_t kCICPTrfnPQ = 16;
-static constexpr uint32_t kCICPTrfnHLG = 18;
-
-enum ParaCurveType {
- kExponential_ParaCurveType = 0,
- kGAB_ParaCurveType = 1,
- kGABC_ParaCurveType = 2,
- kGABDE_ParaCurveType = 3,
- kGABCDEF_ParaCurveType = 4,
-};
-
-/**
- * Return the closest int for the given float. Returns MaxS32FitsInFloat for NaN.
- */
-static inline int float_saturate2int(float x) {
- x = x < MaxS32FitsInFloat ? x : MaxS32FitsInFloat;
- x = x > MinS32FitsInFloat ? x : MinS32FitsInFloat;
- return (int)x;
-}
-
-static Fixed float_round_to_fixed(float x) {
- return float_saturate2int((float)floor((double)x * Fixed1 + 0.5));
-}
-
-static uint16_t float_round_to_unorm16(float x) {
- x = x * 65535.f + 0.5;
- if (x > 65535) return 65535;
- if (x < 0) return 0;
- return static_cast<uint16_t>(x);
-}
-
-static inline void float_to_table16(const float f, uint8_t* table_16) {
- *reinterpret_cast<uint16_t*>(table_16) = Endian_SwapBE16(float_round_to_unorm16(f));
-}
-
-static inline bool isfinitef_(float x) { return 0 == x*0; }
-
-struct ICCHeader {
- // Size of the profile (computed)
- uint32_t size;
- // Preferred CMM type (ignored)
- uint32_t cmm_type = 0;
- // Version 4.3 or 4.4 if CICP is included.
- uint32_t version = Endian_SwapBE32(0x04300000);
- // Display device profile
- uint32_t profile_class = Endian_SwapBE32(kDisplay_Profile);
- // RGB input color space;
- uint32_t data_color_space = Endian_SwapBE32(kRGB_ColorSpace);
- // Profile connection space.
- uint32_t pcs = Endian_SwapBE32(kXYZ_PCSSpace);
- // Date and time (ignored)
- uint8_t creation_date_time[12] = {0};
- // Profile signature
- uint32_t signature = Endian_SwapBE32(kACSP_Signature);
- // Platform target (ignored)
- uint32_t platform = 0;
- // Flags: not embedded, can be used independently
- uint32_t flags = 0x00000000;
- // Device manufacturer (ignored)
- uint32_t device_manufacturer = 0;
- // Device model (ignored)
- uint32_t device_model = 0;
- // Device attributes (ignored)
- uint8_t device_attributes[8] = {0};
- // Relative colorimetric rendering intent
- uint32_t rendering_intent = Endian_SwapBE32(1);
- // D50 standard illuminant (X, Y, Z)
- uint32_t illuminant_X = Endian_SwapBE32(float_round_to_fixed(kD50_x));
- uint32_t illuminant_Y = Endian_SwapBE32(float_round_to_fixed(kD50_y));
- uint32_t illuminant_Z = Endian_SwapBE32(float_round_to_fixed(kD50_z));
- // Profile creator (ignored)
- uint32_t creator = 0;
- // Profile id checksum (ignored)
- uint8_t profile_id[16] = {0};
- // Reserved (ignored)
- uint8_t reserved[28] = {0};
- // Technically not part of header, but required
- uint32_t tag_count = 0;
-};
-
-class IccHelper {
-private:
- static constexpr uint32_t kTrcTableSize = 65;
- static constexpr uint32_t kGridSize = 17;
- static constexpr size_t kNumChannels = 3;
-
- static std::shared_ptr<DataStruct> write_text_tag(const char* text);
- static std::string get_desc_string(const ultrahdr_transfer_function tf,
- const ultrahdr_color_gamut gamut);
- static std::shared_ptr<DataStruct> write_xyz_tag(float x, float y, float z);
- static std::shared_ptr<DataStruct> write_trc_tag(const int table_entries, const void* table_16);
- static std::shared_ptr<DataStruct> write_trc_tag(const TransferFunction& fn);
- static float compute_tone_map_gain(const ultrahdr_transfer_function tf, float L);
- static std::shared_ptr<DataStruct> write_cicp_tag(uint32_t color_primaries,
- uint32_t transfer_characteristics);
- static std::shared_ptr<DataStruct> write_mAB_or_mBA_tag(uint32_t type, bool has_a_curves,
- const uint8_t* grid_points,
- const uint8_t* grid_16);
- static void compute_lut_entry(const Matrix3x3& src_to_XYZD50, float rgb[3]);
- static std::shared_ptr<DataStruct> write_clut(const uint8_t* grid_points,
- const uint8_t* grid_16);
-
- // Checks if a set of xyz tags is equivalent to a 3x3 Matrix. Each input
- // tag buffer assumed to be at least kColorantTagSize in size.
- static bool tagsEqualToMatrix(const Matrix3x3& matrix,
- const uint8_t* red_tag,
- const uint8_t* green_tag,
- const uint8_t* blue_tag);
-
-public:
- // Output includes JPEG embedding identifier and chunk information, but not
- // APPx information.
- static std::shared_ptr<DataStruct> writeIccProfile(const ultrahdr_transfer_function tf,
- const ultrahdr_color_gamut gamut);
- // NOTE: this function is not robust; it can infer gamuts that IccHelper
- // writes out but should not be considered a reference implementation for
- // robust parsing of ICC profiles or their gamuts.
- static ultrahdr_color_gamut readIccColorGamut(void* icc_data, size_t icc_size);
-};
-} // namespace ultrahdr
-
-#endif //ULTRAHDR_ICC_H
diff --git a/include/ultrahdr/jpegdecoderhelper.h b/include/ultrahdr/jpegdecoderhelper.h
deleted file mode 100644
index 4c8b601..0000000
--- a/include/ultrahdr/jpegdecoderhelper.h
+++ /dev/null
@@ -1,155 +0,0 @@
-/*
- * Copyright 2022 The Android Open Source Project
- *
- * 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.
- */
-
-#ifndef ULTRAHDR_JPEGDECODERHELPER_H
-#define ULTRAHDR_JPEGDECODERHELPER_H
-
-#include <stdio.h> // For jpeglib.h.
-
-// C++ build requires extern C for jpeg internals.
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include <jerror.h>
-#include <jpeglib.h>
-
-#ifdef __cplusplus
-} // extern "C"
-#endif
-
-#include <cstdint>
-#include <memory>
-#include <vector>
-
-// constraint on max width and max height is only due to device alloc constraints
-// Can tune these values basing on the target device
-static const int kMaxWidth = 8192;
-static const int kMaxHeight = 8192;
-
-namespace ultrahdr {
-/*
- * Encapsulates a converter from JPEG to raw image (YUV420planer or grey-scale) format.
- * This class is not thread-safe.
- */
-class JpegDecoderHelper {
-public:
- JpegDecoderHelper();
- ~JpegDecoderHelper();
- /*
- * Decompresses JPEG image to raw image (YUV420planer, grey-scale or RGBA) format. After
- * calling this method, call getDecompressedImage() to get the image.
- * Returns false if decompressing the image fails.
- */
- bool decompressImage(const void* image, int length, bool decodeToRGBA = false);
- /*
- * Returns the decompressed raw image buffer pointer. This method must be called only after
- * calling decompressImage().
- */
- void* getDecompressedImagePtr();
- /*
- * Returns the decompressed raw image buffer size. This method must be called only after
- * calling decompressImage().
- */
- size_t getDecompressedImageSize();
- /*
- * Returns the image width in pixels. This method must be called only after calling
- * decompressImage().
- */
- size_t getDecompressedImageWidth();
- /*
- * Returns the image width in pixels. This method must be called only after calling
- * decompressImage().
- */
- size_t getDecompressedImageHeight();
- /*
- * Returns the XMP data from the image.
- */
- void* getXMPPtr();
- /*
- * Returns the decompressed XMP buffer size. This method must be called only after
- * calling decompressImage() or getCompressedImageParameters().
- */
- size_t getXMPSize();
- /*
- * Extracts EXIF package and updates the EXIF position / length without decoding the image.
- */
- bool extractEXIF(const void* image, int length);
- /*
- * Returns the EXIF data from the image.
- * This method must be called after extractEXIF() or decompressImage().
- */
- void* getEXIFPtr();
- /*
- * Returns the decompressed EXIF buffer size. This method must be called only after
- * calling decompressImage(), extractEXIF() or getCompressedImageParameters().
- */
- size_t getEXIFSize();
- /*
- * Returns the position offset of EXIF package
- * (4 bypes offset to FF sign, the byte after FF E1 XX XX <this byte>),
- * or -1 if no EXIF exists.
- * This method must be called after extractEXIF() or decompressImage().
- */
- int getEXIFPos() { return mExifPos; }
- /*
- * Returns the ICC data from the image.
- */
- void* getICCPtr();
- /*
- * Returns the decompressed ICC buffer size. This method must be called only after
- * calling decompressImage() or getCompressedImageParameters().
- */
- size_t getICCSize();
- /*
- * Decompresses metadata of the image. All vectors are owned by the caller.
- */
- bool getCompressedImageParameters(const void* image, int length, size_t* pWidth,
- size_t* pHeight, std::vector<uint8_t>* iccData,
- std::vector<uint8_t>* exifData);
-
-private:
- bool decode(const void* image, int length, bool decodeToRGBA);
- // Returns false if errors occur.
- bool decompress(jpeg_decompress_struct* cinfo, const uint8_t* dest, bool isSingleChannel);
- bool decompressYUV(jpeg_decompress_struct* cinfo, const uint8_t* dest);
- bool decompressRGBA(jpeg_decompress_struct* cinfo, const uint8_t* dest);
- bool decompressSingleChannel(jpeg_decompress_struct* cinfo, const uint8_t* dest);
- // Process 16 lines of Y and 16 lines of U/V each time.
- // We must pass at least 16 scanlines according to libjpeg documentation.
- static const int kCompressBatchSize = 16;
- // The buffer that holds the decompressed result.
- std::vector<JOCTET> mResultBuffer;
- // The buffer that holds XMP Data.
- std::vector<JOCTET> mXMPBuffer;
- // The buffer that holds EXIF Data.
- std::vector<JOCTET> mEXIFBuffer;
- // The buffer that holds ICC Data.
- std::vector<JOCTET> mICCBuffer;
-
- // Resolution of the decompressed image.
- size_t mWidth;
- size_t mHeight;
-
- // Position of EXIF package, default value is -1 which means no EXIF package appears.
- int mExifPos = -1;
-
- std::unique_ptr<uint8_t[]> mEmpty = nullptr;
- std::unique_ptr<uint8_t[]> mBufferIntermediate = nullptr;
-};
-} /* namespace ultrahdr */
-
-#endif // ULTRAHDR_JPEGDECODERHELPER_H
diff --git a/include/ultrahdr/jpegencoderhelper.h b/include/ultrahdr/jpegencoderhelper.h
deleted file mode 100644
index 47325fe..0000000
--- a/include/ultrahdr/jpegencoderhelper.h
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- * Copyright 2022 The Android Open Source Project
- *
- * 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.
- */
-
-#ifndef ULTRAHDR_JPEGENCODERHELPER_H
-#define ULTRAHDR_JPEGENCODERHELPER_H
-
-#include <stdio.h> // For jpeglib.h.
-
-// C++ build requires extern C for jpeg internals.
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include <jerror.h>
-#include <jpeglib.h>
-
-#ifdef __cplusplus
-} // extern "C"
-#endif
-
-#include <cstdint>
-#include <vector>
-
-namespace ultrahdr {
-
-/*
- * Encapsulates a converter from raw image (YUV420planer or grey-scale) to JPEG format.
- * This class is not thread-safe.
- */
-class JpegEncoderHelper {
-public:
- JpegEncoderHelper();
- ~JpegEncoderHelper();
-
- /*
- * Compresses YUV420Planer image to JPEG format. After calling this method, call
- * getCompressedImage() to get the image. |quality| is the jpeg image quality parameter to use.
- * It ranges from 1 (poorest quality) to 100 (highest quality). |iccBuffer| is the buffer of
- * ICC segment which will be added to the compressed image.
- * Returns false if errors occur during compression.
- */
- bool compressImage(const uint8_t* yBuffer, const uint8_t* uvBuffer, int width, int height,
- int lumaStride, int chromaStride, int quality, const void* iccBuffer,
- unsigned int iccSize);
-
- /*
- * Returns the compressed JPEG buffer pointer. This method must be called only after calling
- * compressImage().
- */
- void* getCompressedImagePtr();
-
- /*
- * Returns the compressed JPEG buffer size. This method must be called only after calling
- * compressImage().
- */
- size_t getCompressedImageSize();
-
- /*
- * Process 16 lines of Y and 16 lines of U/V each time.
- * We must pass at least 16 scanlines according to libjpeg documentation.
- */
- static const int kCompressBatchSize = 16;
-
-private:
- // initDestination(), emptyOutputBuffer() and emptyOutputBuffer() are callback functions to be
- // passed into jpeg library.
- static void initDestination(j_compress_ptr cinfo);
- static boolean emptyOutputBuffer(j_compress_ptr cinfo);
- static void terminateDestination(j_compress_ptr cinfo);
- static void outputErrorMessage(j_common_ptr cinfo);
-
- // Returns false if errors occur.
- bool encode(const uint8_t* yBuffer, const uint8_t* uvBuffer, int width, int height,
- int lumaStride, int chromaStride, int quality, const void* iccBuffer,
- unsigned int iccSize);
- void setJpegDestination(jpeg_compress_struct* cinfo);
- void setJpegCompressStruct(int width, int height, int quality, jpeg_compress_struct* cinfo,
- bool isSingleChannel);
- // Returns false if errors occur.
- bool compressYuv(jpeg_compress_struct* cinfo, const uint8_t* yBuffer, const uint8_t* uvBuffer,
- int lumaStride, int chromaStride);
- bool compressY(jpeg_compress_struct* cinfo, const uint8_t* yBuffer, int lumaStride);
-
- // The block size for encoded jpeg image buffer.
- static const int kBlockSize = 16384;
-
- // The buffer that holds the compressed result.
- std::vector<JOCTET> mResultBuffer;
-};
-
-} /* namespace ultrahdr */
-
-#endif // ULTRAHDR_JPEGENCODERHELPER_H
diff --git a/include/ultrahdr/jpegr.h b/include/ultrahdr/jpegr.h
deleted file mode 100644
index d5edf56..0000000
--- a/include/ultrahdr/jpegr.h
+++ /dev/null
@@ -1,465 +0,0 @@
-/*
- * Copyright 2022 The Android Open Source Project
- *
- * 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.
- */
-
-#ifndef ULTRAHDR_JPEGR_H
-#define ULTRAHDR_JPEGR_H
-
-#include <cfloat>
-
-#include "ultrahdr/ultrahdr.h"
-#include "ultrahdr/jpegdecoderhelper.h"
-#include "ultrahdr/jpegencoderhelper.h"
-
-namespace ultrahdr {
-
-// The current JPEGR version that we encode to
-static const char* const kJpegrVersion = "1.0";
-
-// Map is quarter res / sixteenth size
-static const size_t kMapDimensionScaleFactor = 4;
-
-// Gain Map width is (image_width / kMapDimensionScaleFactor). If we were to
-// compress 420 GainMap in jpeg, then we need at least 2 samples. For Grayscale
-// 1 sample is sufficient. We are using 2 here anyways
-static const int kMinWidth = 2 * kMapDimensionScaleFactor;
-static const int kMinHeight = 2 * kMapDimensionScaleFactor;
-
-typedef enum {
- OK = 0,
- NO_ERROR = OK,
- UNKNOWN_ERROR = (-2147483647 - 1),
-
- JPEGR_IO_ERROR_BASE = -10000,
- ERROR_JPEGR_INVALID_INPUT_TYPE = JPEGR_IO_ERROR_BASE,
- ERROR_JPEGR_INVALID_OUTPUT_TYPE = JPEGR_IO_ERROR_BASE - 1,
- ERROR_JPEGR_INVALID_NULL_PTR = JPEGR_IO_ERROR_BASE - 2,
- ERROR_JPEGR_RESOLUTION_MISMATCH = JPEGR_IO_ERROR_BASE - 3,
- ERROR_JPEGR_BUFFER_TOO_SMALL = JPEGR_IO_ERROR_BASE - 4,
- ERROR_JPEGR_INVALID_COLORGAMUT = JPEGR_IO_ERROR_BASE - 5,
- ERROR_JPEGR_INVALID_TRANS_FUNC = JPEGR_IO_ERROR_BASE - 6,
- ERROR_JPEGR_INVALID_METADATA = JPEGR_IO_ERROR_BASE - 7,
- ERROR_JPEGR_UNSUPPORTED_METADATA = JPEGR_IO_ERROR_BASE - 8,
- ERROR_JPEGR_GAIN_MAP_IMAGE_NOT_FOUND = JPEGR_IO_ERROR_BASE - 9,
-
- JPEGR_RUNTIME_ERROR_BASE = -20000,
- ERROR_JPEGR_ENCODE_ERROR = JPEGR_RUNTIME_ERROR_BASE - 1,
- ERROR_JPEGR_DECODE_ERROR = JPEGR_RUNTIME_ERROR_BASE - 2,
- ERROR_JPEGR_CALCULATION_ERROR = JPEGR_RUNTIME_ERROR_BASE - 3,
- ERROR_JPEGR_METADATA_ERROR = JPEGR_RUNTIME_ERROR_BASE - 4,
- ERROR_JPEGR_TONEMAP_ERROR = JPEGR_RUNTIME_ERROR_BASE - 5,
-
- ERROR_JPEGR_UNSUPPORTED_FEATURE = -20000,
-} status_t;
-
-/*
- * Holds information of jpegr image
- */
-struct jpegr_info_struct {
- size_t width;
- size_t height;
- std::vector<uint8_t>* iccData;
- std::vector<uint8_t>* exifData;
-};
-
-/*
- * Holds information for uncompressed image or gain map.
- */
-struct jpegr_uncompressed_struct {
- // Pointer to the data location.
- void* data;
- // Width of the gain map or the luma plane of the image in pixels.
- size_t width;
- // Height of the gain map or the luma plane of the image in pixels.
- size_t height;
- // Color gamut.
- ultrahdr_color_gamut colorGamut;
-
- // Values below are optional
- // Pointer to chroma data, if it's NULL, chroma plane is considered to be immediately
- // after the luma plane.
- void* chroma_data = nullptr;
- // Stride of Y plane in number of pixels. 0 indicates the member is uninitialized. If
- // non-zero this value must be larger than or equal to luma width. If stride is
- // uninitialized then it is assumed to be equal to luma width.
- size_t luma_stride = 0;
- // Stride of UV plane in number of pixels.
- // 1. If this handle points to P010 image then this value must be larger than
- // or equal to luma width.
- // 2. If this handle points to 420 image then this value must be larger than
- // or equal to (luma width / 2).
- // NOTE: if chroma_data is nullptr, chroma_stride is irrelevant. Just as the way,
- // chroma_data is derived from luma ptr, chroma stride is derived from luma stride.
- size_t chroma_stride = 0;
-};
-
-/*
- * Holds information for compressed image or gain map.
- */
-struct jpegr_compressed_struct {
- // Pointer to the data location.
- void* data;
- // Used data length in bytes.
- int length;
- // Maximum available data length in bytes.
- int maxLength;
- // Color gamut.
- ultrahdr_color_gamut colorGamut;
-};
-
-/*
- * Holds information for EXIF metadata.
- */
-struct jpegr_exif_struct {
- // Pointer to the data location.
- void* data;
- // Data length;
- size_t length;
-};
-
-typedef struct jpegr_uncompressed_struct* jr_uncompressed_ptr;
-typedef struct jpegr_compressed_struct* jr_compressed_ptr;
-typedef struct jpegr_exif_struct* jr_exif_ptr;
-typedef struct jpegr_info_struct* jr_info_ptr;
-
-class JpegR {
-public:
- /*
- * Experimental only
- *
- * Encode API-0
- * Compress JPEGR image from 10-bit HDR YUV.
- *
- * Tonemap the HDR input to a SDR image, generate gain map from the HDR and SDR images,
- * compress SDR YUV to 8-bit JPEG and append the gain map to the end of the compressed
- * JPEG.
- * @param p010_image_ptr uncompressed HDR image in P010 color format
- * @param hdr_tf transfer function of the HDR image
- * @param dest destination of the compressed JPEGR image. Please note that {@code maxLength}
- * represents the maximum available size of the destination buffer, and it must be
- * set before calling this method. If the encoded JPEGR size exceeds
- * {@code maxLength}, this method will return {@code ERROR_JPEGR_BUFFER_TOO_SMALL}.
- * @param quality target quality of the JPEG encoding, must be in range of 0-100 where 100 is
- * the highest quality
- * @param exif pointer to the exif metadata.
- * @return NO_ERROR if encoding succeeds, error code if error occurs.
- */
- status_t encodeJPEGR(jr_uncompressed_ptr p010_image_ptr, ultrahdr_transfer_function hdr_tf,
- jr_compressed_ptr dest, int quality, jr_exif_ptr exif);
-
- /*
- * Encode API-1
- * Compress JPEGR image from 10-bit HDR YUV and 8-bit SDR YUV.
- *
- * Generate gain map from the HDR and SDR inputs, compress SDR YUV to 8-bit JPEG and append
- * the gain map to the end of the compressed JPEG. HDR and SDR inputs must be the same
- * resolution. SDR input is assumed to use the sRGB transfer function.
- * @param p010_image_ptr uncompressed HDR image in P010 color format
- * @param yuv420_image_ptr uncompressed SDR image in YUV_420 color format
- * @param hdr_tf transfer function of the HDR image
- * @param dest destination of the compressed JPEGR image. Please note that {@code maxLength}
- * represents the maximum available size of the desitination buffer, and it must be
- * set before calling this method. If the encoded JPEGR size exceeds
- * {@code maxLength}, this method will return {@code ERROR_JPEGR_BUFFER_TOO_SMALL}.
- * @param quality target quality of the JPEG encoding, must be in range of 0-100 where 100 is
- * the highest quality
- * @param exif pointer to the exif metadata.
- * @return NO_ERROR if encoding succeeds, error code if error occurs.
- */
- status_t encodeJPEGR(jr_uncompressed_ptr p010_image_ptr, jr_uncompressed_ptr yuv420_image_ptr,
- ultrahdr_transfer_function hdr_tf, jr_compressed_ptr dest, int quality,
- jr_exif_ptr exif);
-
- /*
- * Encode API-2
- * Compress JPEGR image from 10-bit HDR YUV, 8-bit SDR YUV and compressed 8-bit JPEG.
- *
- * This method requires HAL Hardware JPEG encoder.
- *
- * Generate gain map from the HDR and SDR inputs, append the gain map to the end of the
- * compressed JPEG. Adds an ICC profile if one isn't present in the input JPEG image. HDR and
- * SDR inputs must be the same resolution and color space. SDR image is assumed to use the sRGB
- * transfer function.
- * @param p010_image_ptr uncompressed HDR image in P010 color format
- * @param yuv420_image_ptr uncompressed SDR image in YUV_420 color format
- * @param yuv420jpg_image_ptr SDR image compressed in jpeg format
- * @param hdr_tf transfer function of the HDR image
- * @param dest destination of the compressed JPEGR image. Please note that {@code maxLength}
- * represents the maximum available size of the desitination buffer, and it must be
- * set before calling this method. If the encoded JPEGR size exceeds
- * {@code maxLength}, this method will return {@code ERROR_JPEGR_BUFFER_TOO_SMALL}.
- * @return NO_ERROR if encoding succeeds, error code if error occurs.
- */
- status_t encodeJPEGR(jr_uncompressed_ptr p010_image_ptr, jr_uncompressed_ptr yuv420_image_ptr,
- jr_compressed_ptr yuv420jpg_image_ptr, ultrahdr_transfer_function hdr_tf,
- jr_compressed_ptr dest);
-
- /*
- * Encode API-3
- * Compress JPEGR image from 10-bit HDR YUV and 8-bit SDR YUV.
- *
- * This method requires HAL Hardware JPEG encoder.
- *
- * Decode the compressed 8-bit JPEG image to YUV SDR, generate gain map from the HDR input
- * and the decoded SDR result, append the gain map to the end of the compressed JPEG. Adds an
- * ICC profile if one isn't present in the input JPEG image. HDR and SDR inputs must be the same
- * resolution. JPEG image is assumed to use the sRGB transfer function.
- * @param p010_image_ptr uncompressed HDR image in P010 color format
- * @param yuv420jpg_image_ptr SDR image compressed in jpeg format
- * @param hdr_tf transfer function of the HDR image
- * @param dest destination of the compressed JPEGR image. Please note that {@code maxLength}
- * represents the maximum available size of the desitination buffer, and it must be
- * set before calling this method. If the encoded JPEGR size exceeds
- * {@code maxLength}, this method will return {@code ERROR_JPEGR_BUFFER_TOO_SMALL}.
- * @return NO_ERROR if encoding succeeds, error code if error occurs.
- */
- status_t encodeJPEGR(jr_uncompressed_ptr p010_image_ptr, jr_compressed_ptr yuv420jpg_image_ptr,
- ultrahdr_transfer_function hdr_tf, jr_compressed_ptr dest);
-
- /*
- * Encode API-4
- * Assemble JPEGR image from SDR JPEG and gainmap JPEG.
- *
- * Assemble the primary JPEG image, the gain map and the metadata to JPEG/R format. Adds an ICC
- * profile if one isn't present in the input JPEG image.
- * @param yuv420jpg_image_ptr SDR image compressed in jpeg format
- * @param gainmapjpg_image_ptr gain map image compressed in jpeg format
- * @param metadata metadata to be written in XMP of the primary jpeg
- * @param dest destination of the compressed JPEGR image. Please note that {@code maxLength}
- * represents the maximum available size of the desitination buffer, and it must be
- * set before calling this method. If the encoded JPEGR size exceeds
- * {@code maxLength}, this method will return {@code ERROR_JPEGR_BUFFER_TOO_SMALL}.
- * @return NO_ERROR if encoding succeeds, error code if error occurs.
- */
- status_t encodeJPEGR(jr_compressed_ptr yuv420jpg_image_ptr,
- jr_compressed_ptr gainmapjpg_image_ptr, ultrahdr_metadata_ptr metadata,
- jr_compressed_ptr dest);
-
- /*
- * Decode API
- * Decompress JPEGR image.
- *
- * This method assumes that the JPEGR image contains an ICC profile with primaries that match
- * those of a color gamut that this library is aware of; Bt.709, Display-P3, or Bt.2100. It also
- * assumes the base image uses the sRGB transfer function.
- *
- * This method only supports single gain map metadata values for fields that allow multi-channel
- * metadata values.
- * @param jpegr_image_ptr compressed JPEGR image.
- * @param dest destination of the uncompressed JPEGR image.
- * @param max_display_boost (optional) the maximum available boost supported by a display,
- * the value must be greater than or equal to 1.0.
- * @param exif destination of the decoded EXIF metadata. The default value is NULL where the
- decoder will do nothing about it. If configured not NULL the decoder will write
- EXIF data into this structure. The format is defined in {@code jpegr_exif_struct}
- * @param output_format flag for setting output color format. Its value configures the output
- color format. The default value is {@code JPEGR_OUTPUT_HDR_LINEAR}.
- ----------------------------------------------------------------------
- | output_format | decoded color format to be written |
- ----------------------------------------------------------------------
- | JPEGR_OUTPUT_SDR | RGBA_8888 |
- ----------------------------------------------------------------------
- | JPEGR_OUTPUT_HDR_LINEAR | (default)RGBA_F16 linear |
- ----------------------------------------------------------------------
- | JPEGR_OUTPUT_HDR_PQ | RGBA_1010102 PQ |
- ----------------------------------------------------------------------
- | JPEGR_OUTPUT_HDR_HLG | RGBA_1010102 HLG |
- ----------------------------------------------------------------------
- * @param gainmap_image_ptr destination of the decoded gain map. The default value is NULL
- where the decoder will do nothing about it. If configured not NULL
- the decoder will write the decoded gain_map data into this
- structure. The format is defined in
- {@code jpegr_uncompressed_struct}.
- * @param metadata destination of the decoded metadata. The default value is NULL where the
- decoder will do nothing about it. If configured not NULL the decoder will
- write metadata into this structure. the format of metadata is defined in
- {@code ultrahdr_metadata_struct}.
- * @return NO_ERROR if decoding succeeds, error code if error occurs.
- */
- status_t decodeJPEGR(jr_compressed_ptr jpegr_image_ptr, jr_uncompressed_ptr dest,
- float max_display_boost = FLT_MAX, jr_exif_ptr exif = nullptr,
- ultrahdr_output_format output_format = ULTRAHDR_OUTPUT_HDR_LINEAR,
- jr_uncompressed_ptr gainmap_image_ptr = nullptr,
- ultrahdr_metadata_ptr metadata = nullptr);
-
- /*
- * Gets Info from JPEGR file without decoding it.
- *
- * This method only supports single gain map metadata values for fields that allow multi-channel
- * metadata values.
- *
- * The output is filled jpegr_info structure
- * @param jpegr_image_ptr compressed JPEGR image
- * @param jpeg_image_info_ptr pointer to jpegr info struct. Members of jpegr_info
- * are owned by the caller
- * @return NO_ERROR if JPEGR parsing succeeds, error code otherwise
- */
- status_t getJPEGRInfo(jr_compressed_ptr jpegr_image_ptr, jr_info_ptr jpeg_image_info_ptr);
-
-protected:
- /*
- * This method is called in the encoding pipeline. It will take the uncompressed 8-bit and
- * 10-bit yuv images as input, and calculate the uncompressed gain map. The input images
- * must be the same resolution. The SDR input is assumed to use the sRGB transfer function.
- *
- * @param yuv420_image_ptr uncompressed SDR image in YUV_420 color format
- * @param p010_image_ptr uncompressed HDR image in P010 color format
- * @param hdr_tf transfer function of the HDR image
- * @param metadata everything but "version" is filled in this struct
- * @param dest location at which gain map image is stored (caller responsible for memory
- of data).
- * @param sdr_is_601 if true, then use BT.601 decoding of YUV regardless of SDR image gamut
- * @return NO_ERROR if calculation succeeds, error code if error occurs.
- */
- status_t generateGainMap(jr_uncompressed_ptr yuv420_image_ptr,
- jr_uncompressed_ptr p010_image_ptr, ultrahdr_transfer_function hdr_tf,
- ultrahdr_metadata_ptr metadata, jr_uncompressed_ptr dest,
- bool sdr_is_601 = false);
-
- /*
- * This method is called in the decoding pipeline. It will take the uncompressed (decoded)
- * 8-bit yuv image, the uncompressed (decoded) gain map, and extracted JPEG/R metadata as
- * input, and calculate the 10-bit recovered image. The recovered output image is the same
- * color gamut as the SDR image, with HLG transfer function, and is in RGBA1010102 data format.
- * The SDR image is assumed to use the sRGB transfer function. The SDR image is also assumed to
- * be a decoded JPEG for the purpose of YUV interpration.
- *
- * @param yuv420_image_ptr uncompressed SDR image in YUV_420 color format
- * @param gainmap_image_ptr pointer to uncompressed gain map image struct.
- * @param metadata JPEG/R metadata extracted from XMP.
- * @param output_format flag for setting output color format. if set to
- * {@code JPEGR_OUTPUT_SDR}, decoder will only decode the primary image
- * which is SDR. Default value is JPEGR_OUTPUT_HDR_LINEAR.
- * @param max_display_boost the maximum available boost supported by a display
- * @param dest reconstructed HDR image
- * @return NO_ERROR if calculation succeeds, error code if error occurs.
- */
- status_t applyGainMap(jr_uncompressed_ptr yuv420_image_ptr,
- jr_uncompressed_ptr gainmap_image_ptr, ultrahdr_metadata_ptr metadata,
- ultrahdr_output_format output_format, float max_display_boost,
- jr_uncompressed_ptr dest);
-
-private:
- /*
- * This method is called in the encoding pipeline. It will encode the gain map.
- *
- * @param gainmap_image_ptr pointer to uncompressed gain map image struct
- * @param jpeg_enc_obj_ptr helper resource to compress gain map
- * @return NO_ERROR if encoding succeeds, error code if error occurs.
- */
- status_t compressGainMap(jr_uncompressed_ptr gainmap_image_ptr,
- JpegEncoderHelper* jpeg_enc_obj_ptr);
-
- /*
- * This method is called to separate primary image and gain map image from JPEGR
- *
- * @param jpegr_image_ptr pointer to compressed JPEGR image.
- * @param primary_jpg_image_ptr destination of primary image
- * @param gainmap_jpg_image_ptr destination of compressed gain map image
- * @return NO_ERROR if calculation succeeds, error code if error occurs.
- */
- status_t extractPrimaryImageAndGainMap(jr_compressed_ptr jpegr_image_ptr,
- jr_compressed_ptr primary_jpg_image_ptr,
- jr_compressed_ptr gainmap_jpg_image_ptr);
-
- /*
- * This method is called in the encoding pipeline. It will take the standard 8-bit JPEG image,
- * the compressed gain map and optionally the exif package as inputs, and generate the XMP
- * metadata, and finally append everything in the order of:
- * SOI, APP2(EXIF) (if EXIF is from outside), APP2(XMP), primary image, gain map
- *
- * Note that in the final JPEG/R output, EXIF package will appear if ONLY ONE of the following
- * conditions is fulfilled:
- * (1) EXIF package is available from outside input. I.e. pExif != nullptr.
- * (2) Input JPEG has EXIF.
- * If both conditions are fulfilled, this method will return ERROR_JPEGR_INVALID_INPUT_TYPE
- *
- * @param primary_jpg_image_ptr destination of primary image
- * @param gainmap_jpg_image_ptr destination of compressed gain map image
- * @param (nullable) pExif EXIF package
- * @param (nullable) pIcc ICC package
- * @param icc_size length in bytes of ICC package
- * @param metadata JPEG/R metadata to encode in XMP of the jpeg
- * @param dest compressed JPEGR image
- * @return NO_ERROR if calculation succeeds, error code if error occurs.
- */
- status_t appendGainMap(jr_compressed_ptr primary_jpg_image_ptr,
- jr_compressed_ptr gainmap_jpg_image_ptr, jr_exif_ptr pExif, void* pIcc,
- size_t icc_size, ultrahdr_metadata_ptr metadata, jr_compressed_ptr dest);
-
- /*
- * This method will tone map a HDR image to an SDR image.
- *
- * @param src pointer to uncompressed HDR image struct. HDR image is expected to be
- * in p010 color format
- * @param dest pointer to store tonemapped SDR image
- */
- status_t toneMap(jr_uncompressed_ptr src, jr_uncompressed_ptr dest);
-
- /*
- * This method will convert a YUV420 image from one YUV encoding to another in-place (eg.
- * Bt.709 to Bt.601 YUV encoding).
- *
- * src_encoding and dest_encoding indicate the encoding via the YUV conversion defined for that
- * gamut. P3 indicates Rec.601, since this is how DataSpace encodes Display-P3 YUV data.
- *
- * @param image the YUV420 image to convert
- * @param src_encoding input YUV encoding
- * @param dest_encoding output YUV encoding
- * @return NO_ERROR if calculation succeeds, error code if error occurs.
- */
- status_t convertYuv(jr_uncompressed_ptr image, ultrahdr_color_gamut src_encoding,
- ultrahdr_color_gamut dest_encoding);
-
- /*
- * This method will check the validity of the input arguments.
- *
- * @param p010_image_ptr uncompressed HDR image in P010 color format
- * @param yuv420_image_ptr pointer to uncompressed SDR image struct. HDR image is expected to
- * be in 420p color format
- * @param hdr_tf transfer function of the HDR image
- * @param dest destination of the compressed JPEGR image. Please note that {@code maxLength}
- * represents the maximum available size of the desitination buffer, and it must be
- * set before calling this method. If the encoded JPEGR size exceeds
- * {@code maxLength}, this method will return {@code ERROR_JPEGR_BUFFER_TOO_SMALL}.
- * @return NO_ERROR if the input args are valid, error code is not valid.
- */
- status_t areInputArgumentsValid(jr_uncompressed_ptr p010_image_ptr,
- jr_uncompressed_ptr yuv420_image_ptr,
- ultrahdr_transfer_function hdr_tf, jr_compressed_ptr dest_ptr);
-
- /*
- * This method will check the validity of the input arguments.
- *
- * @param p010_image_ptr uncompressed HDR image in P010 color format
- * @param yuv420_image_ptr pointer to uncompressed SDR image struct. HDR image is expected to
- * be in 420p color format
- * @param hdr_tf transfer function of the HDR image
- * @param dest destination of the compressed JPEGR image. Please note that {@code maxLength}
- * represents the maximum available size of the destination buffer, and it must be
- * set before calling this method. If the encoded JPEGR size exceeds
- * {@code maxLength}, this method will return {@code ERROR_JPEGR_BUFFER_TOO_SMALL}.
- * @param quality target quality of the JPEG encoding, must be in range of 0-100 where 100 is
- * the highest quality
- * @return NO_ERROR if the input args are valid, error code is not valid.
- */
- status_t areInputArgumentsValid(jr_uncompressed_ptr p010_image_ptr,
- jr_uncompressed_ptr yuv420_image_ptr,
- ultrahdr_transfer_function hdr_tf, jr_compressed_ptr dest,
- int quality);
-};
-} // namespace ultrahdr
-
-#endif // ULTRAHDR_JPEGR_H
diff --git a/include/ultrahdr/ultrahdrcommon.h b/include/ultrahdr/ultrahdrcommon.h
deleted file mode 100644
index 423c8c4..0000000
--- a/include/ultrahdr/ultrahdrcommon.h
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright 2023 The Android Open Source Project
- *
- * 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.
- */
-
-#ifndef ULTRAHDR_ULTRAHDRCOMMON_H
-#define ULTRAHDR_ULTRAHDRCOMMON_H
-
-//#define LOG_NDEBUG 0
-
-#ifdef __ANDROID__
- #include "log/log.h"
-#else
- #ifdef LOG_NDEBUG
- #include <cstdio>
-
- #define ALOGD(...) \
- do { \
- fprintf(stderr, __VA_ARGS__); \
- fprintf(stderr, "\n"); \
- } while (0)
- #define ALOGE(...) \
- do { \
- fprintf(stderr, __VA_ARGS__); \
- fprintf(stderr, "\n"); \
- } while (0)
- #define ALOGI(...) \
- do { \
- fprintf(stdout, __VA_ARGS__); \
- fprintf(stdout, "\n"); \
- } while (0)
- #define ALOGV(...) \
- do { \
- fprintf(stdout, __VA_ARGS__); \
- fprintf(stdout, "\n"); \
- } while (0)
- #define ALOGW(...) \
- do { \
- fprintf(stderr, __VA_ARGS__); \
- fprintf(stderr, "\n"); \
- } while (0)
- #else
- #define ALOGD(...) ((void)0)
- #define ALOGE(...) ((void)0)
- #define ALOGI(...) ((void)0)
- #define ALOGV(...) ((void)0)
- #define ALOGW(...) ((void)0)
- #endif
-#endif
-
-#define ALIGNM(x, m) ((((x) + ((m) - 1)) / (m)) * (m))
-
-#endif // ULTRAHDR_ULTRAHDRCOMMON_H
diff --git a/jpegdecoderhelper.cpp b/jpegdecoderhelper.cpp
deleted file mode 100644
index 53e5b7e..0000000
--- a/jpegdecoderhelper.cpp
+++ /dev/null
@@ -1,567 +0,0 @@
-/*
- * Copyright 2022 The Android Open Source Project
- *
- * 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.
- */
-
-#include <errno.h>
-#include <setjmp.h>
-
-#include <cstring>
-
-#include "ultrahdr/ultrahdrcommon.h"
-#include "ultrahdr/ultrahdr.h"
-#include "ultrahdr/jpegdecoderhelper.h"
-
-using namespace std;
-
-namespace ultrahdr {
-
-const uint32_t kAPP0Marker = JPEG_APP0; // JFIF
-const uint32_t kAPP1Marker = JPEG_APP0 + 1; // EXIF, XMP
-const uint32_t kAPP2Marker = JPEG_APP0 + 2; // ICC
-
-constexpr uint32_t kICCMarkerHeaderSize = 14;
-constexpr uint8_t kICCSig[] = {
- 'I', 'C', 'C', '_', 'P', 'R', 'O', 'F', 'I', 'L', 'E', '\0',
-};
-constexpr uint8_t kXmpNameSpace[] = {
- 'h', 't', 't', 'p', ':', '/', '/', 'n', 's', '.', 'a', 'd', 'o', 'b', 'e',
- '.', 'c', 'o', 'm', '/', 'x', 'a', 'p', '/', '1', '.', '0', '/', '\0',
-};
-constexpr uint8_t kExifIdCode[] = {
- 'E', 'x', 'i', 'f', '\0', '\0',
-};
-
-struct jpegr_source_mgr : jpeg_source_mgr {
- jpegr_source_mgr(const uint8_t* ptr, int len);
- ~jpegr_source_mgr();
-
- const uint8_t* mBufferPtr;
- size_t mBufferLength;
-};
-
-struct jpegrerror_mgr {
- struct jpeg_error_mgr pub;
- jmp_buf setjmp_buffer;
-};
-
-static void jpegr_init_source(j_decompress_ptr cinfo) {
- jpegr_source_mgr* src = static_cast<jpegr_source_mgr*>(cinfo->src);
- src->next_input_byte = static_cast<const JOCTET*>(src->mBufferPtr);
- src->bytes_in_buffer = src->mBufferLength;
-}
-
-static boolean jpegr_fill_input_buffer(j_decompress_ptr /* cinfo */) {
- ALOGE("%s : should not get here", __func__);
- return FALSE;
-}
-
-static void jpegr_skip_input_data(j_decompress_ptr cinfo, long num_bytes) {
- jpegr_source_mgr* src = static_cast<jpegr_source_mgr*>(cinfo->src);
-
- if (num_bytes > static_cast<long>(src->bytes_in_buffer)) {
- ALOGE("jpegr_skip_input_data - num_bytes > (long)src->bytes_in_buffer");
- } else {
- src->next_input_byte += num_bytes;
- src->bytes_in_buffer -= num_bytes;
- }
-}
-
-static void jpegr_term_source(j_decompress_ptr /*cinfo*/) {}
-
-jpegr_source_mgr::jpegr_source_mgr(const uint8_t* ptr, int len)
- : mBufferPtr(ptr), mBufferLength(len) {
- init_source = jpegr_init_source;
- fill_input_buffer = jpegr_fill_input_buffer;
- skip_input_data = jpegr_skip_input_data;
- resync_to_restart = jpeg_resync_to_restart;
- term_source = jpegr_term_source;
-}
-
-jpegr_source_mgr::~jpegr_source_mgr() {}
-
-static void jpegrerror_exit(j_common_ptr cinfo) {
- jpegrerror_mgr* err = reinterpret_cast<jpegrerror_mgr*>(cinfo->err);
- longjmp(err->setjmp_buffer, 1);
-}
-
-static void output_message(j_common_ptr cinfo) {
- char buffer[JMSG_LENGTH_MAX];
-
- /* Create the message */
- (*cinfo->err->format_message)(cinfo, buffer);
- ALOGE("%s\n", buffer);
-}
-
-JpegDecoderHelper::JpegDecoderHelper() {}
-
-JpegDecoderHelper::~JpegDecoderHelper() {}
-
-bool JpegDecoderHelper::decompressImage(const void* image, int length, bool decodeToRGBA) {
- if (image == nullptr || length <= 0) {
- ALOGE("Image size can not be handled: %d", length);
- return false;
- }
- mResultBuffer.clear();
- mXMPBuffer.clear();
- return decode(image, length, decodeToRGBA);
-}
-
-void* JpegDecoderHelper::getDecompressedImagePtr() {
- return mResultBuffer.data();
-}
-
-size_t JpegDecoderHelper::getDecompressedImageSize() {
- return mResultBuffer.size();
-}
-
-void* JpegDecoderHelper::getXMPPtr() {
- return mXMPBuffer.data();
-}
-
-size_t JpegDecoderHelper::getXMPSize() {
- return mXMPBuffer.size();
-}
-
-void* JpegDecoderHelper::getEXIFPtr() {
- return mEXIFBuffer.data();
-}
-
-size_t JpegDecoderHelper::getEXIFSize() {
- return mEXIFBuffer.size();
-}
-
-void* JpegDecoderHelper::getICCPtr() {
- return mICCBuffer.data();
-}
-
-size_t JpegDecoderHelper::getICCSize() {
- return mICCBuffer.size();
-}
-
-size_t JpegDecoderHelper::getDecompressedImageWidth() {
- return mWidth;
-}
-
-size_t JpegDecoderHelper::getDecompressedImageHeight() {
- return mHeight;
-}
-
-// Here we only handle the first EXIF package, and in theary EXIF (or JFIF) must be the first
-// in the image file.
-// We assume that all packages are starting with two bytes marker (eg FF E1 for EXIF package),
-// two bytes of package length which is stored in marker->original_length, and the real data
-// which is stored in marker->data.
-bool JpegDecoderHelper::extractEXIF(const void* image, int length) {
- jpeg_decompress_struct cinfo;
- jpegr_source_mgr mgr(static_cast<const uint8_t*>(image), length);
- jpegrerror_mgr myerr;
-
- cinfo.err = jpeg_std_error(&myerr.pub);
- myerr.pub.error_exit = jpegrerror_exit;
- myerr.pub.output_message = output_message;
-
- if (setjmp(myerr.setjmp_buffer)) {
- jpeg_destroy_decompress(&cinfo);
- return false;
- }
- jpeg_create_decompress(&cinfo);
-
- jpeg_save_markers(&cinfo, kAPP0Marker, 0xFFFF);
- jpeg_save_markers(&cinfo, kAPP1Marker, 0xFFFF);
-
- cinfo.src = &mgr;
- jpeg_read_header(&cinfo, TRUE);
-
- size_t pos = 2; // position after SOI
- for (jpeg_marker_struct* marker = cinfo.marker_list;
- marker;
- marker = marker->next) {
-
- pos += 4;
- pos += marker->original_length;
-
- if (marker->marker != kAPP1Marker) {
- continue;
- }
-
- const unsigned int len = marker->data_length;
-
- if (len > sizeof(kExifIdCode) &&
- !memcmp(marker->data, kExifIdCode, sizeof(kExifIdCode))) {
- mEXIFBuffer.resize(len, 0);
- memcpy(static_cast<void*>(mEXIFBuffer.data()), marker->data, len);
- mExifPos = pos - marker->original_length;
- break;
- }
- }
-
- jpeg_destroy_decompress(&cinfo);
- return true;
-}
-
-bool JpegDecoderHelper::decode(const void* image, int length, bool decodeToRGBA) {
- bool status = true;
- jpeg_decompress_struct cinfo;
- jpegrerror_mgr myerr;
- cinfo.err = jpeg_std_error(&myerr.pub);
- myerr.pub.error_exit = jpegrerror_exit;
- myerr.pub.output_message = output_message;
-
- if (setjmp(myerr.setjmp_buffer)) {
- jpeg_destroy_decompress(&cinfo);
- return false;
- }
-
- jpeg_create_decompress(&cinfo);
-
- jpeg_save_markers(&cinfo, kAPP0Marker, 0xFFFF);
- jpeg_save_markers(&cinfo, kAPP1Marker, 0xFFFF);
- jpeg_save_markers(&cinfo, kAPP2Marker, 0xFFFF);
-
- jpegr_source_mgr mgr(static_cast<const uint8_t*>(image), length);
- cinfo.src = &mgr;
- if (jpeg_read_header(&cinfo, TRUE) != JPEG_HEADER_OK) {
- jpeg_destroy_decompress(&cinfo);
- return false;
- }
-
- // Save XMP data, EXIF data, and ICC data.
- // Here we only handle the first XMP / EXIF / ICC package.
- // We assume that all packages are starting with two bytes marker (eg FF E1 for EXIF package),
- // two bytes of package length which is stored in marker->original_length, and the real data
- // which is stored in marker->data.
- bool exifAppears = false;
- bool xmpAppears = false;
- bool iccAppears = false;
- size_t pos = 2; // position after SOI
- for (jpeg_marker_struct* marker = cinfo.marker_list;
- marker && !(exifAppears && xmpAppears && iccAppears);
- marker = marker->next) {
- pos += 4;
- pos += marker->original_length;
- if (marker->marker != kAPP1Marker && marker->marker != kAPP2Marker) {
- continue;
- }
- const unsigned int len = marker->data_length;
- if (!xmpAppears &&
- len > sizeof(kXmpNameSpace) &&
- !memcmp(marker->data, kXmpNameSpace, sizeof(kXmpNameSpace))) {
- mXMPBuffer.resize(len+1, 0);
- memcpy(static_cast<void*>(mXMPBuffer.data()), marker->data, len);
- xmpAppears = true;
- } else if (!exifAppears &&
- len > sizeof(kExifIdCode) &&
- !memcmp(marker->data, kExifIdCode, sizeof(kExifIdCode))) {
- mEXIFBuffer.resize(len, 0);
- memcpy(static_cast<void*>(mEXIFBuffer.data()), marker->data, len);
- exifAppears = true;
- mExifPos = pos - marker->original_length;
- } else if (!iccAppears &&
- len > sizeof(kICCSig) &&
- !memcmp(marker->data, kICCSig, sizeof(kICCSig))) {
- mICCBuffer.resize(len, 0);
- memcpy(static_cast<void*>(mICCBuffer.data()), marker->data, len);
- iccAppears = true;
- }
- }
-
- mWidth = cinfo.image_width;
- mHeight = cinfo.image_height;
- if (mWidth > kMaxWidth || mHeight > kMaxHeight) {
- status = false;
- goto CleanUp;
- }
-
- if (decodeToRGBA) {
- // The primary image is expected to be yuv420 sampling
- if (cinfo.jpeg_color_space != JCS_YCbCr) {
- status = false;
- ALOGE("%s: decodeToRGBA unexpected jpeg color space ", __func__);
- goto CleanUp;
- }
- if (cinfo.comp_info[0].h_samp_factor != 2 || cinfo.comp_info[0].v_samp_factor != 2 ||
- cinfo.comp_info[1].h_samp_factor != 1 || cinfo.comp_info[1].v_samp_factor != 1 ||
- cinfo.comp_info[2].h_samp_factor != 1 || cinfo.comp_info[2].v_samp_factor != 1) {
- status = false;
- ALOGE("%s: decodeToRGBA unexpected primary image sub-sampling", __func__);
- goto CleanUp;
- }
- // 4 bytes per pixel
- mResultBuffer.resize(cinfo.image_width * cinfo.image_height * 4);
- cinfo.out_color_space = JCS_EXT_RGBA;
- } else {
- if (cinfo.jpeg_color_space == JCS_YCbCr) {
- if (cinfo.comp_info[0].h_samp_factor != 2 || cinfo.comp_info[0].v_samp_factor != 2 ||
- cinfo.comp_info[1].h_samp_factor != 1 || cinfo.comp_info[1].v_samp_factor != 1 ||
- cinfo.comp_info[2].h_samp_factor != 1 || cinfo.comp_info[2].v_samp_factor != 1) {
- status = false;
- ALOGE("%s: decoding to YUV only supports 4:2:0 subsampling", __func__);
- goto CleanUp;
- }
- mResultBuffer.resize(cinfo.image_width * cinfo.image_height * 3 / 2, 0);
- } else if (cinfo.jpeg_color_space == JCS_GRAYSCALE) {
- mResultBuffer.resize(cinfo.image_width * cinfo.image_height, 0);
- } else {
- status = false;
- ALOGE("%s: decodeToYUV unexpected jpeg color space", __func__);
- goto CleanUp;
- }
- cinfo.out_color_space = cinfo.jpeg_color_space;
- cinfo.raw_data_out = TRUE;
- }
-
- cinfo.dct_method = JDCT_ISLOW;
- jpeg_start_decompress(&cinfo);
- if (!decompress(&cinfo, static_cast<const uint8_t*>(mResultBuffer.data()),
- cinfo.jpeg_color_space == JCS_GRAYSCALE)) {
- status = false;
- goto CleanUp;
- }
-
-CleanUp:
- jpeg_finish_decompress(&cinfo);
- jpeg_destroy_decompress(&cinfo);
-
- return status;
-}
-
-bool JpegDecoderHelper::decompress(jpeg_decompress_struct* cinfo, const uint8_t* dest,
- bool isSingleChannel) {
- return isSingleChannel
- ? decompressSingleChannel(cinfo, dest)
- : ((cinfo->out_color_space == JCS_EXT_RGBA) ? decompressRGBA(cinfo, dest)
- : decompressYUV(cinfo, dest));
-}
-
-bool JpegDecoderHelper::getCompressedImageParameters(const void* image, int length, size_t* pWidth,
- size_t* pHeight, std::vector<uint8_t>* iccData,
- std::vector<uint8_t>* exifData) {
- jpeg_decompress_struct cinfo;
- jpegrerror_mgr myerr;
- cinfo.err = jpeg_std_error(&myerr.pub);
- myerr.pub.error_exit = jpegrerror_exit;
- myerr.pub.output_message = output_message;
-
- if (setjmp(myerr.setjmp_buffer)) {
- jpeg_destroy_decompress(&cinfo);
- return false;
- }
- jpeg_create_decompress(&cinfo);
-
- jpeg_save_markers(&cinfo, kAPP1Marker, 0xFFFF);
- jpeg_save_markers(&cinfo, kAPP2Marker, 0xFFFF);
-
- jpegr_source_mgr mgr(static_cast<const uint8_t*>(image), length);
- cinfo.src = &mgr;
- if (jpeg_read_header(&cinfo, TRUE) != JPEG_HEADER_OK) {
- jpeg_destroy_decompress(&cinfo);
- return false;
- }
-
- if (pWidth != nullptr) {
- *pWidth = cinfo.image_width;
- }
- if (pHeight != nullptr) {
- *pHeight = cinfo.image_height;
- }
-
- if (iccData != nullptr) {
- for (jpeg_marker_struct* marker = cinfo.marker_list; marker; marker = marker->next) {
- if (marker->marker != kAPP2Marker) {
- continue;
- }
- if (marker->data_length <= kICCMarkerHeaderSize ||
- memcmp(marker->data, kICCSig, sizeof(kICCSig)) != 0) {
- continue;
- }
-
- iccData->insert(iccData->end(), marker->data, marker->data + marker->data_length);
- }
- }
-
- if (exifData != nullptr) {
- bool exifAppears = false;
- for (jpeg_marker_struct* marker = cinfo.marker_list; marker && !exifAppears;
- marker = marker->next) {
- if (marker->marker != kAPP1Marker) {
- continue;
- }
-
- const unsigned int len = marker->data_length;
- if (len >= sizeof(kExifIdCode) &&
- !memcmp(marker->data, kExifIdCode, sizeof(kExifIdCode))) {
- exifData->resize(len, 0);
- memcpy(static_cast<void*>(exifData->data()), marker->data, len);
- exifAppears = true;
- }
- }
- }
-
- jpeg_destroy_decompress(&cinfo);
- return true;
-}
-
-bool JpegDecoderHelper::decompressRGBA(jpeg_decompress_struct* cinfo, const uint8_t* dest) {
- JSAMPLE* out = (JSAMPLE*)dest;
-
- while (cinfo->output_scanline < cinfo->image_height) {
- if (1 != jpeg_read_scanlines(cinfo, &out, 1)) return false;
- out += cinfo->image_width * 4;
- }
- return true;
-}
-
-bool JpegDecoderHelper::decompressYUV(jpeg_decompress_struct* cinfo, const uint8_t* dest) {
- size_t luma_plane_size = cinfo->image_width * cinfo->image_height;
- size_t chroma_plane_size = luma_plane_size / 4;
- uint8_t* y_plane = const_cast<uint8_t*>(dest);
- uint8_t* u_plane = const_cast<uint8_t*>(dest + luma_plane_size);
- uint8_t* v_plane = const_cast<uint8_t*>(dest + luma_plane_size + chroma_plane_size);
-
- const size_t aligned_width = ALIGNM(cinfo->image_width, kCompressBatchSize);
- const bool is_width_aligned = (aligned_width == cinfo->image_width);
- uint8_t* y_plane_intrm = nullptr;
- uint8_t* u_plane_intrm = nullptr;
- uint8_t* v_plane_intrm = nullptr;
-
- JSAMPROW y[kCompressBatchSize];
- JSAMPROW cb[kCompressBatchSize / 2];
- JSAMPROW cr[kCompressBatchSize / 2];
- JSAMPARRAY planes[3]{y, cb, cr};
- JSAMPROW y_intrm[kCompressBatchSize];
- JSAMPROW cb_intrm[kCompressBatchSize / 2];
- JSAMPROW cr_intrm[kCompressBatchSize / 2];
- JSAMPARRAY planes_intrm[3]{y_intrm, cb_intrm, cr_intrm};
-
- if (cinfo->image_height % kCompressBatchSize != 0) {
- mEmpty = std::make_unique<uint8_t[]>(aligned_width);
- }
-
- if (!is_width_aligned) {
- size_t mcu_row_size = aligned_width * kCompressBatchSize * 3 / 2;
- mBufferIntermediate = std::make_unique<uint8_t[]>(mcu_row_size);
- y_plane_intrm = mBufferIntermediate.get();
- u_plane_intrm = y_plane_intrm + (aligned_width * kCompressBatchSize);
- v_plane_intrm = u_plane_intrm + (aligned_width * kCompressBatchSize) / 4;
- for (int i = 0; i < kCompressBatchSize; ++i) {
- y_intrm[i] = y_plane_intrm + i * aligned_width;
- }
- for (int i = 0; i < kCompressBatchSize / 2; ++i) {
- int offset_intrm = i * (aligned_width / 2);
- cb_intrm[i] = u_plane_intrm + offset_intrm;
- cr_intrm[i] = v_plane_intrm + offset_intrm;
- }
- }
-
- while (cinfo->output_scanline < cinfo->image_height) {
- size_t scanline_copy = cinfo->output_scanline;
- for (int i = 0; i < kCompressBatchSize; ++i) {
- size_t scanline = cinfo->output_scanline + i;
- if (scanline < cinfo->image_height) {
- y[i] = y_plane + scanline * cinfo->image_width;
- } else {
- y[i] = mEmpty.get();
- }
- }
- // cb, cr only have half scanlines
- for (int i = 0; i < kCompressBatchSize / 2; ++i) {
- size_t scanline = cinfo->output_scanline / 2 + i;
- if (scanline < cinfo->image_height / 2) {
- int offset = scanline * (cinfo->image_width / 2);
- cb[i] = u_plane + offset;
- cr[i] = v_plane + offset;
- } else {
- cb[i] = cr[i] = mEmpty.get();
- }
- }
-
- int processed = jpeg_read_raw_data(cinfo, is_width_aligned ? planes : planes_intrm,
- kCompressBatchSize);
- if (processed != kCompressBatchSize) {
- ALOGE("Number of processed lines does not equal input lines.");
- return false;
- }
- if (!is_width_aligned) {
- for (int i = 0; i < kCompressBatchSize; ++i) {
- if (scanline_copy + i < cinfo->image_height) {
- memcpy(y[i], y_intrm[i], cinfo->image_width);
- }
- }
- for (int i = 0; i < kCompressBatchSize / 2; ++i) {
- if (((scanline_copy / 2) + i) < (cinfo->image_height / 2)) {
- memcpy(cb[i], cb_intrm[i], cinfo->image_width / 2);
- memcpy(cr[i], cr_intrm[i], cinfo->image_width / 2);
- }
- }
- }
- }
- return true;
-}
-
-bool JpegDecoderHelper::decompressSingleChannel(jpeg_decompress_struct* cinfo,
- const uint8_t* dest) {
- uint8_t* y_plane = const_cast<uint8_t*>(dest);
- uint8_t* y_plane_intrm = nullptr;
-
- const size_t aligned_width = ALIGNM(cinfo->image_width, kCompressBatchSize);
- const bool is_width_aligned = (aligned_width == cinfo->image_width);
-
- JSAMPROW y[kCompressBatchSize];
- JSAMPARRAY planes[1]{y};
- JSAMPROW y_intrm[kCompressBatchSize];
- JSAMPARRAY planes_intrm[1]{y_intrm};
-
- if (cinfo->image_height % kCompressBatchSize != 0) {
- mEmpty = std::make_unique<uint8_t[]>(aligned_width);
- }
-
- if (!is_width_aligned) {
- size_t mcu_row_size = aligned_width * kCompressBatchSize;
- mBufferIntermediate = std::make_unique<uint8_t[]>(mcu_row_size);
- y_plane_intrm = mBufferIntermediate.get();
- for (int i = 0; i < kCompressBatchSize; ++i) {
- y_intrm[i] = y_plane_intrm + i * aligned_width;
- }
- }
-
- while (cinfo->output_scanline < cinfo->image_height) {
- size_t scanline_copy = cinfo->output_scanline;
- for (int i = 0; i < kCompressBatchSize; ++i) {
- size_t scanline = cinfo->output_scanline + i;
- if (scanline < cinfo->image_height) {
- y[i] = y_plane + scanline * cinfo->image_width;
- } else {
- y[i] = mEmpty.get();
- }
- }
-
- int processed = jpeg_read_raw_data(cinfo, is_width_aligned ? planes : planes_intrm,
- kCompressBatchSize);
- if (processed != kCompressBatchSize / 2) {
- ALOGE("Number of processed lines does not equal input lines.");
- return false;
- }
- if (!is_width_aligned) {
- for (int i = 0; i < kCompressBatchSize; ++i) {
- if (scanline_copy + i < cinfo->image_height) {
- memcpy(y[i], y_intrm[i], cinfo->image_width);
- }
- }
- }
- }
- return true;
-}
-
-} // namespace ultrahdr
diff --git a/jpegencoderhelper.cpp b/jpegencoderhelper.cpp
deleted file mode 100644
index 3b55c43..0000000
--- a/jpegencoderhelper.cpp
+++ /dev/null
@@ -1,294 +0,0 @@
-/*
- * Copyright 2022 The Android Open Source Project
- *
- * 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.
- */
-
-#include <cstring>
-#include <memory>
-#include <string>
-
-#include "ultrahdr/ultrahdrcommon.h"
-#include "ultrahdr/ultrahdr.h"
-#include "ultrahdr/jpegencoderhelper.h"
-
-namespace ultrahdr {
-
-// The destination manager that can access |mResultBuffer| in JpegEncoderHelper.
-struct destination_mgr {
- struct jpeg_destination_mgr mgr;
- JpegEncoderHelper* encoder;
-};
-
-JpegEncoderHelper::JpegEncoderHelper() {}
-
-JpegEncoderHelper::~JpegEncoderHelper() {}
-
-bool JpegEncoderHelper::compressImage(const uint8_t* yBuffer, const uint8_t* uvBuffer, int width,
- int height, int lumaStride, int chromaStride, int quality,
- const void* iccBuffer, unsigned int iccSize) {
- mResultBuffer.clear();
- if (!encode(yBuffer, uvBuffer, width, height, lumaStride, chromaStride, quality, iccBuffer,
- iccSize)) {
- return false;
- }
- ALOGV("Compressed JPEG: %d[%dx%d] -> %zu bytes", (width * height * 12) / 8, width, height,
- mResultBuffer.size());
- return true;
-}
-
-void* JpegEncoderHelper::getCompressedImagePtr() {
- return mResultBuffer.data();
-}
-
-size_t JpegEncoderHelper::getCompressedImageSize() {
- return mResultBuffer.size();
-}
-
-void JpegEncoderHelper::initDestination(j_compress_ptr cinfo) {
- destination_mgr* dest = reinterpret_cast<destination_mgr*>(cinfo->dest);
- std::vector<JOCTET>& buffer = dest->encoder->mResultBuffer;
- buffer.resize(kBlockSize);
- dest->mgr.next_output_byte = &buffer[0];
- dest->mgr.free_in_buffer = buffer.size();
-}
-
-boolean JpegEncoderHelper::emptyOutputBuffer(j_compress_ptr cinfo) {
- destination_mgr* dest = reinterpret_cast<destination_mgr*>(cinfo->dest);
- std::vector<JOCTET>& buffer = dest->encoder->mResultBuffer;
- size_t oldsize = buffer.size();
- buffer.resize(oldsize + kBlockSize);
- dest->mgr.next_output_byte = &buffer[oldsize];
- dest->mgr.free_in_buffer = kBlockSize;
- return true;
-}
-
-void JpegEncoderHelper::terminateDestination(j_compress_ptr cinfo) {
- destination_mgr* dest = reinterpret_cast<destination_mgr*>(cinfo->dest);
- std::vector<JOCTET>& buffer = dest->encoder->mResultBuffer;
- buffer.resize(buffer.size() - dest->mgr.free_in_buffer);
-}
-
-void JpegEncoderHelper::outputErrorMessage(j_common_ptr cinfo) {
- char buffer[JMSG_LENGTH_MAX];
-
- /* Create the message */
- (*cinfo->err->format_message)(cinfo, buffer);
- ALOGE("%s\n", buffer);
-}
-
-bool JpegEncoderHelper::encode(const uint8_t* yBuffer, const uint8_t* uvBuffer, int width,
- int height, int lumaStride, int chromaStride, int quality,
- const void* iccBuffer, unsigned int iccSize) {
- jpeg_compress_struct cinfo;
- jpeg_error_mgr jerr;
-
- cinfo.err = jpeg_std_error(&jerr);
- cinfo.err->output_message = &outputErrorMessage;
- jpeg_create_compress(&cinfo);
- setJpegDestination(&cinfo);
- setJpegCompressStruct(width, height, quality, &cinfo, uvBuffer == nullptr);
- jpeg_start_compress(&cinfo, TRUE);
- if (iccBuffer != nullptr && iccSize > 0) {
- jpeg_write_marker(&cinfo, JPEG_APP0 + 2, static_cast<const JOCTET*>(iccBuffer), iccSize);
- }
- bool status = cinfo.num_components == 1
- ? compressY(&cinfo, yBuffer, lumaStride)
- : compressYuv(&cinfo, yBuffer, uvBuffer, lumaStride, chromaStride);
- jpeg_finish_compress(&cinfo);
- jpeg_destroy_compress(&cinfo);
-
- return status;
-}
-
-void JpegEncoderHelper::setJpegDestination(jpeg_compress_struct* cinfo) {
- destination_mgr* dest = static_cast<struct destination_mgr*>(
- (*cinfo->mem->alloc_small)((j_common_ptr)cinfo, JPOOL_PERMANENT,
- sizeof(destination_mgr)));
- dest->encoder = this;
- dest->mgr.init_destination = &initDestination;
- dest->mgr.empty_output_buffer = &emptyOutputBuffer;
- dest->mgr.term_destination = &terminateDestination;
- cinfo->dest = reinterpret_cast<struct jpeg_destination_mgr*>(dest);
-}
-
-void JpegEncoderHelper::setJpegCompressStruct(int width, int height, int quality,
- jpeg_compress_struct* cinfo, bool isSingleChannel) {
- cinfo->image_width = width;
- cinfo->image_height = height;
- cinfo->input_components = isSingleChannel ? 1 : 3;
- cinfo->in_color_space = isSingleChannel ? JCS_GRAYSCALE : JCS_YCbCr;
- jpeg_set_defaults(cinfo);
- jpeg_set_quality(cinfo, quality, TRUE);
- cinfo->raw_data_in = TRUE;
- cinfo->dct_method = JDCT_ISLOW;
- cinfo->comp_info[0].h_samp_factor = cinfo->in_color_space == JCS_GRAYSCALE ? 1 : 2;
- cinfo->comp_info[0].v_samp_factor = cinfo->in_color_space == JCS_GRAYSCALE ? 1 : 2;
- for (int i = 1; i < cinfo->num_components; i++) {
- cinfo->comp_info[i].h_samp_factor = 1;
- cinfo->comp_info[i].v_samp_factor = 1;
- }
-}
-
-bool JpegEncoderHelper::compressYuv(jpeg_compress_struct* cinfo, const uint8_t* yBuffer,
- const uint8_t* uvBuffer, int lumaStride, int chromaStride) {
- size_t chroma_plane_size = chromaStride * cinfo->image_height / 2;
- uint8_t* y_plane = const_cast<uint8_t*>(yBuffer);
- uint8_t* u_plane = const_cast<uint8_t*>(uvBuffer);
- uint8_t* v_plane = const_cast<uint8_t*>(u_plane + chroma_plane_size);
-
- const int aligned_width = ALIGNM(cinfo->image_width, kCompressBatchSize);
- const bool need_luma_padding = (lumaStride < aligned_width);
- const int aligned_chroma_width = ALIGNM(cinfo->image_width / 2, kCompressBatchSize / 2);
- const bool need_chroma_padding = (chromaStride < aligned_chroma_width);
-
- std::unique_ptr<uint8_t[]> empty = nullptr;
- std::unique_ptr<uint8_t[]> y_mcu_row = nullptr;
- std::unique_ptr<uint8_t[]> cb_mcu_row = nullptr;
- std::unique_ptr<uint8_t[]> cr_mcu_row = nullptr;
- uint8_t* y_mcu_row_ptr = nullptr;
- uint8_t* cb_mcu_row_ptr = nullptr;
- uint8_t* cr_mcu_row_ptr = nullptr;
-
- JSAMPROW y[kCompressBatchSize];
- JSAMPROW cb[kCompressBatchSize / 2];
- JSAMPROW cr[kCompressBatchSize / 2];
- JSAMPARRAY planes[3]{y, cb, cr};
-
- if (cinfo->image_height % kCompressBatchSize != 0) {
- empty = std::make_unique<uint8_t[]>(aligned_width);
- memset(empty.get(), 0, aligned_width);
- }
-
- if (need_luma_padding) {
- size_t mcu_row_size = aligned_width * kCompressBatchSize;
- y_mcu_row = std::make_unique<uint8_t[]>(mcu_row_size);
- y_mcu_row_ptr = y_mcu_row.get();
- uint8_t* tmp = y_mcu_row_ptr;
- for (int i = 0; i < kCompressBatchSize; ++i, tmp += aligned_width) {
- memset(tmp + cinfo->image_width, 0, aligned_width - cinfo->image_width);
- }
- }
-
- if (need_chroma_padding) {
- size_t mcu_row_size = aligned_chroma_width * kCompressBatchSize / 2;
- cb_mcu_row = std::make_unique<uint8_t[]>(mcu_row_size);
- cb_mcu_row_ptr = cb_mcu_row.get();
- cr_mcu_row = std::make_unique<uint8_t[]>(mcu_row_size);
- cr_mcu_row_ptr = cr_mcu_row.get();
- uint8_t* tmp1 = cb_mcu_row_ptr;
- uint8_t* tmp2 = cr_mcu_row_ptr;
- for (int i = 0; i < kCompressBatchSize / 2;
- ++i, tmp1 += aligned_chroma_width, tmp2 += aligned_chroma_width) {
- memset(tmp1 + cinfo->image_width / 2, 0,
- aligned_chroma_width - (cinfo->image_width / 2));
- memset(tmp2 + cinfo->image_width / 2, 0,
- aligned_chroma_width - (cinfo->image_width / 2));
- }
- }
-
- while (cinfo->next_scanline < cinfo->image_height) {
- for (int i = 0; i < kCompressBatchSize; ++i) {
- size_t scanline = cinfo->next_scanline + i;
- if (scanline < cinfo->image_height) {
- y[i] = y_plane + scanline * lumaStride;
- if (need_luma_padding) {
- uint8_t* tmp = y_mcu_row_ptr + i * aligned_width;
- memcpy(tmp, y[i], cinfo->image_width);
- y[i] = tmp;
- }
- } else {
- y[i] = empty.get();
- }
- }
- // cb, cr only have half scanlines
- for (int i = 0; i < kCompressBatchSize / 2; ++i) {
- size_t scanline = cinfo->next_scanline / 2 + i;
- if (scanline < cinfo->image_height / 2) {
- int offset = scanline * chromaStride;
- cb[i] = u_plane + offset;
- cr[i] = v_plane + offset;
- if (need_chroma_padding) {
- uint8_t* tmp = cb_mcu_row_ptr + i * aligned_chroma_width;
- memcpy(tmp, cb[i], cinfo->image_width / 2);
- cb[i] = tmp;
- tmp = cr_mcu_row_ptr + i * aligned_chroma_width;
- memcpy(tmp, cr[i], cinfo->image_width / 2);
- cr[i] = tmp;
- }
- } else {
- cb[i] = cr[i] = empty.get();
- }
- }
- int processed = jpeg_write_raw_data(cinfo, planes, kCompressBatchSize);
- if (processed != kCompressBatchSize) {
- ALOGE("Number of processed lines does not equal input lines.");
- return false;
- }
- }
- return true;
-}
-
-bool JpegEncoderHelper::compressY(jpeg_compress_struct* cinfo, const uint8_t* yBuffer,
- int lumaStride) {
- uint8_t* y_plane = const_cast<uint8_t*>(yBuffer);
-
- const int aligned_luma_width = ALIGNM(cinfo->image_width, kCompressBatchSize);
- const bool need_luma_padding = (lumaStride < aligned_luma_width);
-
- std::unique_ptr<uint8_t[]> empty = nullptr;
- std::unique_ptr<uint8_t[]> y_mcu_row = nullptr;
- uint8_t* y_mcu_row_ptr = nullptr;
-
- JSAMPROW y[kCompressBatchSize];
- JSAMPARRAY planes[1]{y};
-
- if (cinfo->image_height % kCompressBatchSize != 0) {
- empty = std::make_unique<uint8_t[]>(aligned_luma_width);
- memset(empty.get(), 0, aligned_luma_width);
- }
-
- if (need_luma_padding) {
- size_t mcu_row_size = aligned_luma_width * kCompressBatchSize;
- y_mcu_row = std::make_unique<uint8_t[]>(mcu_row_size);
- y_mcu_row_ptr = y_mcu_row.get();
- uint8_t* tmp = y_mcu_row_ptr;
- for (int i = 0; i < kCompressBatchSize; ++i, tmp += aligned_luma_width) {
- memset(tmp + cinfo->image_width, 0, aligned_luma_width - cinfo->image_width);
- }
- }
-
- while (cinfo->next_scanline < cinfo->image_height) {
- for (int i = 0; i < kCompressBatchSize; ++i) {
- size_t scanline = cinfo->next_scanline + i;
- if (scanline < cinfo->image_height) {
- y[i] = y_plane + scanline * lumaStride;
- if (need_luma_padding) {
- uint8_t* tmp = y_mcu_row_ptr + i * aligned_luma_width;
- memcpy(tmp, y[i], cinfo->image_width);
- y[i] = tmp;
- }
- } else {
- y[i] = empty.get();
- }
- }
- int processed = jpeg_write_raw_data(cinfo, planes, kCompressBatchSize);
- if (processed != kCompressBatchSize / 2) {
- ALOGE("Number of processed lines does not equal input lines.");
- return false;
- }
- }
- return true;
-}
-
-} // namespace ultrahdr
diff --git a/jpegrutils.cpp b/jpegrutils.cpp
deleted file mode 100644
index 8abf249..0000000
--- a/jpegrutils.cpp
+++ /dev/null
@@ -1,601 +0,0 @@
-/*
- * Copyright 2022 The Android Open Source Project
- *
- * 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.
- */
-
-#include <algorithm>
-#include <cmath>
-
-#include "ultrahdr/ultrahdrcommon.h"
-#include "ultrahdr/jpegr.h"
-#include "ultrahdr/jpegrutils.h"
-
-#include "image_io/xml/xml_reader.h"
-#include "image_io/xml/xml_writer.h"
-#include "image_io/base/message_handler.h"
-#include "image_io/xml/xml_element_rules.h"
-#include "image_io/xml/xml_handler.h"
-#include "image_io/xml/xml_rule.h"
-
-using namespace photos_editing_formats::image_io;
-using namespace std;
-
-namespace ultrahdr {
-/*
- * Helper function used for generating XMP metadata.
- *
- * @param prefix The prefix part of the name.
- * @param suffix The suffix part of the name.
- * @return A name of the form "prefix:suffix".
- */
-static inline string Name(const string &prefix, const string &suffix) {
- std::stringstream ss;
- ss << prefix << ":" << suffix;
- return ss.str();
-}
-
-DataStruct::DataStruct(int s) {
- data = malloc(s);
- length = s;
- memset(data, 0, s);
- writePos = 0;
-}
-
-DataStruct::~DataStruct() {
- if (data != nullptr) {
- free(data);
- }
-}
-
-void* DataStruct::getData() {
- return data;
-}
-
-int DataStruct::getLength() {
- return length;
-}
-
-int DataStruct::getBytesWritten() {
- return writePos;
-}
-
-bool DataStruct::write8(uint8_t value) {
- uint8_t v = value;
- return write(&v, 1);
-}
-
-bool DataStruct::write16(uint16_t value) {
- uint16_t v = value;
- return write(&v, 2);
-}
-bool DataStruct::write32(uint32_t value) {
- uint32_t v = value;
- return write(&v, 4);
-}
-
-bool DataStruct::write(const void* src, int size) {
- if (writePos + size > length) {
- ALOGE("Writing out of boundary: write position: %d, size: %d, capacity: %d",
- writePos, size, length);
- return false;
- }
- memcpy((uint8_t*) data + writePos, src, size);
- writePos += size;
- return true;
-}
-
-/*
- * Helper function used for writing data to destination.
- */
-status_t Write(jr_compressed_ptr destination, const void* source, size_t length, int &position) {
- if (position + length > destination->maxLength) {
- return ERROR_JPEGR_BUFFER_TOO_SMALL;
- }
-
- memcpy((uint8_t*)destination->data + sizeof(uint8_t) * position, source, length);
- position += length;
- return NO_ERROR;
-}
-
-// Extremely simple XML Handler - just searches for interesting elements
-class XMPXmlHandler : public XmlHandler {
-public:
-
- XMPXmlHandler() : XmlHandler() {
- state = NotStrarted;
- versionFound = false;
- minContentBoostFound = false;
- maxContentBoostFound = false;
- gammaFound = false;
- offsetSdrFound = false;
- offsetHdrFound = false;
- hdrCapacityMinFound = false;
- hdrCapacityMaxFound = false;
- baseRenditionIsHdrFound = false;
- }
-
- enum ParseState {
- NotStrarted,
- Started,
- Done
- };
-
- virtual DataMatchResult StartElement(const XmlTokenContext& context) {
- string val;
- if (context.BuildTokenValue(&val)) {
- if (!val.compare(containerName)) {
- state = Started;
- } else {
- if (state != Done) {
- state = NotStrarted;
- }
- }
- }
- return context.GetResult();
- }
-
- virtual DataMatchResult FinishElement(const XmlTokenContext& context) {
- if (state == Started) {
- state = Done;
- lastAttributeName = "";
- }
- return context.GetResult();
- }
-
- virtual DataMatchResult AttributeName(const XmlTokenContext& context) {
- string val;
- if (state == Started) {
- if (context.BuildTokenValue(&val)) {
- if (!val.compare(versionAttrName)) {
- lastAttributeName = versionAttrName;
- } else if (!val.compare(maxContentBoostAttrName)) {
- lastAttributeName = maxContentBoostAttrName;
- } else if (!val.compare(minContentBoostAttrName)) {
- lastAttributeName = minContentBoostAttrName;
- } else if (!val.compare(gammaAttrName)) {
- lastAttributeName = gammaAttrName;
- } else if (!val.compare(offsetSdrAttrName)) {
- lastAttributeName = offsetSdrAttrName;
- } else if (!val.compare(offsetHdrAttrName)) {
- lastAttributeName = offsetHdrAttrName;
- } else if (!val.compare(hdrCapacityMinAttrName)) {
- lastAttributeName = hdrCapacityMinAttrName;
- } else if (!val.compare(hdrCapacityMaxAttrName)) {
- lastAttributeName = hdrCapacityMaxAttrName;
- } else if (!val.compare(baseRenditionIsHdrAttrName)) {
- lastAttributeName = baseRenditionIsHdrAttrName;
- } else {
- lastAttributeName = "";
- }
- }
- }
- return context.GetResult();
- }
-
- virtual DataMatchResult AttributeValue(const XmlTokenContext& context) {
- string val;
- if (state == Started) {
- if (context.BuildTokenValue(&val, true)) {
- if (!lastAttributeName.compare(versionAttrName)) {
- versionStr = val;
- versionFound = true;
- } else if (!lastAttributeName.compare(maxContentBoostAttrName)) {
- maxContentBoostStr = val;
- maxContentBoostFound = true;
- } else if (!lastAttributeName.compare(minContentBoostAttrName)) {
- minContentBoostStr = val;
- minContentBoostFound = true;
- } else if (!lastAttributeName.compare(gammaAttrName)) {
- gammaStr = val;
- gammaFound = true;
- } else if (!lastAttributeName.compare(offsetSdrAttrName)) {
- offsetSdrStr = val;
- offsetSdrFound = true;
- } else if (!lastAttributeName.compare(offsetHdrAttrName)) {
- offsetHdrStr = val;
- offsetHdrFound = true;
- } else if (!lastAttributeName.compare(hdrCapacityMinAttrName)) {
- hdrCapacityMinStr = val;
- hdrCapacityMinFound = true;
- } else if (!lastAttributeName.compare(hdrCapacityMaxAttrName)) {
- hdrCapacityMaxStr = val;
- hdrCapacityMaxFound = true;
- } else if (!lastAttributeName.compare(baseRenditionIsHdrAttrName)) {
- baseRenditionIsHdrStr = val;
- baseRenditionIsHdrFound = true;
- }
- }
- }
- return context.GetResult();
- }
-
- bool getVersion(string* version, bool* present) {
- if (state == Done) {
- *version = versionStr;
- *present = versionFound;
- return true;
- } else {
- return false;
- }
- }
-
- bool getMaxContentBoost(float* max_content_boost, bool* present) {
- if (state == Done) {
- *present = maxContentBoostFound;
- stringstream ss(maxContentBoostStr);
- float val;
- if (ss >> val) {
- *max_content_boost = exp2(val);
- return true;
- } else {
- return false;
- }
- } else {
- return false;
- }
- }
-
- bool getMinContentBoost(float* min_content_boost, bool* present) {
- if (state == Done) {
- *present = minContentBoostFound;
- stringstream ss(minContentBoostStr);
- float val;
- if (ss >> val) {
- *min_content_boost = exp2(val);
- return true;
- } else {
- return false;
- }
- } else {
- return false;
- }
- }
-
- bool getGamma(float* gamma, bool* present) {
- if (state == Done) {
- *present = gammaFound;
- stringstream ss(gammaStr);
- float val;
- if (ss >> val) {
- *gamma = val;
- return true;
- } else {
- return false;
- }
- } else {
- return false;
- }
- }
-
-
- bool getOffsetSdr(float* offset_sdr, bool* present) {
- if (state == Done) {
- *present = offsetSdrFound;
- stringstream ss(offsetSdrStr);
- float val;
- if (ss >> val) {
- *offset_sdr = val;
- return true;
- } else {
- return false;
- }
- } else {
- return false;
- }
- }
-
-
- bool getOffsetHdr(float* offset_hdr, bool* present) {
- if (state == Done) {
- *present = offsetHdrFound;
- stringstream ss(offsetHdrStr);
- float val;
- if (ss >> val) {
- *offset_hdr = val;
- return true;
- } else {
- return false;
- }
- } else {
- return false;
- }
- }
-
-
- bool getHdrCapacityMin(float* hdr_capacity_min, bool* present) {
- if (state == Done) {
- *present = hdrCapacityMinFound;
- stringstream ss(hdrCapacityMinStr);
- float val;
- if (ss >> val) {
- *hdr_capacity_min = exp2(val);
- return true;
- } else {
- return false;
- }
- } else {
- return false;
- }
- }
-
-
- bool getHdrCapacityMax(float* hdr_capacity_max, bool* present) {
- if (state == Done) {
- *present = hdrCapacityMaxFound;
- stringstream ss(hdrCapacityMaxStr);
- float val;
- if (ss >> val) {
- *hdr_capacity_max = exp2(val);
- return true;
- } else {
- return false;
- }
- } else {
- return false;
- }
- }
-
-
- bool getBaseRenditionIsHdr(bool* base_rendition_is_hdr, bool* present) {
- if (state == Done) {
- *present = baseRenditionIsHdrFound;
- if (!baseRenditionIsHdrStr.compare("False")) {
- *base_rendition_is_hdr = false;
- return true;
- } else if (!baseRenditionIsHdrStr.compare("True")) {
- *base_rendition_is_hdr = true;
- return true;
- } else {
- return false;
- }
- } else {
- return false;
- }
- }
-
-
-
-private:
- static const string containerName;
-
- static const string versionAttrName;
- string versionStr;
- bool versionFound;
- static const string maxContentBoostAttrName;
- string maxContentBoostStr;
- bool maxContentBoostFound;
- static const string minContentBoostAttrName;
- string minContentBoostStr;
- bool minContentBoostFound;
- static const string gammaAttrName;
- string gammaStr;
- bool gammaFound;
- static const string offsetSdrAttrName;
- string offsetSdrStr;
- bool offsetSdrFound;
- static const string offsetHdrAttrName;
- string offsetHdrStr;
- bool offsetHdrFound;
- static const string hdrCapacityMinAttrName;
- string hdrCapacityMinStr;
- bool hdrCapacityMinFound;
- static const string hdrCapacityMaxAttrName;
- string hdrCapacityMaxStr;
- bool hdrCapacityMaxFound;
- static const string baseRenditionIsHdrAttrName;
- string baseRenditionIsHdrStr;
- bool baseRenditionIsHdrFound;
-
- string lastAttributeName;
- ParseState state;
-};
-
-// GContainer XMP constants - URI and namespace prefix
-const string kContainerUri = "http://ns.google.com/photos/1.0/container/";
-const string kContainerPrefix = "Container";
-
-// GContainer XMP constants - element and attribute names
-const string kConDirectory = Name(kContainerPrefix, "Directory");
-const string kConItem = Name(kContainerPrefix, "Item");
-
-// GContainer XMP constants - names for XMP handlers
-const string XMPXmlHandler::containerName = "rdf:Description";
-// Item XMP constants - URI and namespace prefix
-const string kItemUri = "http://ns.google.com/photos/1.0/container/item/";
-const string kItemPrefix = "Item";
-
-// Item XMP constants - element and attribute names
-const string kItemLength = Name(kItemPrefix, "Length");
-const string kItemMime = Name(kItemPrefix, "Mime");
-const string kItemSemantic = Name(kItemPrefix, "Semantic");
-
-// Item XMP constants - element and attribute values
-const string kSemanticPrimary = "Primary";
-const string kSemanticGainMap = "GainMap";
-const string kMimeImageJpeg = "image/jpeg";
-
-// GainMap XMP constants - URI and namespace prefix
-const string kGainMapUri = "http://ns.adobe.com/hdr-gain-map/1.0/";
-const string kGainMapPrefix = "hdrgm";
-
-// GainMap XMP constants - element and attribute names
-const string kMapVersion = Name(kGainMapPrefix, "Version");
-const string kMapGainMapMin = Name(kGainMapPrefix, "GainMapMin");
-const string kMapGainMapMax = Name(kGainMapPrefix, "GainMapMax");
-const string kMapGamma = Name(kGainMapPrefix, "Gamma");
-const string kMapOffsetSdr = Name(kGainMapPrefix, "OffsetSDR");
-const string kMapOffsetHdr = Name(kGainMapPrefix, "OffsetHDR");
-const string kMapHDRCapacityMin = Name(kGainMapPrefix, "HDRCapacityMin");
-const string kMapHDRCapacityMax = Name(kGainMapPrefix, "HDRCapacityMax");
-const string kMapBaseRenditionIsHDR = Name(kGainMapPrefix, "BaseRenditionIsHDR");
-
-// GainMap XMP constants - names for XMP handlers
-const string XMPXmlHandler::versionAttrName = kMapVersion;
-const string XMPXmlHandler::minContentBoostAttrName = kMapGainMapMin;
-const string XMPXmlHandler::maxContentBoostAttrName = kMapGainMapMax;
-const string XMPXmlHandler::gammaAttrName = kMapGamma;
-const string XMPXmlHandler::offsetSdrAttrName = kMapOffsetSdr;
-const string XMPXmlHandler::offsetHdrAttrName = kMapOffsetHdr;
-const string XMPXmlHandler::hdrCapacityMinAttrName = kMapHDRCapacityMin;
-const string XMPXmlHandler::hdrCapacityMaxAttrName = kMapHDRCapacityMax;
-const string XMPXmlHandler::baseRenditionIsHdrAttrName = kMapBaseRenditionIsHDR;
-
-bool getMetadataFromXMP(uint8_t* xmp_data, size_t xmp_size, ultrahdr_metadata_struct* metadata) {
- string nameSpace = "http://ns.adobe.com/xap/1.0/\0";
-
- if (xmp_size < nameSpace.size()+2) {
- // Data too short
- return false;
- }
-
- if (strncmp(reinterpret_cast<char*>(xmp_data), nameSpace.c_str(), nameSpace.size())) {
- // Not correct namespace
- return false;
- }
-
- // Position the pointers to the start of XMP XML portion
- xmp_data += nameSpace.size()+1;
- xmp_size -= nameSpace.size()+1;
- XMPXmlHandler handler;
-
- // We need to remove tail data until the closing tag. Otherwise parser will throw an error.
- while(xmp_data[xmp_size-1]!='>' && xmp_size > 1) {
- xmp_size--;
- }
-
- string str(reinterpret_cast<const char*>(xmp_data), xmp_size);
- MessageHandler msg_handler;
- unique_ptr<XmlRule> rule(new XmlElementRule);
- XmlReader reader(&handler, &msg_handler);
- reader.StartParse(std::move(rule));
- reader.Parse(str);
- reader.FinishParse();
- if (reader.HasErrors()) {
- // Parse error
- return false;
- }
-
- // Apply default values to any not-present fields, except for Version,
- // maxContentBoost, and hdrCapacityMax, which are required. Return false if
- // we encounter a present field that couldn't be parsed, since this
- // indicates it is invalid (eg. string where there should be a float).
- bool present = false;
- if (!handler.getVersion(&metadata->version, &present) || !present) {
- return false;
- }
- if (!handler.getMaxContentBoost(&metadata->maxContentBoost, &present) || !present) {
- return false;
- }
- if (!handler.getHdrCapacityMax(&metadata->hdrCapacityMax, &present) || !present) {
- return false;
- }
- if (!handler.getMinContentBoost(&metadata->minContentBoost, &present)) {
- if (present) return false;
- metadata->minContentBoost = 1.0f;
- }
- if (!handler.getGamma(&metadata->gamma, &present)) {
- if (present) return false;
- metadata->gamma = 1.0f;
- }
- if (!handler.getOffsetSdr(&metadata->offsetSdr, &present)) {
- if (present) return false;
- metadata->offsetSdr = 1.0f / 64.0f;
- }
- if (!handler.getOffsetHdr(&metadata->offsetHdr, &present)) {
- if (present) return false;
- metadata->offsetHdr = 1.0f / 64.0f;
- }
- if (!handler.getHdrCapacityMin(&metadata->hdrCapacityMin, &present)) {
- if (present) return false;
- metadata->hdrCapacityMin = 1.0f;
- }
-
- bool base_rendition_is_hdr;
- if (!handler.getBaseRenditionIsHdr(&base_rendition_is_hdr, &present)) {
- if (present) return false;
- base_rendition_is_hdr = false;
- }
- if (base_rendition_is_hdr) {
- ALOGE("Base rendition of HDR is not supported!");
- return false;
- }
-
- return true;
-}
-
-string generateXmpForPrimaryImage(int secondary_image_length, ultrahdr_metadata_struct& metadata) {
- const vector<string> kConDirSeq({kConDirectory, string("rdf:Seq")});
- const vector<string> kLiItem({string("rdf:li"), kConItem});
-
- std::stringstream ss;
- photos_editing_formats::image_io::XmlWriter writer(ss);
- writer.StartWritingElement("x:xmpmeta");
- writer.WriteXmlns("x", "adobe:ns:meta/");
- writer.WriteAttributeNameAndValue("x:xmptk", "Adobe XMP Core 5.1.2");
- writer.StartWritingElement("rdf:RDF");
- writer.WriteXmlns("rdf", "http://www.w3.org/1999/02/22-rdf-syntax-ns#");
- writer.StartWritingElement("rdf:Description");
- writer.WriteXmlns(kContainerPrefix, kContainerUri);
- writer.WriteXmlns(kItemPrefix, kItemUri);
- writer.WriteXmlns(kGainMapPrefix, kGainMapUri);
- writer.WriteAttributeNameAndValue(kMapVersion, metadata.version);
-
- writer.StartWritingElements(kConDirSeq);
-
- size_t item_depth = writer.StartWritingElement("rdf:li");
- writer.WriteAttributeNameAndValue("rdf:parseType", "Resource");
- writer.StartWritingElement(kConItem);
- writer.WriteAttributeNameAndValue(kItemSemantic, kSemanticPrimary);
- writer.WriteAttributeNameAndValue(kItemMime, kMimeImageJpeg);
- writer.FinishWritingElementsToDepth(item_depth);
-
- writer.StartWritingElement("rdf:li");
- writer.WriteAttributeNameAndValue("rdf:parseType", "Resource");
- writer.StartWritingElement(kConItem);
- writer.WriteAttributeNameAndValue(kItemSemantic, kSemanticGainMap);
- writer.WriteAttributeNameAndValue(kItemMime, kMimeImageJpeg);
- writer.WriteAttributeNameAndValue(kItemLength, secondary_image_length);
-
- writer.FinishWriting();
-
- return ss.str();
-}
-
-string generateXmpForSecondaryImage(ultrahdr_metadata_struct& metadata) {
- const vector<string> kConDirSeq({kConDirectory, string("rdf:Seq")});
-
- std::stringstream ss;
- photos_editing_formats::image_io::XmlWriter writer(ss);
- writer.StartWritingElement("x:xmpmeta");
- writer.WriteXmlns("x", "adobe:ns:meta/");
- writer.WriteAttributeNameAndValue("x:xmptk", "Adobe XMP Core 5.1.2");
- writer.StartWritingElement("rdf:RDF");
- writer.WriteXmlns("rdf", "http://www.w3.org/1999/02/22-rdf-syntax-ns#");
- writer.StartWritingElement("rdf:Description");
- writer.WriteXmlns(kGainMapPrefix, kGainMapUri);
- writer.WriteAttributeNameAndValue(kMapVersion, metadata.version);
- writer.WriteAttributeNameAndValue(kMapGainMapMin, log2(metadata.minContentBoost));
- writer.WriteAttributeNameAndValue(kMapGainMapMax, log2(metadata.maxContentBoost));
- writer.WriteAttributeNameAndValue(kMapGamma, metadata.gamma);
- writer.WriteAttributeNameAndValue(kMapOffsetSdr, metadata.offsetSdr);
- writer.WriteAttributeNameAndValue(kMapOffsetHdr, metadata.offsetHdr);
- writer.WriteAttributeNameAndValue(kMapHDRCapacityMin, log2(metadata.hdrCapacityMin));
- writer.WriteAttributeNameAndValue(kMapHDRCapacityMax, log2(metadata.hdrCapacityMax));
- writer.WriteAttributeNameAndValue(kMapBaseRenditionIsHDR, "False");
- writer.FinishWriting();
-
- return ss.str();
-}
-
-} // namespace ultrahdr
diff --git a/gainmapmath.cpp b/lib/gainmapmath.cpp
index a0698ba..23791c2 100644
--- a/gainmapmath.cpp
+++ b/lib/gainmapmath.cpp
@@ -14,53 +14,53 @@
* limitations under the License.
*/
-#include "ultrahdr/gainmapmath.h"
+#include "gainmapmath.h"
namespace ultrahdr {
static const std::vector<float> kPqOETF = [] {
- std::vector<float> result;
- for (size_t idx = 0; idx < kPqOETFNumEntries; idx++) {
- float value = static_cast<float>(idx) / static_cast<float>(kPqOETFNumEntries - 1);
- result.push_back(pqOetf(value));
- }
- return result;
+ std::vector<float> result;
+ for (size_t idx = 0; idx < kPqOETFNumEntries; idx++) {
+ float value = static_cast<float>(idx) / static_cast<float>(kPqOETFNumEntries - 1);
+ result.push_back(pqOetf(value));
+ }
+ return result;
}();
static const std::vector<float> kPqInvOETF = [] {
- std::vector<float> result;
- for (size_t idx = 0; idx < kPqInvOETFNumEntries; idx++) {
- float value = static_cast<float>(idx) / static_cast<float>(kPqInvOETFNumEntries - 1);
- result.push_back(pqInvOetf(value));
- }
- return result;
+ std::vector<float> result;
+ for (size_t idx = 0; idx < kPqInvOETFNumEntries; idx++) {
+ float value = static_cast<float>(idx) / static_cast<float>(kPqInvOETFNumEntries - 1);
+ result.push_back(pqInvOetf(value));
+ }
+ return result;
}();
static const std::vector<float> kHlgOETF = [] {
- std::vector<float> result;
- for (size_t idx = 0; idx < kHlgOETFNumEntries; idx++) {
- float value = static_cast<float>(idx) / static_cast<float>(kHlgOETFNumEntries - 1);
- result.push_back(hlgOetf(value));
- }
- return result;
+ std::vector<float> result;
+ for (size_t idx = 0; idx < kHlgOETFNumEntries; idx++) {
+ float value = static_cast<float>(idx) / static_cast<float>(kHlgOETFNumEntries - 1);
+ result.push_back(hlgOetf(value));
+ }
+ return result;
}();
static const std::vector<float> kHlgInvOETF = [] {
- std::vector<float> result;
- for (size_t idx = 0; idx < kHlgInvOETFNumEntries; idx++) {
- float value = static_cast<float>(idx) / static_cast<float>(kHlgInvOETFNumEntries - 1);
- result.push_back(hlgInvOetf(value));
- }
- return result;
+ std::vector<float> result;
+ for (size_t idx = 0; idx < kHlgInvOETFNumEntries; idx++) {
+ float value = static_cast<float>(idx) / static_cast<float>(kHlgInvOETFNumEntries - 1);
+ result.push_back(hlgInvOetf(value));
+ }
+ return result;
}();
static const std::vector<float> kSrgbInvOETF = [] {
- std::vector<float> result;
- for (size_t idx = 0; idx < kSrgbInvOETFNumEntries; idx++) {
- float value = static_cast<float>(idx) / static_cast<float>(kSrgbInvOETFNumEntries - 1);
- result.push_back(srgbInvOetf(value));
- }
- return result;
+ std::vector<float> result;
+ for (size_t idx = 0; idx < kSrgbInvOETFNumEntries; idx++) {
+ float value = static_cast<float>(idx) / static_cast<float>(kSrgbInvOETFNumEntries - 1);
+ result.push_back(srgbInvOetf(value));
+ }
+ return result;
}();
// Use Shepard's method for inverse distance weighting. For more information:
@@ -70,7 +70,7 @@ float ShepardsIDW::euclideanDistance(float x1, float x2, float y1, float y2) {
return sqrt(((y2 - y1) * (y2 - y1)) + (x2 - x1) * (x2 - x1));
}
-void ShepardsIDW::fillShepardsIDW(float *weights, int incR, int incB) {
+void ShepardsIDW::fillShepardsIDW(float* weights, int incR, int incB) {
for (int y = 0; y < mMapScaleFactor; y++) {
for (int x = 0; x < mMapScaleFactor; x++) {
float pos_x = ((float)x) / mMapScaleFactor;
@@ -114,15 +114,13 @@ void ShepardsIDW::fillShepardsIDW(float *weights, int incR, int incB) {
static const float kMaxPixelFloat = 1.0f;
static float clampPixelFloat(float value) {
- return (value < 0.0f) ? 0.0f : (value > kMaxPixelFloat) ? kMaxPixelFloat : value;
+ return (value < 0.0f) ? 0.0f : (value > kMaxPixelFloat) ? kMaxPixelFloat : value;
}
// See IEC 61966-2-1/Amd 1:2003, Equation F.7.
static const float kSrgbR = 0.2126f, kSrgbG = 0.7152f, kSrgbB = 0.0722f;
-float srgbLuminance(Color e) {
- return kSrgbR * e.r + kSrgbG * e.g + kSrgbB * e.b;
-}
+float srgbLuminance(Color e) { return kSrgbR * e.r + kSrgbG * e.g + kSrgbB * e.b; }
// See ITU-R BT.709-6, Section 3.
// Uses the same coefficients for deriving luma signal as
@@ -132,9 +130,7 @@ static const float kSrgbCb = 1.8556f, kSrgbCr = 1.5748f;
Color srgbRgbToYuv(Color e_gamma) {
float y_gamma = srgbLuminance(e_gamma);
- return {{{ y_gamma,
- (e_gamma.b - y_gamma) / kSrgbCb,
- (e_gamma.r - y_gamma) / kSrgbCr }}};
+ return {{{y_gamma, (e_gamma.b - y_gamma) / kSrgbCb, (e_gamma.r - y_gamma) / kSrgbCr}}};
}
// See ITU-R BT.709-6, Section 3.
@@ -144,9 +140,9 @@ static const float kSrgbGCb = kSrgbB * kSrgbCb / kSrgbG;
static const float kSrgbGCr = kSrgbR * kSrgbCr / kSrgbG;
Color srgbYuvToRgb(Color e_gamma) {
- return {{{ clampPixelFloat(e_gamma.y + kSrgbCr * e_gamma.v),
- clampPixelFloat(e_gamma.y - kSrgbGCb * e_gamma.u - kSrgbGCr * e_gamma.v),
- clampPixelFloat(e_gamma.y + kSrgbCb * e_gamma.u) }}};
+ return {{{clampPixelFloat(e_gamma.y + kSrgbCr * e_gamma.v),
+ clampPixelFloat(e_gamma.y - kSrgbGCb * e_gamma.u - kSrgbGCr * e_gamma.v),
+ clampPixelFloat(e_gamma.y + kSrgbCb * e_gamma.u)}}};
}
// See IEC 61966-2-1/Amd 1:2003, Equations F.5 and F.6.
@@ -159,23 +155,19 @@ float srgbInvOetf(float e_gamma) {
}
Color srgbInvOetf(Color e_gamma) {
- return {{{ srgbInvOetf(e_gamma.r),
- srgbInvOetf(e_gamma.g),
- srgbInvOetf(e_gamma.b) }}};
+ return {{{srgbInvOetf(e_gamma.r), srgbInvOetf(e_gamma.g), srgbInvOetf(e_gamma.b)}}};
}
// See IEC 61966-2-1, Equations F.5 and F.6.
float srgbInvOetfLUT(float e_gamma) {
uint32_t value = static_cast<uint32_t>(e_gamma * (kSrgbInvOETFNumEntries - 1) + 0.5);
- //TODO() : Remove once conversion modules have appropriate clamping in place
+ // TODO() : Remove once conversion modules have appropriate clamping in place
value = CLIP3(value, 0, kSrgbInvOETFNumEntries - 1);
return kSrgbInvOETF[value];
}
Color srgbInvOetfLUT(Color e_gamma) {
- return {{{ srgbInvOetfLUT(e_gamma.r),
- srgbInvOetfLUT(e_gamma.g),
- srgbInvOetfLUT(e_gamma.b) }}};
+ return {{{srgbInvOetfLUT(e_gamma.r), srgbInvOetfLUT(e_gamma.g), srgbInvOetfLUT(e_gamma.b)}}};
}
////////////////////////////////////////////////////////////////////////////////
@@ -184,9 +176,7 @@ Color srgbInvOetfLUT(Color e_gamma) {
// See SMPTE EG 432-1, Equation 7-8.
static const float kP3R = 0.20949f, kP3G = 0.72160f, kP3B = 0.06891f;
-float p3Luminance(Color e) {
- return kP3R * e.r + kP3G * e.g + kP3B * e.b;
-}
+float p3Luminance(Color e) { return kP3R * e.r + kP3G * e.g + kP3B * e.b; }
// See ITU-R BT.601-7, Sections 2.5.1 and 2.5.2.
// Unfortunately, calculation of luma signal differs from calculation of
@@ -196,9 +186,7 @@ static const float kP3Cb = 1.772f, kP3Cr = 1.402f;
Color p3RgbToYuv(Color e_gamma) {
float y_gamma = kP3YR * e_gamma.r + kP3YG * e_gamma.g + kP3YB * e_gamma.b;
- return {{{ y_gamma,
- (e_gamma.b - y_gamma) / kP3Cb,
- (e_gamma.r - y_gamma) / kP3Cr }}};
+ return {{{y_gamma, (e_gamma.b - y_gamma) / kP3Cb, (e_gamma.r - y_gamma) / kP3Cr}}};
}
// See ITU-R BT.601-7, Sections 2.5.1 and 2.5.2.
@@ -208,21 +196,18 @@ static const float kP3GCb = kP3YB * kP3Cb / kP3YG;
static const float kP3GCr = kP3YR * kP3Cr / kP3YG;
Color p3YuvToRgb(Color e_gamma) {
- return {{{ clampPixelFloat(e_gamma.y + kP3Cr * e_gamma.v),
- clampPixelFloat(e_gamma.y - kP3GCb * e_gamma.u - kP3GCr * e_gamma.v),
- clampPixelFloat(e_gamma.y + kP3Cb * e_gamma.u) }}};
+ return {{{clampPixelFloat(e_gamma.y + kP3Cr * e_gamma.v),
+ clampPixelFloat(e_gamma.y - kP3GCb * e_gamma.u - kP3GCr * e_gamma.v),
+ clampPixelFloat(e_gamma.y + kP3Cb * e_gamma.u)}}};
}
-
////////////////////////////////////////////////////////////////////////////////
// BT.2100 transformations - according to ITU-R BT.2100-2
// See ITU-R BT.2100-2, Table 5, HLG Reference OOTF
static const float kBt2100R = 0.2627f, kBt2100G = 0.6780f, kBt2100B = 0.0593f;
-float bt2100Luminance(Color e) {
- return kBt2100R * e.r + kBt2100G * e.g + kBt2100B * e.b;
-}
+float bt2100Luminance(Color e) { return kBt2100R * e.r + kBt2100G * e.g + kBt2100B * e.b; }
// See ITU-R BT.2100-2, Table 6, Derivation of colour difference signals.
// BT.2100 uses the same coefficients for calculating luma signal and luminance,
@@ -231,9 +216,7 @@ static const float kBt2100Cb = 1.8814f, kBt2100Cr = 1.4746f;
Color bt2100RgbToYuv(Color e_gamma) {
float y_gamma = bt2100Luminance(e_gamma);
- return {{{ y_gamma,
- (e_gamma.b - y_gamma) / kBt2100Cb,
- (e_gamma.r - y_gamma) / kBt2100Cr }}};
+ return {{{y_gamma, (e_gamma.b - y_gamma) / kBt2100Cb, (e_gamma.r - y_gamma) / kBt2100Cr}}};
}
// See ITU-R BT.2100-2, Table 6, Derivation of colour difference signals.
@@ -265,37 +248,33 @@ static const float kBt2100GCb = kBt2100B * kBt2100Cb / kBt2100G;
static const float kBt2100GCr = kBt2100R * kBt2100Cr / kBt2100G;
Color bt2100YuvToRgb(Color e_gamma) {
- return {{{ clampPixelFloat(e_gamma.y + kBt2100Cr * e_gamma.v),
- clampPixelFloat(e_gamma.y - kBt2100GCb * e_gamma.u - kBt2100GCr * e_gamma.v),
- clampPixelFloat(e_gamma.y + kBt2100Cb * e_gamma.u) }}};
+ return {{{clampPixelFloat(e_gamma.y + kBt2100Cr * e_gamma.v),
+ clampPixelFloat(e_gamma.y - kBt2100GCb * e_gamma.u - kBt2100GCr * e_gamma.v),
+ clampPixelFloat(e_gamma.y + kBt2100Cb * e_gamma.u)}}};
}
// See ITU-R BT.2100-2, Table 5, HLG Reference OETF.
static const float kHlgA = 0.17883277f, kHlgB = 0.28466892f, kHlgC = 0.55991073;
float hlgOetf(float e) {
- if (e <= 1.0f/12.0f) {
+ if (e <= 1.0f / 12.0f) {
return sqrt(3.0f * e);
} else {
return kHlgA * log(12.0f * e - kHlgB) + kHlgC;
}
}
-Color hlgOetf(Color e) {
- return {{{ hlgOetf(e.r), hlgOetf(e.g), hlgOetf(e.b) }}};
-}
+Color hlgOetf(Color e) { return {{{hlgOetf(e.r), hlgOetf(e.g), hlgOetf(e.b)}}}; }
float hlgOetfLUT(float e) {
uint32_t value = static_cast<uint32_t>(e * (kHlgOETFNumEntries - 1) + 0.5);
- //TODO() : Remove once conversion modules have appropriate clamping in place
+ // TODO() : Remove once conversion modules have appropriate clamping in place
value = CLIP3(value, 0, kHlgOETFNumEntries - 1);
return kHlgOETF[value];
}
-Color hlgOetfLUT(Color e) {
- return {{{ hlgOetfLUT(e.r), hlgOetfLUT(e.g), hlgOetfLUT(e.b) }}};
-}
+Color hlgOetfLUT(Color e) { return {{{hlgOetfLUT(e.r), hlgOetfLUT(e.g), hlgOetfLUT(e.b)}}}; }
// See ITU-R BT.2100-2, Table 5, HLG Reference EOTF.
float hlgInvOetf(float e_gamma) {
@@ -307,23 +286,19 @@ float hlgInvOetf(float e_gamma) {
}
Color hlgInvOetf(Color e_gamma) {
- return {{{ hlgInvOetf(e_gamma.r),
- hlgInvOetf(e_gamma.g),
- hlgInvOetf(e_gamma.b) }}};
+ return {{{hlgInvOetf(e_gamma.r), hlgInvOetf(e_gamma.g), hlgInvOetf(e_gamma.b)}}};
}
float hlgInvOetfLUT(float e_gamma) {
uint32_t value = static_cast<uint32_t>(e_gamma * (kHlgInvOETFNumEntries - 1) + 0.5);
- //TODO() : Remove once conversion modules have appropriate clamping in place
+ // TODO() : Remove once conversion modules have appropriate clamping in place
value = CLIP3(value, 0, kHlgInvOETFNumEntries - 1);
return kHlgInvOETF[value];
}
Color hlgInvOetfLUT(Color e_gamma) {
- return {{{ hlgInvOetfLUT(e_gamma.r),
- hlgInvOetfLUT(e_gamma.g),
- hlgInvOetfLUT(e_gamma.b) }}};
+ return {{{hlgInvOetfLUT(e_gamma.r), hlgInvOetfLUT(e_gamma.g), hlgInvOetfLUT(e_gamma.b)}}};
}
// See ITU-R BT.2100-2, Table 4, Reference PQ OETF.
@@ -333,25 +308,20 @@ static const float kPqC1 = 3424.0f / 4096.0f, kPqC2 = 2413.0f / 4096.0f * 32.0f,
float pqOetf(float e) {
if (e <= 0.0f) return 0.0f;
- return pow((kPqC1 + kPqC2 * pow(e, kPqM1)) / (1 + kPqC3 * pow(e, kPqM1)),
- kPqM2);
+ return pow((kPqC1 + kPqC2 * pow(e, kPqM1)) / (1 + kPqC3 * pow(e, kPqM1)), kPqM2);
}
-Color pqOetf(Color e) {
- return {{{ pqOetf(e.r), pqOetf(e.g), pqOetf(e.b) }}};
-}
+Color pqOetf(Color e) { return {{{pqOetf(e.r), pqOetf(e.g), pqOetf(e.b)}}}; }
float pqOetfLUT(float e) {
uint32_t value = static_cast<uint32_t>(e * (kPqOETFNumEntries - 1) + 0.5);
- //TODO() : Remove once conversion modules have appropriate clamping in place
+ // TODO() : Remove once conversion modules have appropriate clamping in place
value = CLIP3(value, 0, kPqOETFNumEntries - 1);
return kPqOETF[value];
}
-Color pqOetfLUT(Color e) {
- return {{{ pqOetfLUT(e.r), pqOetfLUT(e.g), pqOetfLUT(e.b) }}};
-}
+Color pqOetfLUT(Color e) { return {{{pqOetfLUT(e.r), pqOetfLUT(e.g), pqOetfLUT(e.b)}}}; }
// Derived from the inverse of the Reference PQ OETF.
static const float kPqInvA = 128.0f, kPqInvB = 107.0f, kPqInvC = 2413.0f, kPqInvD = 2392.0f,
@@ -362,70 +332,64 @@ float pqInvOetf(float e_gamma) {
// always catch 0.0. So, check on 0.0001, since anything this small will
// effectively be crushed to zero anyways.
if (e_gamma <= 0.0001f) return 0.0f;
- return pow((kPqInvA * pow(e_gamma, kPqInvF) - kPqInvB)
- / (kPqInvC - kPqInvD * pow(e_gamma, kPqInvF)),
- kPqInvE);
+ return pow(
+ (kPqInvA * pow(e_gamma, kPqInvF) - kPqInvB) / (kPqInvC - kPqInvD * pow(e_gamma, kPqInvF)),
+ kPqInvE);
}
Color pqInvOetf(Color e_gamma) {
- return {{{ pqInvOetf(e_gamma.r),
- pqInvOetf(e_gamma.g),
- pqInvOetf(e_gamma.b) }}};
+ return {{{pqInvOetf(e_gamma.r), pqInvOetf(e_gamma.g), pqInvOetf(e_gamma.b)}}};
}
float pqInvOetfLUT(float e_gamma) {
uint32_t value = static_cast<uint32_t>(e_gamma * (kPqInvOETFNumEntries - 1) + 0.5);
- //TODO() : Remove once conversion modules have appropriate clamping in place
+ // TODO() : Remove once conversion modules have appropriate clamping in place
value = CLIP3(value, 0, kPqInvOETFNumEntries - 1);
return kPqInvOETF[value];
}
Color pqInvOetfLUT(Color e_gamma) {
- return {{{ pqInvOetfLUT(e_gamma.r),
- pqInvOetfLUT(e_gamma.g),
- pqInvOetfLUT(e_gamma.b) }}};
+ return {{{pqInvOetfLUT(e_gamma.r), pqInvOetfLUT(e_gamma.g), pqInvOetfLUT(e_gamma.b)}}};
}
-
////////////////////////////////////////////////////////////////////////////////
// Color conversions
Color bt709ToP3(Color e) {
- return {{{ 0.82254f * e.r + 0.17755f * e.g + 0.00006f * e.b,
+ return {{{0.82254f * e.r + 0.17755f * e.g + 0.00006f * e.b,
0.03312f * e.r + 0.96684f * e.g + -0.00001f * e.b,
- 0.01706f * e.r + 0.07240f * e.g + 0.91049f * e.b }}};
+ 0.01706f * e.r + 0.07240f * e.g + 0.91049f * e.b}}};
}
Color bt709ToBt2100(Color e) {
- return {{{ 0.62740f * e.r + 0.32930f * e.g + 0.04332f * e.b,
+ return {{{0.62740f * e.r + 0.32930f * e.g + 0.04332f * e.b,
0.06904f * e.r + 0.91958f * e.g + 0.01138f * e.b,
- 0.01636f * e.r + 0.08799f * e.g + 0.89555f * e.b }}};
+ 0.01636f * e.r + 0.08799f * e.g + 0.89555f * e.b}}};
}
Color p3ToBt709(Color e) {
- return {{{ 1.22482f * e.r + -0.22490f * e.g + -0.00007f * e.b,
+ return {{{1.22482f * e.r + -0.22490f * e.g + -0.00007f * e.b,
-0.04196f * e.r + 1.04199f * e.g + 0.00001f * e.b,
- -0.01961f * e.r + -0.07865f * e.g + 1.09831f * e.b }}};
+ -0.01961f * e.r + -0.07865f * e.g + 1.09831f * e.b}}};
}
Color p3ToBt2100(Color e) {
- return {{{ 0.75378f * e.r + 0.19862f * e.g + 0.04754f * e.b,
+ return {{{0.75378f * e.r + 0.19862f * e.g + 0.04754f * e.b,
0.04576f * e.r + 0.94177f * e.g + 0.01250f * e.b,
- -0.00121f * e.r + 0.01757f * e.g + 0.98359f * e.b }}};
+ -0.00121f * e.r + 0.01757f * e.g + 0.98359f * e.b}}};
}
Color bt2100ToBt709(Color e) {
- return {{{ 1.66045f * e.r + -0.58764f * e.g + -0.07286f * e.b,
+ return {{{1.66045f * e.r + -0.58764f * e.g + -0.07286f * e.b,
-0.12445f * e.r + 1.13282f * e.g + -0.00837f * e.b,
- -0.01811f * e.r + -0.10057f * e.g + 1.11878f * e.b }}};
+ -0.01811f * e.r + -0.10057f * e.g + 1.11878f * e.b}}};
}
Color bt2100ToP3(Color e) {
- return {{{ 1.34369f * e.r + -0.28223f * e.g + -0.06135f * e.b,
+ return {{{1.34369f * e.r + -0.28223f * e.g + -0.06135f * e.b,
-0.06533f * e.r + 1.07580f * e.g + -0.01051f * e.b,
- 0.00283f * e.r + -0.01957f * e.g + 1.01679f * e.b
- }}};
+ 0.00283f * e.r + -0.01957f * e.g + 1.01679f * e.b}}};
}
// TODO: confirm we always want to convert like this before calculating
@@ -481,46 +445,46 @@ ColorTransformFn getHdrConversionFn(ultrahdr_color_gamut sdr_gamut,
// DataSpace.
Color yuv709To601(Color e_gamma) {
- return {{{ 1.0f * e_gamma.y + 0.101579f * e_gamma.u + 0.196076f * e_gamma.v,
- 0.0f * e_gamma.y + 0.989854f * e_gamma.u + -0.110653f * e_gamma.v,
- 0.0f * e_gamma.y + -0.072453f * e_gamma.u + 0.983398f * e_gamma.v }}};
+ return {{{1.0f * e_gamma.y + 0.101579f * e_gamma.u + 0.196076f * e_gamma.v,
+ 0.0f * e_gamma.y + 0.989854f * e_gamma.u + -0.110653f * e_gamma.v,
+ 0.0f * e_gamma.y + -0.072453f * e_gamma.u + 0.983398f * e_gamma.v}}};
}
Color yuv709To2100(Color e_gamma) {
- return {{{ 1.0f * e_gamma.y + -0.016969f * e_gamma.u + 0.096312f * e_gamma.v,
- 0.0f * e_gamma.y + 0.995306f * e_gamma.u + -0.051192f * e_gamma.v,
- 0.0f * e_gamma.y + 0.011507f * e_gamma.u + 1.002637f * e_gamma.v }}};
+ return {{{1.0f * e_gamma.y + -0.016969f * e_gamma.u + 0.096312f * e_gamma.v,
+ 0.0f * e_gamma.y + 0.995306f * e_gamma.u + -0.051192f * e_gamma.v,
+ 0.0f * e_gamma.y + 0.011507f * e_gamma.u + 1.002637f * e_gamma.v}}};
}
Color yuv601To709(Color e_gamma) {
- return {{{ 1.0f * e_gamma.y + -0.118188f * e_gamma.u + -0.212685f * e_gamma.v,
- 0.0f * e_gamma.y + 1.018640f * e_gamma.u + 0.114618f * e_gamma.v,
- 0.0f * e_gamma.y + 0.075049f * e_gamma.u + 1.025327f * e_gamma.v }}};
+ return {{{1.0f * e_gamma.y + -0.118188f * e_gamma.u + -0.212685f * e_gamma.v,
+ 0.0f * e_gamma.y + 1.018640f * e_gamma.u + 0.114618f * e_gamma.v,
+ 0.0f * e_gamma.y + 0.075049f * e_gamma.u + 1.025327f * e_gamma.v}}};
}
Color yuv601To2100(Color e_gamma) {
- return {{{ 1.0f * e_gamma.y + -0.128245f * e_gamma.u + -0.115879f * e_gamma.v,
- 0.0f * e_gamma.y + 1.010016f * e_gamma.u + 0.061592f * e_gamma.v,
- 0.0f * e_gamma.y + 0.086969f * e_gamma.u + 1.029350f * e_gamma.v }}};
+ return {{{1.0f * e_gamma.y + -0.128245f * e_gamma.u + -0.115879f * e_gamma.v,
+ 0.0f * e_gamma.y + 1.010016f * e_gamma.u + 0.061592f * e_gamma.v,
+ 0.0f * e_gamma.y + 0.086969f * e_gamma.u + 1.029350f * e_gamma.v}}};
}
Color yuv2100To709(Color e_gamma) {
- return {{{ 1.0f * e_gamma.y + 0.018149f * e_gamma.u + -0.095132f * e_gamma.v,
- 0.0f * e_gamma.y + 1.004123f * e_gamma.u + 0.051267f * e_gamma.v,
- 0.0f * e_gamma.y + -0.011524f * e_gamma.u + 0.996782f * e_gamma.v }}};
+ return {{{1.0f * e_gamma.y + 0.018149f * e_gamma.u + -0.095132f * e_gamma.v,
+ 0.0f * e_gamma.y + 1.004123f * e_gamma.u + 0.051267f * e_gamma.v,
+ 0.0f * e_gamma.y + -0.011524f * e_gamma.u + 0.996782f * e_gamma.v}}};
}
Color yuv2100To601(Color e_gamma) {
- return {{{ 1.0f * e_gamma.y + 0.117887f * e_gamma.u + 0.105521f * e_gamma.v,
- 0.0f * e_gamma.y + 0.995211f * e_gamma.u + -0.059549f * e_gamma.v,
- 0.0f * e_gamma.y + -0.084085f * e_gamma.u + 0.976518f * e_gamma.v }}};
+ return {{{1.0f * e_gamma.y + 0.117887f * e_gamma.u + 0.105521f * e_gamma.v,
+ 0.0f * e_gamma.y + 0.995211f * e_gamma.u + -0.059549f * e_gamma.v,
+ 0.0f * e_gamma.y + -0.084085f * e_gamma.u + 0.976518f * e_gamma.v}}};
}
void transformYuv420(jr_uncompressed_ptr image, size_t x_chroma, size_t y_chroma,
ColorTransformFn fn) {
- Color yuv1 = getYuv420Pixel(image, x_chroma * 2, y_chroma * 2 );
- Color yuv2 = getYuv420Pixel(image, x_chroma * 2 + 1, y_chroma * 2 );
- Color yuv3 = getYuv420Pixel(image, x_chroma * 2, y_chroma * 2 + 1);
+ Color yuv1 = getYuv420Pixel(image, x_chroma * 2, y_chroma * 2);
+ Color yuv2 = getYuv420Pixel(image, x_chroma * 2 + 1, y_chroma * 2);
+ Color yuv3 = getYuv420Pixel(image, x_chroma * 2, y_chroma * 2 + 1);
Color yuv4 = getYuv420Pixel(image, x_chroma * 2 + 1, y_chroma * 2 + 1);
yuv1 = fn(yuv1);
@@ -530,9 +494,9 @@ void transformYuv420(jr_uncompressed_ptr image, size_t x_chroma, size_t y_chroma
Color new_uv = (yuv1 + yuv2 + yuv3 + yuv4) / 4.0f;
- size_t pixel_y1_idx = x_chroma * 2 + y_chroma * 2 * image->luma_stride;
- size_t pixel_y2_idx = (x_chroma * 2 + 1) + y_chroma * 2 * image->luma_stride;
- size_t pixel_y3_idx = x_chroma * 2 + (y_chroma * 2 + 1) * image->luma_stride;
+ size_t pixel_y1_idx = x_chroma * 2 + y_chroma * 2 * image->luma_stride;
+ size_t pixel_y2_idx = (x_chroma * 2 + 1) + y_chroma * 2 * image->luma_stride;
+ size_t pixel_y3_idx = x_chroma * 2 + (y_chroma * 2 + 1) * image->luma_stride;
size_t pixel_y4_idx = (x_chroma * 2 + 1) + (y_chroma * 2 + 1) * image->luma_stride;
uint8_t& y1_uint = reinterpret_cast<uint8_t*>(image->data)[pixel_y1_idx];
@@ -558,8 +522,8 @@ void transformYuv420(jr_uncompressed_ptr image, size_t x_chroma, size_t y_chroma
////////////////////////////////////////////////////////////////////////////////
// Gain map calculations
uint8_t encodeGain(float y_sdr, float y_hdr, ultrahdr_metadata_ptr metadata) {
- return encodeGain(y_sdr, y_hdr, metadata,
- log2(metadata->minContentBoost), log2(metadata->maxContentBoost));
+ return encodeGain(y_sdr, y_hdr, metadata, log2(metadata->minContentBoost),
+ log2(metadata->maxContentBoost));
}
uint8_t encodeGain(float y_sdr, float y_hdr, ultrahdr_metadata_ptr metadata,
@@ -572,21 +536,20 @@ uint8_t encodeGain(float y_sdr, float y_hdr, ultrahdr_metadata_ptr metadata,
if (gain < metadata->minContentBoost) gain = metadata->minContentBoost;
if (gain > metadata->maxContentBoost) gain = metadata->maxContentBoost;
- return static_cast<uint8_t>((log2(gain) - log2MinContentBoost)
- / (log2MaxContentBoost - log2MinContentBoost)
- * 255.0f);
+ return static_cast<uint8_t>((log2(gain) - log2MinContentBoost) /
+ (log2MaxContentBoost - log2MinContentBoost) * 255.0f);
}
Color applyGain(Color e, float gain, ultrahdr_metadata_ptr metadata) {
- float logBoost = log2(metadata->minContentBoost) * (1.0f - gain)
- + log2(metadata->maxContentBoost) * gain;
+ float logBoost =
+ log2(metadata->minContentBoost) * (1.0f - gain) + log2(metadata->maxContentBoost) * gain;
float gainFactor = exp2(logBoost);
return e * gainFactor;
}
Color applyGain(Color e, float gain, ultrahdr_metadata_ptr metadata, float displayBoost) {
- float logBoost = log2(metadata->minContentBoost) * (1.0f - gain)
- + log2(metadata->maxContentBoost) * gain;
+ float logBoost =
+ log2(metadata->minContentBoost) * (1.0f - gain) + log2(metadata->maxContentBoost) * gain;
float gainFactor = exp2(logBoost * displayBoost / metadata->maxContentBoost);
return e * gainFactor;
}
@@ -612,9 +575,8 @@ Color getYuv420Pixel(jr_uncompressed_ptr image, size_t x, size_t y) {
// 128 bias for UV given we are using jpeglib; see:
// https://github.com/kornelski/libjpeg/blob/master/structure.doc
- return {{{ static_cast<float>(y_uint) / 255.0f,
- (static_cast<float>(u_uint) - 128.0f) / 255.0f,
- (static_cast<float>(v_uint) - 128.0f) / 255.0f }}};
+ return {{{static_cast<float>(y_uint) / 255.0f, (static_cast<float>(u_uint) - 128.0f) / 255.0f,
+ (static_cast<float>(v_uint) - 128.0f) / 255.0f}}};
}
Color getP010Pixel(jr_uncompressed_ptr image, size_t x, size_t y) {
@@ -632,16 +594,16 @@ Color getP010Pixel(jr_uncompressed_ptr image, size_t x, size_t y) {
uint16_t v_uint = chroma_data[pixel_v_idx] >> 6;
// Conversions include taking narrow-range into account.
- return {{{ (static_cast<float>(y_uint) - 64.0f) / 876.0f,
- (static_cast<float>(u_uint) - 64.0f) / 896.0f - 0.5f,
- (static_cast<float>(v_uint) - 64.0f) / 896.0f - 0.5f }}};
+ return {{{(static_cast<float>(y_uint) - 64.0f) / 876.0f,
+ (static_cast<float>(u_uint) - 64.0f) / 896.0f - 0.5f,
+ (static_cast<float>(v_uint) - 64.0f) / 896.0f - 0.5f}}};
}
typedef Color (*getPixelFn)(jr_uncompressed_ptr, size_t, size_t);
static Color samplePixels(jr_uncompressed_ptr image, size_t map_scale_factor, size_t x, size_t y,
getPixelFn get_pixel_fn) {
- Color e = {{{ 0.0f, 0.0f, 0.0f }}};
+ Color e = {{{0.0f, 0.0f, 0.0f}}};
for (size_t dy = 0; dy < map_scale_factor; ++dy) {
for (size_t dx = 0; dx < map_scale_factor; ++dx) {
e += get_pixel_fn(image, x * map_scale_factor + dx, y * map_scale_factor + dy);
@@ -666,9 +628,7 @@ static size_t clamp(const size_t& val, const size_t& low, const size_t& high) {
return val < low ? low : (high < val ? high : val);
}
-static float mapUintToFloat(uint8_t map_uint) {
- return static_cast<float>(map_uint) / 255.0f;
-}
+static float mapUintToFloat(uint8_t map_uint) { return static_cast<float>(map_uint) / 255.0f; }
static float pythDistance(float x_diff, float y_diff) {
return sqrt(pow(x_diff, 2.0f) + pow(y_diff, 2.0f));
@@ -693,23 +653,23 @@ float sampleMap(jr_uncompressed_ptr map, float map_scale_factor, size_t x, size_
// en.wikipedia.org/wiki/Inverse_distance_weighting#Shepard's_method
float e1 = mapUintToFloat(reinterpret_cast<uint8_t*>(map->data)[x_lower + y_lower * map->width]);
- float e1_dist = pythDistance(x_map - static_cast<float>(x_lower),
- y_map - static_cast<float>(y_lower));
+ float e1_dist =
+ pythDistance(x_map - static_cast<float>(x_lower), y_map - static_cast<float>(y_lower));
if (e1_dist == 0.0f) return e1;
float e2 = mapUintToFloat(reinterpret_cast<uint8_t*>(map->data)[x_lower + y_upper * map->width]);
- float e2_dist = pythDistance(x_map - static_cast<float>(x_lower),
- y_map - static_cast<float>(y_upper));
+ float e2_dist =
+ pythDistance(x_map - static_cast<float>(x_lower), y_map - static_cast<float>(y_upper));
if (e2_dist == 0.0f) return e2;
float e3 = mapUintToFloat(reinterpret_cast<uint8_t*>(map->data)[x_upper + y_lower * map->width]);
- float e3_dist = pythDistance(x_map - static_cast<float>(x_upper),
- y_map - static_cast<float>(y_lower));
+ float e3_dist =
+ pythDistance(x_map - static_cast<float>(x_upper), y_map - static_cast<float>(y_lower));
if (e3_dist == 0.0f) return e3;
float e4 = mapUintToFloat(reinterpret_cast<uint8_t*>(map->data)[x_upper + y_upper * map->width]);
- float e4_dist = pythDistance(x_map - static_cast<float>(x_upper),
- y_map - static_cast<float>(y_upper));
+ float e4_dist =
+ pythDistance(x_map - static_cast<float>(x_upper), y_map - static_cast<float>(y_upper));
if (e4_dist == 0.0f) return e2;
float e1_weight = 1.0f / e1_dist;
@@ -718,10 +678,8 @@ float sampleMap(jr_uncompressed_ptr map, float map_scale_factor, size_t x, size_
float e4_weight = 1.0f / e4_dist;
float total_weight = e1_weight + e2_weight + e3_weight + e4_weight;
- return e1 * (e1_weight / total_weight)
- + e2 * (e2_weight / total_weight)
- + e3 * (e3_weight / total_weight)
- + e4 * (e4_weight / total_weight);
+ return e1 * (e1_weight / total_weight) + e2 * (e2_weight / total_weight) +
+ e3 * (e3_weight / total_weight) + e4 * (e4_weight / total_weight);
}
float sampleMap(jr_uncompressed_ptr map, size_t map_scale_factor, size_t x, size_t y,
@@ -749,26 +707,27 @@ float sampleMap(jr_uncompressed_ptr map, size_t map_scale_factor, size_t x, size
int offset_y = y % map_scale_factor;
float* weights = weightTables.mWeights;
- if (x_lower == x_upper && y_lower == y_upper) weights = weightTables.mWeightsC;
- else if (x_lower == x_upper) weights = weightTables.mWeightsNR;
- else if (y_lower == y_upper) weights = weightTables.mWeightsNB;
+ if (x_lower == x_upper && y_lower == y_upper)
+ weights = weightTables.mWeightsC;
+ else if (x_lower == x_upper)
+ weights = weightTables.mWeightsNR;
+ else if (y_lower == y_upper)
+ weights = weightTables.mWeightsNB;
weights += offset_y * map_scale_factor * 4 + offset_x * 4;
return e1 * weights[0] + e2 * weights[1] + e3 * weights[2] + e4 * weights[3];
}
uint32_t colorToRgba1010102(Color e_gamma) {
- return (0x3ff & static_cast<uint32_t>(e_gamma.r * 1023.0f))
- | ((0x3ff & static_cast<uint32_t>(e_gamma.g * 1023.0f)) << 10)
- | ((0x3ff & static_cast<uint32_t>(e_gamma.b * 1023.0f)) << 20)
- | (0x3 << 30); // Set alpha to 1.0
+ return (0x3ff & static_cast<uint32_t>(e_gamma.r * 1023.0f)) |
+ ((0x3ff & static_cast<uint32_t>(e_gamma.g * 1023.0f)) << 10) |
+ ((0x3ff & static_cast<uint32_t>(e_gamma.b * 1023.0f)) << 20) |
+ (0x3 << 30); // Set alpha to 1.0
}
uint64_t colorToRgbaF16(Color e_gamma) {
- return (uint64_t) floatToHalf(e_gamma.r)
- | (((uint64_t) floatToHalf(e_gamma.g)) << 16)
- | (((uint64_t) floatToHalf(e_gamma.b)) << 32)
- | (((uint64_t) floatToHalf(1.0f)) << 48);
+ return (uint64_t)floatToHalf(e_gamma.r) | (((uint64_t)floatToHalf(e_gamma.g)) << 16) |
+ (((uint64_t)floatToHalf(e_gamma.b)) << 32) | (((uint64_t)floatToHalf(1.0f)) << 48);
}
-} // namespace ultrahdr
+} // namespace ultrahdr
diff --git a/include/ultrahdr/gainmapmath.h b/lib/gainmapmath.h
index 8cda24e..bdbaf02 100644
--- a/include/ultrahdr/gainmapmath.h
+++ b/lib/gainmapmath.h
@@ -19,8 +19,8 @@
#include <cmath>
-#include "ultrahdr/ultrahdr.h"
-#include "ultrahdr/jpegr.h"
+#include "ultrahdr.h"
+#include "jpegr.h"
#define CLIP3(x, min, max) ((x) < (min)) ? (min) : ((x) > (max)) ? (max) : (x)
@@ -59,14 +59,13 @@ typedef float (*ColorCalculationFn)(Color);
//
// (A simple gamma transfer function sets g to gamma and a to 1.)
typedef struct TransferFunction {
- float g, a,b,c,d,e,f;
+ float g, a, b, c, d, e, f;
} TransferFunction;
-static constexpr TransferFunction kSRGB_TransFun =
- { 2.4f, (float)(1/1.055), (float)(0.055/1.055), (float)(1/12.92), 0.04045f, 0.0f, 0.0f };
+static constexpr TransferFunction kSRGB_TransFun = {
+ 2.4f, (float)(1 / 1.055), (float)(0.055 / 1.055), (float)(1 / 12.92), 0.04045f, 0.0f, 0.0f};
-static constexpr TransferFunction kLinear_TransFun =
- { 1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f };
+static constexpr TransferFunction kLinear_TransFun = {1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f};
inline Color operator+=(Color& lhs, const Color& rhs) {
lhs.r += rhs.r;
@@ -136,14 +135,13 @@ inline uint16_t floatToHalf(float f) {
// round-to-nearest-even: add last bit after truncated mantissa
const uint32_t b = *((uint32_t*)&f) + 0x00001000;
- const int32_t e = (b & 0x7F800000) >> 23; // exponent
- const uint32_t m = b & 0x007FFFFF; // mantissa
+ const int32_t e = (b & 0x7F800000) >> 23; // exponent
+ const uint32_t m = b & 0x007FFFFF; // mantissa
// sign : normalized : denormalized : saturate
- return (b & 0x80000000) >> 16
- | (e > 112) * ((((e - 112) << 10) & 0x7C00) | m >> 13)
- | ((e < 113) & (e > 101)) * ((((0x007FF000 + m) >> (125 - e)) + 1) >> 1)
- | (e > 143) * 0x7FFF;
+ return (b & 0x80000000) >> 16 | (e > 112) * ((((e - 112) << 10) & 0x7C00) | m >> 13) |
+ ((e < 113) & (e > 101)) * ((((0x007FF000 + m) >> (125 - e)) + 1) >> 1) |
+ (e > 143) * 0x7FFF;
}
constexpr size_t kGainFactorPrecision = 10;
@@ -152,8 +150,8 @@ struct GainLUT {
GainLUT(ultrahdr_metadata_ptr metadata) {
for (size_t idx = 0; idx < kGainFactorNumEntries; idx++) {
float value = static_cast<float>(idx) / static_cast<float>(kGainFactorNumEntries - 1);
- float logBoost = log2(metadata->minContentBoost) * (1.0f - value)
- + log2(metadata->maxContentBoost) * value;
+ float logBoost = log2(metadata->minContentBoost) * (1.0f - value) +
+ log2(metadata->maxContentBoost) * value;
mGainTable[idx] = exp2(logBoost);
}
}
@@ -162,23 +160,22 @@ struct GainLUT {
float boostFactor = displayBoost > 0 ? displayBoost / metadata->maxContentBoost : 1.0f;
for (size_t idx = 0; idx < kGainFactorNumEntries; idx++) {
float value = static_cast<float>(idx) / static_cast<float>(kGainFactorNumEntries - 1);
- float logBoost = log2(metadata->minContentBoost) * (1.0f - value)
- + log2(metadata->maxContentBoost) * value;
+ float logBoost = log2(metadata->minContentBoost) * (1.0f - value) +
+ log2(metadata->maxContentBoost) * value;
mGainTable[idx] = exp2(logBoost * boostFactor);
}
}
- ~GainLUT() {
- }
+ ~GainLUT() {}
float getGainFactor(float gain) {
uint32_t idx = static_cast<uint32_t>(gain * (kGainFactorNumEntries - 1) + 0.5);
- //TODO() : Remove once conversion modules have appropriate clamping in place
+ // TODO() : Remove once conversion modules have appropriate clamping in place
idx = CLIP3(idx, 0, kGainFactorNumEntries - 1);
return mGainTable[idx];
}
-private:
+ private:
float mGainTable[kGainFactorNumEntries];
};
@@ -222,10 +219,10 @@ struct ShepardsIDW {
// TODO: check if its ok to mWeights at places
float* mWeightsNR; // no right
float* mWeightsNB; // no bottom
- float* mWeightsC; // no right & bottom
+ float* mWeightsC; // no right & bottom
float euclideanDistance(float x1, float x2, float y1, float y2);
- void fillShepardsIDW(float *weights, int incR, int incB);
+ void fillShepardsIDW(float* weights, int incR, int incB);
};
////////////////////////////////////////////////////////////////////////////////
@@ -249,7 +246,6 @@ float srgbLuminance(Color e);
*/
Color srgbRgbToYuv(Color e_gamma);
-
/*
* Convert from OETF'd srgb YUV to RGB, according to ITU-R BT.709-6.
*
@@ -294,7 +290,6 @@ Color p3RgbToYuv(Color e_gamma);
*/
Color p3YuvToRgb(Color e_gamma);
-
////////////////////////////////////////////////////////////////////////////////
// BT.2100 transformations - according to ITU-R BT.2100-2
@@ -371,7 +366,6 @@ Color pqInvOetfLUT(Color e_gamma);
constexpr size_t kPqInvOETFPrecision = 12;
constexpr size_t kPqInvOETFNumEntries = 1 << kPqInvOETFPrecision;
-
////////////////////////////////////////////////////////////////////////////////
// Color space conversions
@@ -424,7 +418,6 @@ Color yuv2100To601(Color e_gamma);
void transformYuv420(jr_uncompressed_ptr image, size_t x_chroma, size_t y_chroma,
ColorTransformFn fn);
-
////////////////////////////////////////////////////////////////////////////////
// Gain map calculations
@@ -500,6 +493,6 @@ uint32_t colorToRgba1010102(Color e_gamma);
*/
uint64_t colorToRgbaF16(Color e_gamma);
-} // namespace ultrahdr
+} // namespace ultrahdr
-#endif // ULTRAHDR_GAINMAPMATH_H
+#endif // ULTRAHDR_GAINMAPMATH_H
diff --git a/lib/icc.cpp b/lib/icc.cpp
new file mode 100644
index 0000000..851dd9d
--- /dev/null
+++ b/lib/icc.cpp
@@ -0,0 +1,680 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * 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.
+ */
+
+#include <cstring>
+
+#include "ultrahdrcommon.h"
+#include "icc.h"
+
+namespace ultrahdr {
+
+static void Matrix3x3_apply(const Matrix3x3* m, float* x) {
+ float y0 = x[0] * m->vals[0][0] + x[1] * m->vals[0][1] + x[2] * m->vals[0][2];
+ float y1 = x[0] * m->vals[1][0] + x[1] * m->vals[1][1] + x[2] * m->vals[1][2];
+ float y2 = x[0] * m->vals[2][0] + x[1] * m->vals[2][1] + x[2] * m->vals[2][2];
+ x[0] = y0;
+ x[1] = y1;
+ x[2] = y2;
+}
+
+bool Matrix3x3_invert(const Matrix3x3* src, Matrix3x3* dst) {
+ double a00 = src->vals[0][0];
+ double a01 = src->vals[1][0];
+ double a02 = src->vals[2][0];
+ double a10 = src->vals[0][1];
+ double a11 = src->vals[1][1];
+ double a12 = src->vals[2][1];
+ double a20 = src->vals[0][2];
+ double a21 = src->vals[1][2];
+ double a22 = src->vals[2][2];
+
+ double b0 = a00 * a11 - a01 * a10;
+ double b1 = a00 * a12 - a02 * a10;
+ double b2 = a01 * a12 - a02 * a11;
+ double b3 = a20;
+ double b4 = a21;
+ double b5 = a22;
+
+ double determinant = b0 * b5 - b1 * b4 + b2 * b3;
+
+ if (determinant == 0) {
+ return false;
+ }
+
+ double invdet = 1.0 / determinant;
+ if (invdet > +FLT_MAX || invdet < -FLT_MAX || !isfinitef_((float)invdet)) {
+ return false;
+ }
+
+ b0 *= invdet;
+ b1 *= invdet;
+ b2 *= invdet;
+ b3 *= invdet;
+ b4 *= invdet;
+ b5 *= invdet;
+
+ dst->vals[0][0] = (float)(a11 * b5 - a12 * b4);
+ dst->vals[1][0] = (float)(a02 * b4 - a01 * b5);
+ dst->vals[2][0] = (float)(+b2);
+ dst->vals[0][1] = (float)(a12 * b3 - a10 * b5);
+ dst->vals[1][1] = (float)(a00 * b5 - a02 * b3);
+ dst->vals[2][1] = (float)(-b1);
+ dst->vals[0][2] = (float)(a10 * b4 - a11 * b3);
+ dst->vals[1][2] = (float)(a01 * b3 - a00 * b4);
+ dst->vals[2][2] = (float)(+b0);
+
+ for (int r = 0; r < 3; ++r)
+ for (int c = 0; c < 3; ++c) {
+ if (!isfinitef_(dst->vals[r][c])) {
+ return false;
+ }
+ }
+ return true;
+}
+
+static Matrix3x3 Matrix3x3_concat(const Matrix3x3* A, const Matrix3x3* B) {
+ Matrix3x3 m = {{{0, 0, 0}, {0, 0, 0}, {0, 0, 0}}};
+ for (int r = 0; r < 3; r++)
+ for (int c = 0; c < 3; c++) {
+ m.vals[r][c] = A->vals[r][0] * B->vals[0][c] + A->vals[r][1] * B->vals[1][c] +
+ A->vals[r][2] * B->vals[2][c];
+ }
+ return m;
+}
+
+static void float_XYZD50_to_grid16_lab(const float* xyz_float, uint8_t* grid16_lab) {
+ float v[3] = {
+ xyz_float[0] / kD50_x,
+ xyz_float[1] / kD50_y,
+ xyz_float[2] / kD50_z,
+ };
+ for (size_t i = 0; i < 3; ++i) {
+ v[i] = v[i] > 0.008856f ? cbrtf(v[i]) : v[i] * 7.787f + (16 / 116.0f);
+ }
+ const float L = v[1] * 116.0f - 16.0f;
+ const float a = (v[0] - v[1]) * 500.0f;
+ const float b = (v[1] - v[2]) * 200.0f;
+ const float Lab_unorm[3] = {
+ L * (1 / 100.f),
+ (a + 128.0f) * (1 / 255.0f),
+ (b + 128.0f) * (1 / 255.0f),
+ };
+ // This will encode L=1 as 0xFFFF. This matches how skcms will interpret the
+ // table, but the spec appears to indicate that the value should be 0xFF00.
+ // https://crbug.com/skia/13807
+ for (size_t i = 0; i < 3; ++i) {
+ reinterpret_cast<uint16_t*>(grid16_lab)[i] =
+ Endian_SwapBE16(float_round_to_unorm16(Lab_unorm[i]));
+ }
+}
+
+std::string IccHelper::get_desc_string(const ultrahdr_transfer_function tf,
+ const ultrahdr_color_gamut gamut) {
+ std::string result;
+ switch (gamut) {
+ case ULTRAHDR_COLORGAMUT_BT709:
+ result += "sRGB";
+ break;
+ case ULTRAHDR_COLORGAMUT_P3:
+ result += "Display P3";
+ break;
+ case ULTRAHDR_COLORGAMUT_BT2100:
+ result += "Rec2020";
+ break;
+ default:
+ result += "Unknown";
+ break;
+ }
+ result += " Gamut with ";
+ switch (tf) {
+ case ULTRAHDR_TF_SRGB:
+ result += "sRGB";
+ break;
+ case ULTRAHDR_TF_LINEAR:
+ result += "Linear";
+ break;
+ case ULTRAHDR_TF_PQ:
+ result += "PQ";
+ break;
+ case ULTRAHDR_TF_HLG:
+ result += "HLG";
+ break;
+ default:
+ result += "Unknown";
+ break;
+ }
+ result += " Transfer";
+ return result;
+}
+
+std::shared_ptr<DataStruct> IccHelper::write_text_tag(const char* text) {
+ uint32_t text_length = strlen(text);
+ uint32_t header[] = {
+ Endian_SwapBE32(kTAG_TextType), // Type signature
+ 0, // Reserved
+ Endian_SwapBE32(1), // Number of records
+ Endian_SwapBE32(12), // Record size (must be 12)
+ Endian_SwapBE32(SetFourByteTag('e', 'n', 'U', 'S')), // English USA
+ Endian_SwapBE32(2 * text_length), // Length of string in bytes
+ Endian_SwapBE32(28), // Offset of string
+ };
+
+ uint32_t total_length = text_length * 2 + sizeof(header);
+ total_length = (((total_length + 2) >> 2) << 2); // 4 aligned
+ std::shared_ptr<DataStruct> dataStruct = std::make_shared<DataStruct>(total_length);
+
+ if (!dataStruct->write(header, sizeof(header))) {
+ ALOGE("write_text_tag(): error in writing data");
+ return dataStruct;
+ }
+
+ for (size_t i = 0; i < text_length; i++) {
+ // Convert ASCII to big-endian UTF-16.
+ dataStruct->write8(0);
+ dataStruct->write8(text[i]);
+ }
+
+ return dataStruct;
+}
+
+std::shared_ptr<DataStruct> IccHelper::write_xyz_tag(float x, float y, float z) {
+ uint32_t data[] = {
+ Endian_SwapBE32(kXYZ_PCSSpace),
+ 0,
+ static_cast<uint32_t>(Endian_SwapBE32(float_round_to_fixed(x))),
+ static_cast<uint32_t>(Endian_SwapBE32(float_round_to_fixed(y))),
+ static_cast<uint32_t>(Endian_SwapBE32(float_round_to_fixed(z))),
+ };
+ std::shared_ptr<DataStruct> dataStruct = std::make_shared<DataStruct>(sizeof(data));
+ dataStruct->write(&data, sizeof(data));
+ return dataStruct;
+}
+
+std::shared_ptr<DataStruct> IccHelper::write_trc_tag(const int table_entries,
+ const void* table_16) {
+ int total_length = 4 + 4 + 4 + table_entries * 2;
+ total_length = (((total_length + 2) >> 2) << 2); // 4 aligned
+ std::shared_ptr<DataStruct> dataStruct = std::make_shared<DataStruct>(total_length);
+ dataStruct->write32(Endian_SwapBE32(kTAG_CurveType)); // Type
+ dataStruct->write32(0); // Reserved
+ dataStruct->write32(Endian_SwapBE32(table_entries)); // Value count
+ for (int i = 0; i < table_entries; ++i) {
+ uint16_t value = reinterpret_cast<const uint16_t*>(table_16)[i];
+ dataStruct->write16(value);
+ }
+ return dataStruct;
+}
+
+std::shared_ptr<DataStruct> IccHelper::write_trc_tag(const TransferFunction& fn) {
+ if (fn.a == 1.f && fn.b == 0.f && fn.c == 0.f && fn.d == 0.f && fn.e == 0.f && fn.f == 0.f) {
+ int total_length = 16;
+ std::shared_ptr<DataStruct> dataStruct = std::make_shared<DataStruct>(total_length);
+ dataStruct->write32(Endian_SwapBE32(kTAG_ParaCurveType)); // Type
+ dataStruct->write32(0); // Reserved
+ dataStruct->write32(Endian_SwapBE16(kExponential_ParaCurveType));
+ dataStruct->write32(Endian_SwapBE32(float_round_to_fixed(fn.g)));
+ return dataStruct;
+ }
+
+ int total_length = 40;
+ std::shared_ptr<DataStruct> dataStruct = std::make_shared<DataStruct>(total_length);
+ dataStruct->write32(Endian_SwapBE32(kTAG_ParaCurveType)); // Type
+ dataStruct->write32(0); // Reserved
+ dataStruct->write32(Endian_SwapBE16(kGABCDEF_ParaCurveType));
+ dataStruct->write32(Endian_SwapBE32(float_round_to_fixed(fn.g)));
+ dataStruct->write32(Endian_SwapBE32(float_round_to_fixed(fn.a)));
+ dataStruct->write32(Endian_SwapBE32(float_round_to_fixed(fn.b)));
+ dataStruct->write32(Endian_SwapBE32(float_round_to_fixed(fn.c)));
+ dataStruct->write32(Endian_SwapBE32(float_round_to_fixed(fn.d)));
+ dataStruct->write32(Endian_SwapBE32(float_round_to_fixed(fn.e)));
+ dataStruct->write32(Endian_SwapBE32(float_round_to_fixed(fn.f)));
+ return dataStruct;
+}
+
+float IccHelper::compute_tone_map_gain(const ultrahdr_transfer_function tf, float L) {
+ if (L <= 0.f) {
+ return 1.f;
+ }
+ if (tf == ULTRAHDR_TF_PQ) {
+ // The PQ transfer function will map to the range [0, 1]. Linearly scale
+ // it up to the range [0, 10,000/203]. We will then tone map that back
+ // down to [0, 1].
+ constexpr float kInputMaxLuminance = 10000 / 203.f;
+ constexpr float kOutputMaxLuminance = 1.0;
+ L *= kInputMaxLuminance;
+
+ // Compute the tone map gain which will tone map from 10,000/203 to 1.0.
+ constexpr float kToneMapA = kOutputMaxLuminance / (kInputMaxLuminance * kInputMaxLuminance);
+ constexpr float kToneMapB = 1.f / kOutputMaxLuminance;
+ return kInputMaxLuminance * (1.f + kToneMapA * L) / (1.f + kToneMapB * L);
+ }
+ if (tf == ULTRAHDR_TF_HLG) {
+ // Let Lw be the brightness of the display in nits.
+ constexpr float Lw = 203.f;
+ const float gamma = 1.2f + 0.42f * std::log(Lw / 1000.f) / std::log(10.f);
+ return std::pow(L, gamma - 1.f);
+ }
+ return 1.f;
+}
+
+std::shared_ptr<DataStruct> IccHelper::write_cicp_tag(uint32_t color_primaries,
+ uint32_t transfer_characteristics) {
+ int total_length = 12; // 4 + 4 + 1 + 1 + 1 + 1
+ std::shared_ptr<DataStruct> dataStruct = std::make_shared<DataStruct>(total_length);
+ dataStruct->write32(Endian_SwapBE32(kTAG_cicp)); // Type signature
+ dataStruct->write32(0); // Reserved
+ dataStruct->write8(color_primaries); // Color primaries
+ dataStruct->write8(transfer_characteristics); // Transfer characteristics
+ dataStruct->write8(0); // RGB matrix
+ dataStruct->write8(1); // Full range
+ return dataStruct;
+}
+
+void IccHelper::compute_lut_entry(const Matrix3x3& src_to_XYZD50, float rgb[3]) {
+ // Compute the matrices to convert from source to Rec2020, and from Rec2020 to XYZD50.
+ Matrix3x3 src_to_rec2020;
+ const Matrix3x3 rec2020_to_XYZD50 = kRec2020;
+ {
+ Matrix3x3 XYZD50_to_rec2020;
+ Matrix3x3_invert(&rec2020_to_XYZD50, &XYZD50_to_rec2020);
+ src_to_rec2020 = Matrix3x3_concat(&XYZD50_to_rec2020, &src_to_XYZD50);
+ }
+
+ // Convert the source signal to linear.
+ for (size_t i = 0; i < kNumChannels; ++i) {
+ rgb[i] = pqOetf(rgb[i]);
+ }
+
+ // Convert source gamut to Rec2020.
+ Matrix3x3_apply(&src_to_rec2020, rgb);
+
+ // Compute the luminance of the signal.
+ float L = bt2100Luminance({{{rgb[0], rgb[1], rgb[2]}}});
+
+ // Compute the tone map gain based on the luminance.
+ float tone_map_gain = compute_tone_map_gain(ULTRAHDR_TF_PQ, L);
+
+ // Apply the tone map gain.
+ for (size_t i = 0; i < kNumChannels; ++i) {
+ rgb[i] *= tone_map_gain;
+ }
+
+ // Convert from Rec2020-linear to XYZD50.
+ Matrix3x3_apply(&rec2020_to_XYZD50, rgb);
+}
+
+std::shared_ptr<DataStruct> IccHelper::write_clut(const uint8_t* grid_points,
+ const uint8_t* grid_16) {
+ uint32_t value_count = kNumChannels;
+ for (uint32_t i = 0; i < kNumChannels; ++i) {
+ value_count *= grid_points[i];
+ }
+
+ int total_length = 20 + 2 * value_count;
+ total_length = (((total_length + 2) >> 2) << 2); // 4 aligned
+ std::shared_ptr<DataStruct> dataStruct = std::make_shared<DataStruct>(total_length);
+
+ for (size_t i = 0; i < 16; ++i) {
+ dataStruct->write8(i < kNumChannels ? grid_points[i] : 0); // Grid size
+ }
+ dataStruct->write8(2); // Grid byte width (always 16-bit)
+ dataStruct->write8(0); // Reserved
+ dataStruct->write8(0); // Reserved
+ dataStruct->write8(0); // Reserved
+
+ for (uint32_t i = 0; i < value_count; ++i) {
+ uint16_t value = reinterpret_cast<const uint16_t*>(grid_16)[i];
+ dataStruct->write16(value);
+ }
+
+ return dataStruct;
+}
+
+std::shared_ptr<DataStruct> IccHelper::write_mAB_or_mBA_tag(uint32_t type, bool has_a_curves,
+ const uint8_t* grid_points,
+ const uint8_t* grid_16) {
+ const size_t b_curves_offset = 32;
+ std::shared_ptr<DataStruct> b_curves_data[kNumChannels];
+ std::shared_ptr<DataStruct> a_curves_data[kNumChannels];
+ size_t clut_offset = 0;
+ std::shared_ptr<DataStruct> clut;
+ size_t a_curves_offset = 0;
+
+ // The "B" curve is required.
+ for (size_t i = 0; i < kNumChannels; ++i) {
+ b_curves_data[i] = write_trc_tag(kLinear_TransFun);
+ }
+
+ // The "A" curve and CLUT are optional.
+ if (has_a_curves) {
+ clut_offset = b_curves_offset;
+ for (size_t i = 0; i < kNumChannels; ++i) {
+ clut_offset += b_curves_data[i]->getLength();
+ }
+ clut = write_clut(grid_points, grid_16);
+
+ a_curves_offset = clut_offset + clut->getLength();
+ for (size_t i = 0; i < kNumChannels; ++i) {
+ a_curves_data[i] = write_trc_tag(kLinear_TransFun);
+ }
+ }
+
+ int total_length = b_curves_offset;
+ for (size_t i = 0; i < kNumChannels; ++i) {
+ total_length += b_curves_data[i]->getLength();
+ }
+ if (has_a_curves) {
+ total_length += clut->getLength();
+ for (size_t i = 0; i < kNumChannels; ++i) {
+ total_length += a_curves_data[i]->getLength();
+ }
+ }
+ std::shared_ptr<DataStruct> dataStruct = std::make_shared<DataStruct>(total_length);
+ dataStruct->write32(Endian_SwapBE32(type)); // Type signature
+ dataStruct->write32(0); // Reserved
+ dataStruct->write8(kNumChannels); // Input channels
+ dataStruct->write8(kNumChannels); // Output channels
+ dataStruct->write16(0); // Reserved
+ dataStruct->write32(Endian_SwapBE32(b_curves_offset)); // B curve offset
+ dataStruct->write32(Endian_SwapBE32(0)); // Matrix offset (ignored)
+ dataStruct->write32(Endian_SwapBE32(0)); // M curve offset (ignored)
+ dataStruct->write32(Endian_SwapBE32(clut_offset)); // CLUT offset
+ dataStruct->write32(Endian_SwapBE32(a_curves_offset)); // A curve offset
+ for (size_t i = 0; i < kNumChannels; ++i) {
+ if (dataStruct->write(b_curves_data[i]->getData(), b_curves_data[i]->getLength())) {
+ return dataStruct;
+ }
+ }
+ if (has_a_curves) {
+ dataStruct->write(clut->getData(), clut->getLength());
+ for (size_t i = 0; i < kNumChannels; ++i) {
+ dataStruct->write(a_curves_data[i]->getData(), a_curves_data[i]->getLength());
+ }
+ }
+ return dataStruct;
+}
+
+std::shared_ptr<DataStruct> IccHelper::writeIccProfile(ultrahdr_transfer_function tf,
+ ultrahdr_color_gamut gamut) {
+ ICCHeader header;
+
+ std::vector<std::pair<uint32_t, std::shared_ptr<DataStruct>>> tags;
+
+ // Compute profile description tag
+ std::string desc = get_desc_string(tf, gamut);
+
+ tags.emplace_back(kTAG_desc, write_text_tag(desc.c_str()));
+
+ Matrix3x3 toXYZD50;
+ switch (gamut) {
+ case ULTRAHDR_COLORGAMUT_BT709:
+ toXYZD50 = kSRGB;
+ break;
+ case ULTRAHDR_COLORGAMUT_P3:
+ toXYZD50 = kDisplayP3;
+ break;
+ case ULTRAHDR_COLORGAMUT_BT2100:
+ toXYZD50 = kRec2020;
+ break;
+ default:
+ // Should not fall here.
+ return nullptr;
+ }
+
+ // Compute primaries.
+ {
+ tags.emplace_back(kTAG_rXYZ,
+ write_xyz_tag(toXYZD50.vals[0][0], toXYZD50.vals[1][0], toXYZD50.vals[2][0]));
+ tags.emplace_back(kTAG_gXYZ,
+ write_xyz_tag(toXYZD50.vals[0][1], toXYZD50.vals[1][1], toXYZD50.vals[2][1]));
+ tags.emplace_back(kTAG_bXYZ,
+ write_xyz_tag(toXYZD50.vals[0][2], toXYZD50.vals[1][2], toXYZD50.vals[2][2]));
+ }
+
+ // Compute white point tag (must be D50)
+ tags.emplace_back(kTAG_wtpt, write_xyz_tag(kD50_x, kD50_y, kD50_z));
+
+ // Compute transfer curves.
+ if (tf != ULTRAHDR_TF_PQ) {
+ if (tf == ULTRAHDR_TF_HLG) {
+ std::vector<uint8_t> trc_table;
+ trc_table.resize(kTrcTableSize * 2);
+ for (uint32_t i = 0; i < kTrcTableSize; ++i) {
+ float x = i / (kTrcTableSize - 1.f);
+ float y = hlgOetf(x);
+ y *= compute_tone_map_gain(tf, y);
+ float_to_table16(y, &trc_table[2 * i]);
+ }
+
+ tags.emplace_back(kTAG_rTRC,
+ write_trc_tag(kTrcTableSize, reinterpret_cast<uint8_t*>(trc_table.data())));
+ tags.emplace_back(kTAG_gTRC,
+ write_trc_tag(kTrcTableSize, reinterpret_cast<uint8_t*>(trc_table.data())));
+ tags.emplace_back(kTAG_bTRC,
+ write_trc_tag(kTrcTableSize, reinterpret_cast<uint8_t*>(trc_table.data())));
+ } else {
+ tags.emplace_back(kTAG_rTRC, write_trc_tag(kSRGB_TransFun));
+ tags.emplace_back(kTAG_gTRC, write_trc_tag(kSRGB_TransFun));
+ tags.emplace_back(kTAG_bTRC, write_trc_tag(kSRGB_TransFun));
+ }
+ }
+
+ // Compute CICP.
+ if (tf == ULTRAHDR_TF_HLG || tf == ULTRAHDR_TF_PQ) {
+ // The CICP tag is present in ICC 4.4, so update the header's version.
+ header.version = Endian_SwapBE32(0x04400000);
+
+ uint32_t color_primaries = 0;
+ if (gamut == ULTRAHDR_COLORGAMUT_BT709) {
+ color_primaries = kCICPPrimariesSRGB;
+ } else if (gamut == ULTRAHDR_COLORGAMUT_P3) {
+ color_primaries = kCICPPrimariesP3;
+ }
+
+ uint32_t transfer_characteristics = 0;
+ if (tf == ULTRAHDR_TF_SRGB) {
+ transfer_characteristics = kCICPTrfnSRGB;
+ } else if (tf == ULTRAHDR_TF_LINEAR) {
+ transfer_characteristics = kCICPTrfnLinear;
+ } else if (tf == ULTRAHDR_TF_PQ) {
+ transfer_characteristics = kCICPTrfnPQ;
+ } else if (tf == ULTRAHDR_TF_HLG) {
+ transfer_characteristics = kCICPTrfnHLG;
+ }
+ tags.emplace_back(kTAG_cicp, write_cicp_tag(color_primaries, transfer_characteristics));
+ }
+
+ // Compute A2B0.
+ if (tf == ULTRAHDR_TF_PQ) {
+ std::vector<uint8_t> a2b_grid;
+ a2b_grid.resize(kGridSize * kGridSize * kGridSize * kNumChannels * 2);
+ size_t a2b_grid_index = 0;
+ for (uint32_t r_index = 0; r_index < kGridSize; ++r_index) {
+ for (uint32_t g_index = 0; g_index < kGridSize; ++g_index) {
+ for (uint32_t b_index = 0; b_index < kGridSize; ++b_index) {
+ float rgb[3] = {
+ r_index / (kGridSize - 1.f),
+ g_index / (kGridSize - 1.f),
+ b_index / (kGridSize - 1.f),
+ };
+ compute_lut_entry(toXYZD50, rgb);
+ float_XYZD50_to_grid16_lab(rgb, &a2b_grid[a2b_grid_index]);
+ a2b_grid_index += 6;
+ }
+ }
+ }
+ const uint8_t* grid_16 = reinterpret_cast<const uint8_t*>(a2b_grid.data());
+
+ uint8_t grid_points[kNumChannels];
+ for (size_t i = 0; i < kNumChannels; ++i) {
+ grid_points[i] = kGridSize;
+ }
+
+ auto a2b_data = write_mAB_or_mBA_tag(kTAG_mABType,
+ /* has_a_curves */ true, grid_points, grid_16);
+ tags.emplace_back(kTAG_A2B0, std::move(a2b_data));
+ }
+
+ // Compute B2A0.
+ if (tf == ULTRAHDR_TF_PQ) {
+ auto b2a_data = write_mAB_or_mBA_tag(kTAG_mBAType,
+ /* has_a_curves */ false,
+ /* grid_points */ nullptr,
+ /* grid_16 */ nullptr);
+ tags.emplace_back(kTAG_B2A0, std::move(b2a_data));
+ }
+
+ // Compute copyright tag
+ tags.emplace_back(kTAG_cprt, write_text_tag("Google Inc. 2022"));
+
+ // Compute the size of the profile.
+ size_t tag_data_size = 0;
+ for (const auto& tag : tags) {
+ tag_data_size += tag.second->getLength();
+ }
+ size_t tag_table_size = kICCTagTableEntrySize * tags.size();
+ size_t profile_size = kICCHeaderSize + tag_table_size + tag_data_size;
+
+ std::shared_ptr<DataStruct> dataStruct =
+ std::make_shared<DataStruct>(profile_size + kICCIdentifierSize);
+
+ // Write identifier, chunk count, and chunk ID
+ if (!dataStruct->write(kICCIdentifier, sizeof(kICCIdentifier)) || !dataStruct->write8(1) ||
+ !dataStruct->write8(1)) {
+ ALOGE("writeIccProfile(): error in identifier");
+ return dataStruct;
+ }
+
+ // Write the header.
+ header.data_color_space = Endian_SwapBE32(Signature_RGB);
+ header.pcs = Endian_SwapBE32(tf == ULTRAHDR_TF_PQ ? Signature_Lab : Signature_XYZ);
+ header.size = Endian_SwapBE32(profile_size);
+ header.tag_count = Endian_SwapBE32(tags.size());
+
+ if (!dataStruct->write(&header, sizeof(header))) {
+ ALOGE("writeIccProfile(): error in header");
+ return dataStruct;
+ }
+
+ // Write the tag table. Track the offset and size of the previous tag to
+ // compute each tag's offset. An empty SkData indicates that the previous
+ // tag is to be reused.
+ uint32_t last_tag_offset = sizeof(header) + tag_table_size;
+ uint32_t last_tag_size = 0;
+ for (const auto& tag : tags) {
+ last_tag_offset = last_tag_offset + last_tag_size;
+ last_tag_size = tag.second->getLength();
+ uint32_t tag_table_entry[3] = {
+ Endian_SwapBE32(tag.first),
+ Endian_SwapBE32(last_tag_offset),
+ Endian_SwapBE32(last_tag_size),
+ };
+ if (!dataStruct->write(tag_table_entry, sizeof(tag_table_entry))) {
+ ALOGE("writeIccProfile(): error in writing tag table");
+ return dataStruct;
+ }
+ }
+
+ // Write the tags.
+ for (const auto& tag : tags) {
+ if (!dataStruct->write(tag.second->getData(), tag.second->getLength())) {
+ ALOGE("writeIccProfile(): error in writing tags");
+ return dataStruct;
+ }
+ }
+
+ return dataStruct;
+}
+
+bool IccHelper::tagsEqualToMatrix(const Matrix3x3& matrix, const uint8_t* red_tag,
+ const uint8_t* green_tag, const uint8_t* blue_tag) {
+ std::shared_ptr<DataStruct> red_tag_test =
+ write_xyz_tag(matrix.vals[0][0], matrix.vals[1][0], matrix.vals[2][0]);
+ std::shared_ptr<DataStruct> green_tag_test =
+ write_xyz_tag(matrix.vals[0][1], matrix.vals[1][1], matrix.vals[2][1]);
+ std::shared_ptr<DataStruct> blue_tag_test =
+ write_xyz_tag(matrix.vals[0][2], matrix.vals[1][2], matrix.vals[2][2]);
+ return memcmp(red_tag, red_tag_test->getData(), kColorantTagSize) == 0 &&
+ memcmp(green_tag, green_tag_test->getData(), kColorantTagSize) == 0 &&
+ memcmp(blue_tag, blue_tag_test->getData(), kColorantTagSize) == 0;
+}
+
+ultrahdr_color_gamut IccHelper::readIccColorGamut(void* icc_data, size_t icc_size) {
+ // Each tag table entry consists of 3 fields of 4 bytes each.
+ static const size_t kTagTableEntrySize = 12;
+
+ if (icc_data == nullptr || icc_size < sizeof(ICCHeader) + kICCIdentifierSize) {
+ return ULTRAHDR_COLORGAMUT_UNSPECIFIED;
+ }
+
+ if (memcmp(icc_data, kICCIdentifier, sizeof(kICCIdentifier)) != 0) {
+ return ULTRAHDR_COLORGAMUT_UNSPECIFIED;
+ }
+
+ uint8_t* icc_bytes = reinterpret_cast<uint8_t*>(icc_data) + kICCIdentifierSize;
+
+ ICCHeader* header = reinterpret_cast<ICCHeader*>(icc_bytes);
+
+ // Use 0 to indicate not found, since offsets are always relative to start
+ // of ICC data and therefore a tag offset of zero would never be valid.
+ size_t red_primary_offset = 0, green_primary_offset = 0, blue_primary_offset = 0;
+ size_t red_primary_size = 0, green_primary_size = 0, blue_primary_size = 0;
+ for (size_t tag_idx = 0; tag_idx < Endian_SwapBE32(header->tag_count); ++tag_idx) {
+ uint32_t* tag_entry_start =
+ reinterpret_cast<uint32_t*>(icc_bytes + sizeof(ICCHeader) + tag_idx * kTagTableEntrySize);
+ // first 4 bytes are the tag signature, next 4 bytes are the tag offset,
+ // last 4 bytes are the tag length in bytes.
+ if (red_primary_offset == 0 && *tag_entry_start == Endian_SwapBE32(kTAG_rXYZ)) {
+ red_primary_offset = Endian_SwapBE32(*(tag_entry_start + 1));
+ red_primary_size = Endian_SwapBE32(*(tag_entry_start + 2));
+ } else if (green_primary_offset == 0 && *tag_entry_start == Endian_SwapBE32(kTAG_gXYZ)) {
+ green_primary_offset = Endian_SwapBE32(*(tag_entry_start + 1));
+ green_primary_size = Endian_SwapBE32(*(tag_entry_start + 2));
+ } else if (blue_primary_offset == 0 && *tag_entry_start == Endian_SwapBE32(kTAG_bXYZ)) {
+ blue_primary_offset = Endian_SwapBE32(*(tag_entry_start + 1));
+ blue_primary_size = Endian_SwapBE32(*(tag_entry_start + 2));
+ }
+ }
+
+ if (red_primary_offset == 0 || red_primary_size != kColorantTagSize ||
+ kICCIdentifierSize + red_primary_offset + red_primary_size > icc_size ||
+ green_primary_offset == 0 || green_primary_size != kColorantTagSize ||
+ kICCIdentifierSize + green_primary_offset + green_primary_size > icc_size ||
+ blue_primary_offset == 0 || blue_primary_size != kColorantTagSize ||
+ kICCIdentifierSize + blue_primary_offset + blue_primary_size > icc_size) {
+ return ULTRAHDR_COLORGAMUT_UNSPECIFIED;
+ }
+
+ uint8_t* red_tag = icc_bytes + red_primary_offset;
+ uint8_t* green_tag = icc_bytes + green_primary_offset;
+ uint8_t* blue_tag = icc_bytes + blue_primary_offset;
+
+ // Serialize tags as we do on encode and compare what we find to that to
+ // determine the gamut (since we don't have a need yet for full deserialize).
+ if (tagsEqualToMatrix(kSRGB, red_tag, green_tag, blue_tag)) {
+ return ULTRAHDR_COLORGAMUT_BT709;
+ } else if (tagsEqualToMatrix(kDisplayP3, red_tag, green_tag, blue_tag)) {
+ return ULTRAHDR_COLORGAMUT_P3;
+ } else if (tagsEqualToMatrix(kRec2020, red_tag, green_tag, blue_tag)) {
+ return ULTRAHDR_COLORGAMUT_BT2100;
+ }
+
+ // Didn't find a match to one of the profiles we write; indicate the gamut
+ // is unspecified since we don't understand it.
+ return ULTRAHDR_COLORGAMUT_UNSPECIFIED;
+}
+
+} // namespace ultrahdr
diff --git a/lib/icc.h b/lib/icc.h
new file mode 100644
index 0000000..be7453e
--- /dev/null
+++ b/lib/icc.h
@@ -0,0 +1,259 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * 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.
+ */
+
+#ifndef ULTRAHDR_ICC_H
+#define ULTRAHDR_ICC_H
+
+#include <memory>
+
+#ifndef USE_BIG_ENDIAN_IN_ICC
+#define USE_BIG_ENDIAN_IN_ICC true
+#endif
+
+#if USE_BIG_ENDIAN_IN_ICC
+#define Endian_SwapBE32(n) EndianSwap32(n)
+#define Endian_SwapBE16(n) EndianSwap16(n)
+#else
+#define Endian_SwapBE32(n) (n)
+#define Endian_SwapBE16(n) (n)
+#endif
+
+#include "ultrahdr.h"
+#include "jpegr.h"
+#include "gainmapmath.h"
+#include "jpegrutils.h"
+
+namespace ultrahdr {
+
+typedef int32_t Fixed;
+#define Fixed1 (1 << 16)
+#define MaxS32FitsInFloat 2147483520
+#define MinS32FitsInFloat (-MaxS32FitsInFloat)
+#define FixedToFloat(x) ((x)*1.52587890625e-5f)
+
+typedef struct Matrix3x3 {
+ float vals[3][3];
+} Matrix3x3;
+
+// The D50 illuminant.
+constexpr float kD50_x = 0.9642f;
+constexpr float kD50_y = 1.0000f;
+constexpr float kD50_z = 0.8249f;
+
+enum {
+ // data_color_space
+ Signature_CMYK = 0x434D594B,
+ Signature_Gray = 0x47524159,
+ Signature_RGB = 0x52474220,
+
+ // pcs
+ Signature_Lab = 0x4C616220,
+ Signature_XYZ = 0x58595A20,
+};
+
+typedef uint32_t FourByteTag;
+static inline constexpr FourByteTag SetFourByteTag(char a, char b, char c, char d) {
+ return (((uint32_t)a << 24) | ((uint32_t)b << 16) | ((uint32_t)c << 8) | (uint32_t)d);
+}
+
+static constexpr char kICCIdentifier[] = "ICC_PROFILE";
+// 12 for the actual identifier, +2 for the chunk count and chunk index which
+// will always follow.
+static constexpr size_t kICCIdentifierSize = 14;
+
+// This is equal to the header size according to the ICC specification (128)
+// plus the size of the tag count (4). We include the tag count since we
+// always require it to be present anyway.
+static constexpr size_t kICCHeaderSize = 132;
+
+// Contains a signature (4), offset (4), and size (4).
+static constexpr size_t kICCTagTableEntrySize = 12;
+
+// size should be 20; 4 bytes for type descriptor, 4 bytes reserved, 12
+// bytes for a single XYZ number type (4 bytes per coordinate).
+static constexpr size_t kColorantTagSize = 20;
+
+static constexpr uint32_t kDisplay_Profile = SetFourByteTag('m', 'n', 't', 'r');
+static constexpr uint32_t kRGB_ColorSpace = SetFourByteTag('R', 'G', 'B', ' ');
+static constexpr uint32_t kXYZ_PCSSpace = SetFourByteTag('X', 'Y', 'Z', ' ');
+static constexpr uint32_t kACSP_Signature = SetFourByteTag('a', 'c', 's', 'p');
+
+static constexpr uint32_t kTAG_desc = SetFourByteTag('d', 'e', 's', 'c');
+static constexpr uint32_t kTAG_TextType = SetFourByteTag('m', 'l', 'u', 'c');
+static constexpr uint32_t kTAG_rXYZ = SetFourByteTag('r', 'X', 'Y', 'Z');
+static constexpr uint32_t kTAG_gXYZ = SetFourByteTag('g', 'X', 'Y', 'Z');
+static constexpr uint32_t kTAG_bXYZ = SetFourByteTag('b', 'X', 'Y', 'Z');
+static constexpr uint32_t kTAG_wtpt = SetFourByteTag('w', 't', 'p', 't');
+static constexpr uint32_t kTAG_rTRC = SetFourByteTag('r', 'T', 'R', 'C');
+static constexpr uint32_t kTAG_gTRC = SetFourByteTag('g', 'T', 'R', 'C');
+static constexpr uint32_t kTAG_bTRC = SetFourByteTag('b', 'T', 'R', 'C');
+static constexpr uint32_t kTAG_cicp = SetFourByteTag('c', 'i', 'c', 'p');
+static constexpr uint32_t kTAG_cprt = SetFourByteTag('c', 'p', 'r', 't');
+static constexpr uint32_t kTAG_A2B0 = SetFourByteTag('A', '2', 'B', '0');
+static constexpr uint32_t kTAG_B2A0 = SetFourByteTag('B', '2', 'A', '0');
+
+static constexpr uint32_t kTAG_CurveType = SetFourByteTag('c', 'u', 'r', 'v');
+static constexpr uint32_t kTAG_mABType = SetFourByteTag('m', 'A', 'B', ' ');
+static constexpr uint32_t kTAG_mBAType = SetFourByteTag('m', 'B', 'A', ' ');
+static constexpr uint32_t kTAG_ParaCurveType = SetFourByteTag('p', 'a', 'r', 'a');
+
+static constexpr Matrix3x3 kSRGB = {{
+ // ICC fixed-point (16.16) representation, taken from skcms. Please keep them exactly in sync.
+ // 0.436065674f, 0.385147095f, 0.143066406f,
+ // 0.222488403f, 0.716873169f, 0.060607910f,
+ // 0.013916016f, 0.097076416f, 0.714096069f,
+ {FixedToFloat(0x6FA2), FixedToFloat(0x6299), FixedToFloat(0x24A0)},
+ {FixedToFloat(0x38F5), FixedToFloat(0xB785), FixedToFloat(0x0F84)},
+ {FixedToFloat(0x0390), FixedToFloat(0x18DA), FixedToFloat(0xB6CF)},
+}};
+
+static constexpr Matrix3x3 kDisplayP3 = {{
+ {0.515102f, 0.291965f, 0.157153f},
+ {0.241182f, 0.692236f, 0.0665819f},
+ {-0.00104941f, 0.0418818f, 0.784378f},
+}};
+
+static constexpr Matrix3x3 kRec2020 = {{
+ {0.673459f, 0.165661f, 0.125100f},
+ {0.279033f, 0.675338f, 0.0456288f},
+ {-0.00193139f, 0.0299794f, 0.797162f},
+}};
+
+static constexpr uint32_t kCICPPrimariesSRGB = 1;
+static constexpr uint32_t kCICPPrimariesP3 = 12;
+static constexpr uint32_t kCICPPrimariesRec2020 = 9;
+
+static constexpr uint32_t kCICPTrfnSRGB = 1;
+static constexpr uint32_t kCICPTrfnLinear = 8;
+static constexpr uint32_t kCICPTrfnPQ = 16;
+static constexpr uint32_t kCICPTrfnHLG = 18;
+
+enum ParaCurveType {
+ kExponential_ParaCurveType = 0,
+ kGAB_ParaCurveType = 1,
+ kGABC_ParaCurveType = 2,
+ kGABDE_ParaCurveType = 3,
+ kGABCDEF_ParaCurveType = 4,
+};
+
+/**
+ * Return the closest int for the given float. Returns MaxS32FitsInFloat for NaN.
+ */
+static inline int float_saturate2int(float x) {
+ x = x < MaxS32FitsInFloat ? x : MaxS32FitsInFloat;
+ x = x > MinS32FitsInFloat ? x : MinS32FitsInFloat;
+ return (int)x;
+}
+
+static Fixed float_round_to_fixed(float x) {
+ return float_saturate2int((float)floor((double)x * Fixed1 + 0.5));
+}
+
+static uint16_t float_round_to_unorm16(float x) {
+ x = x * 65535.f + 0.5;
+ if (x > 65535) return 65535;
+ if (x < 0) return 0;
+ return static_cast<uint16_t>(x);
+}
+
+static inline void float_to_table16(const float f, uint8_t* table_16) {
+ *reinterpret_cast<uint16_t*>(table_16) = Endian_SwapBE16(float_round_to_unorm16(f));
+}
+
+static inline bool isfinitef_(float x) { return 0 == x * 0; }
+
+struct ICCHeader {
+ // Size of the profile (computed)
+ uint32_t size;
+ // Preferred CMM type (ignored)
+ uint32_t cmm_type = 0;
+ // Version 4.3 or 4.4 if CICP is included.
+ uint32_t version = Endian_SwapBE32(0x04300000);
+ // Display device profile
+ uint32_t profile_class = Endian_SwapBE32(kDisplay_Profile);
+ // RGB input color space;
+ uint32_t data_color_space = Endian_SwapBE32(kRGB_ColorSpace);
+ // Profile connection space.
+ uint32_t pcs = Endian_SwapBE32(kXYZ_PCSSpace);
+ // Date and time (ignored)
+ uint8_t creation_date_time[12] = {0};
+ // Profile signature
+ uint32_t signature = Endian_SwapBE32(kACSP_Signature);
+ // Platform target (ignored)
+ uint32_t platform = 0;
+ // Flags: not embedded, can be used independently
+ uint32_t flags = 0x00000000;
+ // Device manufacturer (ignored)
+ uint32_t device_manufacturer = 0;
+ // Device model (ignored)
+ uint32_t device_model = 0;
+ // Device attributes (ignored)
+ uint8_t device_attributes[8] = {0};
+ // Relative colorimetric rendering intent
+ uint32_t rendering_intent = Endian_SwapBE32(1);
+ // D50 standard illuminant (X, Y, Z)
+ uint32_t illuminant_X = Endian_SwapBE32(float_round_to_fixed(kD50_x));
+ uint32_t illuminant_Y = Endian_SwapBE32(float_round_to_fixed(kD50_y));
+ uint32_t illuminant_Z = Endian_SwapBE32(float_round_to_fixed(kD50_z));
+ // Profile creator (ignored)
+ uint32_t creator = 0;
+ // Profile id checksum (ignored)
+ uint8_t profile_id[16] = {0};
+ // Reserved (ignored)
+ uint8_t reserved[28] = {0};
+ // Technically not part of header, but required
+ uint32_t tag_count = 0;
+};
+
+class IccHelper {
+ private:
+ static constexpr uint32_t kTrcTableSize = 65;
+ static constexpr uint32_t kGridSize = 17;
+ static constexpr size_t kNumChannels = 3;
+
+ static std::shared_ptr<DataStruct> write_text_tag(const char* text);
+ static std::string get_desc_string(const ultrahdr_transfer_function tf,
+ const ultrahdr_color_gamut gamut);
+ static std::shared_ptr<DataStruct> write_xyz_tag(float x, float y, float z);
+ static std::shared_ptr<DataStruct> write_trc_tag(const int table_entries, const void* table_16);
+ static std::shared_ptr<DataStruct> write_trc_tag(const TransferFunction& fn);
+ static float compute_tone_map_gain(const ultrahdr_transfer_function tf, float L);
+ static std::shared_ptr<DataStruct> write_cicp_tag(uint32_t color_primaries,
+ uint32_t transfer_characteristics);
+ static std::shared_ptr<DataStruct> write_mAB_or_mBA_tag(uint32_t type, bool has_a_curves,
+ const uint8_t* grid_points,
+ const uint8_t* grid_16);
+ static void compute_lut_entry(const Matrix3x3& src_to_XYZD50, float rgb[3]);
+ static std::shared_ptr<DataStruct> write_clut(const uint8_t* grid_points, const uint8_t* grid_16);
+
+ // Checks if a set of xyz tags is equivalent to a 3x3 Matrix. Each input
+ // tag buffer assumed to be at least kColorantTagSize in size.
+ static bool tagsEqualToMatrix(const Matrix3x3& matrix, const uint8_t* red_tag,
+ const uint8_t* green_tag, const uint8_t* blue_tag);
+
+ public:
+ // Output includes JPEG embedding identifier and chunk information, but not
+ // APPx information.
+ static std::shared_ptr<DataStruct> writeIccProfile(const ultrahdr_transfer_function tf,
+ const ultrahdr_color_gamut gamut);
+ // NOTE: this function is not robust; it can infer gamuts that IccHelper
+ // writes out but should not be considered a reference implementation for
+ // robust parsing of ICC profiles or their gamuts.
+ static ultrahdr_color_gamut readIccColorGamut(void* icc_data, size_t icc_size);
+};
+} // namespace ultrahdr
+
+#endif // ULTRAHDR_ICC_H
diff --git a/lib/jpegdecoderhelper.cpp b/lib/jpegdecoderhelper.cpp
new file mode 100644
index 0000000..eb55a2e
--- /dev/null
+++ b/lib/jpegdecoderhelper.cpp
@@ -0,0 +1,537 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * 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.
+ */
+
+#include <errno.h>
+#include <setjmp.h>
+
+#include <cstring>
+
+#include "ultrahdrcommon.h"
+#include "ultrahdr.h"
+#include "jpegdecoderhelper.h"
+
+using namespace std;
+
+namespace ultrahdr {
+
+const uint32_t kAPP0Marker = JPEG_APP0; // JFIF
+const uint32_t kAPP1Marker = JPEG_APP0 + 1; // EXIF, XMP
+const uint32_t kAPP2Marker = JPEG_APP0 + 2; // ICC
+
+constexpr uint32_t kICCMarkerHeaderSize = 14;
+constexpr uint8_t kICCSig[] = {
+ 'I', 'C', 'C', '_', 'P', 'R', 'O', 'F', 'I', 'L', 'E', '\0',
+};
+constexpr uint8_t kXmpNameSpace[] = {
+ 'h', 't', 't', 'p', ':', '/', '/', 'n', 's', '.', 'a', 'd', 'o', 'b', 'e',
+ '.', 'c', 'o', 'm', '/', 'x', 'a', 'p', '/', '1', '.', '0', '/', '\0',
+};
+constexpr uint8_t kExifIdCode[] = {
+ 'E', 'x', 'i', 'f', '\0', '\0',
+};
+
+struct jpegr_source_mgr : jpeg_source_mgr {
+ jpegr_source_mgr(const uint8_t* ptr, int len);
+ ~jpegr_source_mgr();
+
+ const uint8_t* mBufferPtr;
+ size_t mBufferLength;
+};
+
+struct jpegrerror_mgr {
+ struct jpeg_error_mgr pub;
+ jmp_buf setjmp_buffer;
+};
+
+static void jpegr_init_source(j_decompress_ptr cinfo) {
+ jpegr_source_mgr* src = static_cast<jpegr_source_mgr*>(cinfo->src);
+ src->next_input_byte = static_cast<const JOCTET*>(src->mBufferPtr);
+ src->bytes_in_buffer = src->mBufferLength;
+}
+
+static boolean jpegr_fill_input_buffer(j_decompress_ptr /* cinfo */) {
+ ALOGE("%s : should not get here", __func__);
+ return FALSE;
+}
+
+static void jpegr_skip_input_data(j_decompress_ptr cinfo, long num_bytes) {
+ jpegr_source_mgr* src = static_cast<jpegr_source_mgr*>(cinfo->src);
+
+ if (num_bytes > static_cast<long>(src->bytes_in_buffer)) {
+ ALOGE("jpegr_skip_input_data - num_bytes > (long)src->bytes_in_buffer");
+ } else {
+ src->next_input_byte += num_bytes;
+ src->bytes_in_buffer -= num_bytes;
+ }
+}
+
+static void jpegr_term_source(j_decompress_ptr /*cinfo*/) {}
+
+jpegr_source_mgr::jpegr_source_mgr(const uint8_t* ptr, int len)
+ : mBufferPtr(ptr), mBufferLength(len) {
+ init_source = jpegr_init_source;
+ fill_input_buffer = jpegr_fill_input_buffer;
+ skip_input_data = jpegr_skip_input_data;
+ resync_to_restart = jpeg_resync_to_restart;
+ term_source = jpegr_term_source;
+}
+
+jpegr_source_mgr::~jpegr_source_mgr() {}
+
+static void jpegrerror_exit(j_common_ptr cinfo) {
+ jpegrerror_mgr* err = reinterpret_cast<jpegrerror_mgr*>(cinfo->err);
+ longjmp(err->setjmp_buffer, 1);
+}
+
+static void output_message(j_common_ptr cinfo) {
+ char buffer[JMSG_LENGTH_MAX];
+
+ /* Create the message */
+ (*cinfo->err->format_message)(cinfo, buffer);
+ ALOGE("%s\n", buffer);
+}
+
+JpegDecoderHelper::JpegDecoderHelper() {}
+
+JpegDecoderHelper::~JpegDecoderHelper() {}
+
+bool JpegDecoderHelper::decompressImage(const void* image, int length, bool decodeToRGBA) {
+ if (image == nullptr || length <= 0) {
+ ALOGE("Image size can not be handled: %d", length);
+ return false;
+ }
+ mResultBuffer.clear();
+ mXMPBuffer.clear();
+ return decode(image, length, decodeToRGBA);
+}
+
+void* JpegDecoderHelper::getDecompressedImagePtr() { return mResultBuffer.data(); }
+
+size_t JpegDecoderHelper::getDecompressedImageSize() { return mResultBuffer.size(); }
+
+void* JpegDecoderHelper::getXMPPtr() { return mXMPBuffer.data(); }
+
+size_t JpegDecoderHelper::getXMPSize() { return mXMPBuffer.size(); }
+
+void* JpegDecoderHelper::getEXIFPtr() { return mEXIFBuffer.data(); }
+
+size_t JpegDecoderHelper::getEXIFSize() { return mEXIFBuffer.size(); }
+
+void* JpegDecoderHelper::getICCPtr() { return mICCBuffer.data(); }
+
+size_t JpegDecoderHelper::getICCSize() { return mICCBuffer.size(); }
+
+size_t JpegDecoderHelper::getDecompressedImageWidth() { return mWidth; }
+
+size_t JpegDecoderHelper::getDecompressedImageHeight() { return mHeight; }
+
+// Here we only handle the first EXIF package, and in theary EXIF (or JFIF) must be the first
+// in the image file.
+// We assume that all packages are starting with two bytes marker (eg FF E1 for EXIF package),
+// two bytes of package length which is stored in marker->original_length, and the real data
+// which is stored in marker->data.
+bool JpegDecoderHelper::extractEXIF(const void* image, int length) {
+ jpeg_decompress_struct cinfo;
+ jpegr_source_mgr mgr(static_cast<const uint8_t*>(image), length);
+ jpegrerror_mgr myerr;
+
+ cinfo.err = jpeg_std_error(&myerr.pub);
+ myerr.pub.error_exit = jpegrerror_exit;
+ myerr.pub.output_message = output_message;
+
+ if (setjmp(myerr.setjmp_buffer)) {
+ jpeg_destroy_decompress(&cinfo);
+ return false;
+ }
+ jpeg_create_decompress(&cinfo);
+
+ jpeg_save_markers(&cinfo, kAPP0Marker, 0xFFFF);
+ jpeg_save_markers(&cinfo, kAPP1Marker, 0xFFFF);
+
+ cinfo.src = &mgr;
+ jpeg_read_header(&cinfo, TRUE);
+
+ size_t pos = 2; // position after SOI
+ for (jpeg_marker_struct* marker = cinfo.marker_list; marker; marker = marker->next) {
+ pos += 4;
+ pos += marker->original_length;
+
+ if (marker->marker != kAPP1Marker) {
+ continue;
+ }
+
+ const unsigned int len = marker->data_length;
+
+ if (len > sizeof(kExifIdCode) && !memcmp(marker->data, kExifIdCode, sizeof(kExifIdCode))) {
+ mEXIFBuffer.resize(len, 0);
+ memcpy(static_cast<void*>(mEXIFBuffer.data()), marker->data, len);
+ mExifPos = pos - marker->original_length;
+ break;
+ }
+ }
+
+ jpeg_destroy_decompress(&cinfo);
+ return true;
+}
+
+bool JpegDecoderHelper::decode(const void* image, int length, bool decodeToRGBA) {
+ bool status = true;
+ jpeg_decompress_struct cinfo;
+ jpegrerror_mgr myerr;
+ cinfo.err = jpeg_std_error(&myerr.pub);
+ myerr.pub.error_exit = jpegrerror_exit;
+ myerr.pub.output_message = output_message;
+
+ if (setjmp(myerr.setjmp_buffer)) {
+ jpeg_destroy_decompress(&cinfo);
+ return false;
+ }
+
+ jpeg_create_decompress(&cinfo);
+
+ jpeg_save_markers(&cinfo, kAPP0Marker, 0xFFFF);
+ jpeg_save_markers(&cinfo, kAPP1Marker, 0xFFFF);
+ jpeg_save_markers(&cinfo, kAPP2Marker, 0xFFFF);
+
+ jpegr_source_mgr mgr(static_cast<const uint8_t*>(image), length);
+ cinfo.src = &mgr;
+ if (jpeg_read_header(&cinfo, TRUE) != JPEG_HEADER_OK) {
+ jpeg_destroy_decompress(&cinfo);
+ return false;
+ }
+
+ // Save XMP data, EXIF data, and ICC data.
+ // Here we only handle the first XMP / EXIF / ICC package.
+ // We assume that all packages are starting with two bytes marker (eg FF E1 for EXIF package),
+ // two bytes of package length which is stored in marker->original_length, and the real data
+ // which is stored in marker->data.
+ bool exifAppears = false;
+ bool xmpAppears = false;
+ bool iccAppears = false;
+ size_t pos = 2; // position after SOI
+ for (jpeg_marker_struct* marker = cinfo.marker_list;
+ marker && !(exifAppears && xmpAppears && iccAppears); marker = marker->next) {
+ pos += 4;
+ pos += marker->original_length;
+ if (marker->marker != kAPP1Marker && marker->marker != kAPP2Marker) {
+ continue;
+ }
+ const unsigned int len = marker->data_length;
+ if (!xmpAppears && len > sizeof(kXmpNameSpace) &&
+ !memcmp(marker->data, kXmpNameSpace, sizeof(kXmpNameSpace))) {
+ mXMPBuffer.resize(len + 1, 0);
+ memcpy(static_cast<void*>(mXMPBuffer.data()), marker->data, len);
+ xmpAppears = true;
+ } else if (!exifAppears && len > sizeof(kExifIdCode) &&
+ !memcmp(marker->data, kExifIdCode, sizeof(kExifIdCode))) {
+ mEXIFBuffer.resize(len, 0);
+ memcpy(static_cast<void*>(mEXIFBuffer.data()), marker->data, len);
+ exifAppears = true;
+ mExifPos = pos - marker->original_length;
+ } else if (!iccAppears && len > sizeof(kICCSig) &&
+ !memcmp(marker->data, kICCSig, sizeof(kICCSig))) {
+ mICCBuffer.resize(len, 0);
+ memcpy(static_cast<void*>(mICCBuffer.data()), marker->data, len);
+ iccAppears = true;
+ }
+ }
+
+ mWidth = cinfo.image_width;
+ mHeight = cinfo.image_height;
+ if (mWidth > kMaxWidth || mHeight > kMaxHeight) {
+ status = false;
+ goto CleanUp;
+ }
+
+ if (decodeToRGBA) {
+ // The primary image is expected to be yuv420 sampling
+ if (cinfo.jpeg_color_space != JCS_YCbCr) {
+ status = false;
+ ALOGE("%s: decodeToRGBA unexpected jpeg color space ", __func__);
+ goto CleanUp;
+ }
+ if (cinfo.comp_info[0].h_samp_factor != 2 || cinfo.comp_info[0].v_samp_factor != 2 ||
+ cinfo.comp_info[1].h_samp_factor != 1 || cinfo.comp_info[1].v_samp_factor != 1 ||
+ cinfo.comp_info[2].h_samp_factor != 1 || cinfo.comp_info[2].v_samp_factor != 1) {
+ status = false;
+ ALOGE("%s: decodeToRGBA unexpected primary image sub-sampling", __func__);
+ goto CleanUp;
+ }
+ // 4 bytes per pixel
+ mResultBuffer.resize(cinfo.image_width * cinfo.image_height * 4);
+ cinfo.out_color_space = JCS_EXT_RGBA;
+ } else {
+ if (cinfo.jpeg_color_space == JCS_YCbCr) {
+ if (cinfo.comp_info[0].h_samp_factor != 2 || cinfo.comp_info[0].v_samp_factor != 2 ||
+ cinfo.comp_info[1].h_samp_factor != 1 || cinfo.comp_info[1].v_samp_factor != 1 ||
+ cinfo.comp_info[2].h_samp_factor != 1 || cinfo.comp_info[2].v_samp_factor != 1) {
+ status = false;
+ ALOGE("%s: decoding to YUV only supports 4:2:0 subsampling", __func__);
+ goto CleanUp;
+ }
+ mResultBuffer.resize(cinfo.image_width * cinfo.image_height * 3 / 2, 0);
+ } else if (cinfo.jpeg_color_space == JCS_GRAYSCALE) {
+ mResultBuffer.resize(cinfo.image_width * cinfo.image_height, 0);
+ } else {
+ status = false;
+ ALOGE("%s: decodeToYUV unexpected jpeg color space", __func__);
+ goto CleanUp;
+ }
+ cinfo.out_color_space = cinfo.jpeg_color_space;
+ cinfo.raw_data_out = TRUE;
+ }
+
+ cinfo.dct_method = JDCT_ISLOW;
+ jpeg_start_decompress(&cinfo);
+ if (!decompress(&cinfo, static_cast<const uint8_t*>(mResultBuffer.data()),
+ cinfo.jpeg_color_space == JCS_GRAYSCALE)) {
+ status = false;
+ goto CleanUp;
+ }
+
+CleanUp:
+ jpeg_finish_decompress(&cinfo);
+ jpeg_destroy_decompress(&cinfo);
+
+ return status;
+}
+
+bool JpegDecoderHelper::decompress(jpeg_decompress_struct* cinfo, const uint8_t* dest,
+ bool isSingleChannel) {
+ return isSingleChannel ? decompressSingleChannel(cinfo, dest)
+ : ((cinfo->out_color_space == JCS_EXT_RGBA) ? decompressRGBA(cinfo, dest)
+ : decompressYUV(cinfo, dest));
+}
+
+bool JpegDecoderHelper::getCompressedImageParameters(const void* image, int length, size_t* pWidth,
+ size_t* pHeight, std::vector<uint8_t>* iccData,
+ std::vector<uint8_t>* exifData) {
+ jpeg_decompress_struct cinfo;
+ jpegrerror_mgr myerr;
+ cinfo.err = jpeg_std_error(&myerr.pub);
+ myerr.pub.error_exit = jpegrerror_exit;
+ myerr.pub.output_message = output_message;
+
+ if (setjmp(myerr.setjmp_buffer)) {
+ jpeg_destroy_decompress(&cinfo);
+ return false;
+ }
+ jpeg_create_decompress(&cinfo);
+
+ jpeg_save_markers(&cinfo, kAPP1Marker, 0xFFFF);
+ jpeg_save_markers(&cinfo, kAPP2Marker, 0xFFFF);
+
+ jpegr_source_mgr mgr(static_cast<const uint8_t*>(image), length);
+ cinfo.src = &mgr;
+ if (jpeg_read_header(&cinfo, TRUE) != JPEG_HEADER_OK) {
+ jpeg_destroy_decompress(&cinfo);
+ return false;
+ }
+
+ if (pWidth != nullptr) {
+ *pWidth = cinfo.image_width;
+ }
+ if (pHeight != nullptr) {
+ *pHeight = cinfo.image_height;
+ }
+
+ if (iccData != nullptr) {
+ for (jpeg_marker_struct* marker = cinfo.marker_list; marker; marker = marker->next) {
+ if (marker->marker != kAPP2Marker) {
+ continue;
+ }
+ if (marker->data_length <= kICCMarkerHeaderSize ||
+ memcmp(marker->data, kICCSig, sizeof(kICCSig)) != 0) {
+ continue;
+ }
+
+ iccData->insert(iccData->end(), marker->data, marker->data + marker->data_length);
+ }
+ }
+
+ if (exifData != nullptr) {
+ bool exifAppears = false;
+ for (jpeg_marker_struct* marker = cinfo.marker_list; marker && !exifAppears;
+ marker = marker->next) {
+ if (marker->marker != kAPP1Marker) {
+ continue;
+ }
+
+ const unsigned int len = marker->data_length;
+ if (len >= sizeof(kExifIdCode) && !memcmp(marker->data, kExifIdCode, sizeof(kExifIdCode))) {
+ exifData->resize(len, 0);
+ memcpy(static_cast<void*>(exifData->data()), marker->data, len);
+ exifAppears = true;
+ }
+ }
+ }
+
+ jpeg_destroy_decompress(&cinfo);
+ return true;
+}
+
+bool JpegDecoderHelper::decompressRGBA(jpeg_decompress_struct* cinfo, const uint8_t* dest) {
+ JSAMPLE* out = (JSAMPLE*)dest;
+
+ while (cinfo->output_scanline < cinfo->image_height) {
+ if (1 != jpeg_read_scanlines(cinfo, &out, 1)) return false;
+ out += cinfo->image_width * 4;
+ }
+ return true;
+}
+
+bool JpegDecoderHelper::decompressYUV(jpeg_decompress_struct* cinfo, const uint8_t* dest) {
+ size_t luma_plane_size = cinfo->image_width * cinfo->image_height;
+ size_t chroma_plane_size = luma_plane_size / 4;
+ uint8_t* y_plane = const_cast<uint8_t*>(dest);
+ uint8_t* u_plane = const_cast<uint8_t*>(dest + luma_plane_size);
+ uint8_t* v_plane = const_cast<uint8_t*>(dest + luma_plane_size + chroma_plane_size);
+
+ const size_t aligned_width = ALIGNM(cinfo->image_width, kCompressBatchSize);
+ const bool is_width_aligned = (aligned_width == cinfo->image_width);
+ uint8_t* y_plane_intrm = nullptr;
+ uint8_t* u_plane_intrm = nullptr;
+ uint8_t* v_plane_intrm = nullptr;
+
+ JSAMPROW y[kCompressBatchSize];
+ JSAMPROW cb[kCompressBatchSize / 2];
+ JSAMPROW cr[kCompressBatchSize / 2];
+ JSAMPARRAY planes[3]{y, cb, cr};
+ JSAMPROW y_intrm[kCompressBatchSize];
+ JSAMPROW cb_intrm[kCompressBatchSize / 2];
+ JSAMPROW cr_intrm[kCompressBatchSize / 2];
+ JSAMPARRAY planes_intrm[3]{y_intrm, cb_intrm, cr_intrm};
+
+ if (cinfo->image_height % kCompressBatchSize != 0) {
+ mEmpty = std::make_unique<uint8_t[]>(aligned_width);
+ }
+
+ if (!is_width_aligned) {
+ size_t mcu_row_size = aligned_width * kCompressBatchSize * 3 / 2;
+ mBufferIntermediate = std::make_unique<uint8_t[]>(mcu_row_size);
+ y_plane_intrm = mBufferIntermediate.get();
+ u_plane_intrm = y_plane_intrm + (aligned_width * kCompressBatchSize);
+ v_plane_intrm = u_plane_intrm + (aligned_width * kCompressBatchSize) / 4;
+ for (int i = 0; i < kCompressBatchSize; ++i) {
+ y_intrm[i] = y_plane_intrm + i * aligned_width;
+ }
+ for (int i = 0; i < kCompressBatchSize / 2; ++i) {
+ int offset_intrm = i * (aligned_width / 2);
+ cb_intrm[i] = u_plane_intrm + offset_intrm;
+ cr_intrm[i] = v_plane_intrm + offset_intrm;
+ }
+ }
+
+ while (cinfo->output_scanline < cinfo->image_height) {
+ size_t scanline_copy = cinfo->output_scanline;
+ for (int i = 0; i < kCompressBatchSize; ++i) {
+ size_t scanline = cinfo->output_scanline + i;
+ if (scanline < cinfo->image_height) {
+ y[i] = y_plane + scanline * cinfo->image_width;
+ } else {
+ y[i] = mEmpty.get();
+ }
+ }
+ // cb, cr only have half scanlines
+ for (int i = 0; i < kCompressBatchSize / 2; ++i) {
+ size_t scanline = cinfo->output_scanline / 2 + i;
+ if (scanline < cinfo->image_height / 2) {
+ int offset = scanline * (cinfo->image_width / 2);
+ cb[i] = u_plane + offset;
+ cr[i] = v_plane + offset;
+ } else {
+ cb[i] = cr[i] = mEmpty.get();
+ }
+ }
+
+ int processed =
+ jpeg_read_raw_data(cinfo, is_width_aligned ? planes : planes_intrm, kCompressBatchSize);
+ if (processed != kCompressBatchSize) {
+ ALOGE("Number of processed lines does not equal input lines.");
+ return false;
+ }
+ if (!is_width_aligned) {
+ for (int i = 0; i < kCompressBatchSize; ++i) {
+ if (scanline_copy + i < cinfo->image_height) {
+ memcpy(y[i], y_intrm[i], cinfo->image_width);
+ }
+ }
+ for (int i = 0; i < kCompressBatchSize / 2; ++i) {
+ if (((scanline_copy / 2) + i) < (cinfo->image_height / 2)) {
+ memcpy(cb[i], cb_intrm[i], cinfo->image_width / 2);
+ memcpy(cr[i], cr_intrm[i], cinfo->image_width / 2);
+ }
+ }
+ }
+ }
+ return true;
+}
+
+bool JpegDecoderHelper::decompressSingleChannel(jpeg_decompress_struct* cinfo,
+ const uint8_t* dest) {
+ uint8_t* y_plane = const_cast<uint8_t*>(dest);
+ uint8_t* y_plane_intrm = nullptr;
+
+ const size_t aligned_width = ALIGNM(cinfo->image_width, kCompressBatchSize);
+ const bool is_width_aligned = (aligned_width == cinfo->image_width);
+
+ JSAMPROW y[kCompressBatchSize];
+ JSAMPARRAY planes[1]{y};
+ JSAMPROW y_intrm[kCompressBatchSize];
+ JSAMPARRAY planes_intrm[1]{y_intrm};
+
+ if (cinfo->image_height % kCompressBatchSize != 0) {
+ mEmpty = std::make_unique<uint8_t[]>(aligned_width);
+ }
+
+ if (!is_width_aligned) {
+ size_t mcu_row_size = aligned_width * kCompressBatchSize;
+ mBufferIntermediate = std::make_unique<uint8_t[]>(mcu_row_size);
+ y_plane_intrm = mBufferIntermediate.get();
+ for (int i = 0; i < kCompressBatchSize; ++i) {
+ y_intrm[i] = y_plane_intrm + i * aligned_width;
+ }
+ }
+
+ while (cinfo->output_scanline < cinfo->image_height) {
+ size_t scanline_copy = cinfo->output_scanline;
+ for (int i = 0; i < kCompressBatchSize; ++i) {
+ size_t scanline = cinfo->output_scanline + i;
+ if (scanline < cinfo->image_height) {
+ y[i] = y_plane + scanline * cinfo->image_width;
+ } else {
+ y[i] = mEmpty.get();
+ }
+ }
+
+ int processed =
+ jpeg_read_raw_data(cinfo, is_width_aligned ? planes : planes_intrm, kCompressBatchSize);
+ if (processed != kCompressBatchSize / 2) {
+ ALOGE("Number of processed lines does not equal input lines.");
+ return false;
+ }
+ if (!is_width_aligned) {
+ for (int i = 0; i < kCompressBatchSize; ++i) {
+ if (scanline_copy + i < cinfo->image_height) {
+ memcpy(y[i], y_intrm[i], cinfo->image_width);
+ }
+ }
+ }
+ }
+ return true;
+}
+
+} // namespace ultrahdr
diff --git a/lib/jpegdecoderhelper.h b/lib/jpegdecoderhelper.h
new file mode 100644
index 0000000..01a05e4
--- /dev/null
+++ b/lib/jpegdecoderhelper.h
@@ -0,0 +1,154 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * 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.
+ */
+
+#ifndef ULTRAHDR_JPEGDECODERHELPER_H
+#define ULTRAHDR_JPEGDECODERHELPER_H
+
+#include <stdio.h> // For jpeglib.h.
+
+// C++ build requires extern C for jpeg internals.
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <jerror.h>
+#include <jpeglib.h>
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#include <cstdint>
+#include <memory>
+#include <vector>
+
+// constraint on max width and max height is only due to device alloc constraints
+// Can tune these values basing on the target device
+static const int kMaxWidth = 8192;
+static const int kMaxHeight = 8192;
+
+namespace ultrahdr {
+/*
+ * Encapsulates a converter from JPEG to raw image (YUV420planer or grey-scale) format.
+ * This class is not thread-safe.
+ */
+class JpegDecoderHelper {
+ public:
+ JpegDecoderHelper();
+ ~JpegDecoderHelper();
+ /*
+ * Decompresses JPEG image to raw image (YUV420planer, grey-scale or RGBA) format. After
+ * calling this method, call getDecompressedImage() to get the image.
+ * Returns false if decompressing the image fails.
+ */
+ bool decompressImage(const void* image, int length, bool decodeToRGBA = false);
+ /*
+ * Returns the decompressed raw image buffer pointer. This method must be called only after
+ * calling decompressImage().
+ */
+ void* getDecompressedImagePtr();
+ /*
+ * Returns the decompressed raw image buffer size. This method must be called only after
+ * calling decompressImage().
+ */
+ size_t getDecompressedImageSize();
+ /*
+ * Returns the image width in pixels. This method must be called only after calling
+ * decompressImage().
+ */
+ size_t getDecompressedImageWidth();
+ /*
+ * Returns the image width in pixels. This method must be called only after calling
+ * decompressImage().
+ */
+ size_t getDecompressedImageHeight();
+ /*
+ * Returns the XMP data from the image.
+ */
+ void* getXMPPtr();
+ /*
+ * Returns the decompressed XMP buffer size. This method must be called only after
+ * calling decompressImage() or getCompressedImageParameters().
+ */
+ size_t getXMPSize();
+ /*
+ * Extracts EXIF package and updates the EXIF position / length without decoding the image.
+ */
+ bool extractEXIF(const void* image, int length);
+ /*
+ * Returns the EXIF data from the image.
+ * This method must be called after extractEXIF() or decompressImage().
+ */
+ void* getEXIFPtr();
+ /*
+ * Returns the decompressed EXIF buffer size. This method must be called only after
+ * calling decompressImage(), extractEXIF() or getCompressedImageParameters().
+ */
+ size_t getEXIFSize();
+ /*
+ * Returns the position offset of EXIF package
+ * (4 bypes offset to FF sign, the byte after FF E1 XX XX <this byte>),
+ * or -1 if no EXIF exists.
+ * This method must be called after extractEXIF() or decompressImage().
+ */
+ int getEXIFPos() { return mExifPos; }
+ /*
+ * Returns the ICC data from the image.
+ */
+ void* getICCPtr();
+ /*
+ * Returns the decompressed ICC buffer size. This method must be called only after
+ * calling decompressImage() or getCompressedImageParameters().
+ */
+ size_t getICCSize();
+ /*
+ * Decompresses metadata of the image. All vectors are owned by the caller.
+ */
+ bool getCompressedImageParameters(const void* image, int length, size_t* pWidth, size_t* pHeight,
+ std::vector<uint8_t>* iccData, std::vector<uint8_t>* exifData);
+
+ private:
+ bool decode(const void* image, int length, bool decodeToRGBA);
+ // Returns false if errors occur.
+ bool decompress(jpeg_decompress_struct* cinfo, const uint8_t* dest, bool isSingleChannel);
+ bool decompressYUV(jpeg_decompress_struct* cinfo, const uint8_t* dest);
+ bool decompressRGBA(jpeg_decompress_struct* cinfo, const uint8_t* dest);
+ bool decompressSingleChannel(jpeg_decompress_struct* cinfo, const uint8_t* dest);
+ // Process 16 lines of Y and 16 lines of U/V each time.
+ // We must pass at least 16 scanlines according to libjpeg documentation.
+ static const int kCompressBatchSize = 16;
+ // The buffer that holds the decompressed result.
+ std::vector<JOCTET> mResultBuffer;
+ // The buffer that holds XMP Data.
+ std::vector<JOCTET> mXMPBuffer;
+ // The buffer that holds EXIF Data.
+ std::vector<JOCTET> mEXIFBuffer;
+ // The buffer that holds ICC Data.
+ std::vector<JOCTET> mICCBuffer;
+
+ // Resolution of the decompressed image.
+ size_t mWidth;
+ size_t mHeight;
+
+ // Position of EXIF package, default value is -1 which means no EXIF package appears.
+ int mExifPos = -1;
+
+ std::unique_ptr<uint8_t[]> mEmpty = nullptr;
+ std::unique_ptr<uint8_t[]> mBufferIntermediate = nullptr;
+};
+} /* namespace ultrahdr */
+
+#endif // ULTRAHDR_JPEGDECODERHELPER_H
diff --git a/lib/jpegencoderhelper.cpp b/lib/jpegencoderhelper.cpp
new file mode 100644
index 0000000..c6f0b77
--- /dev/null
+++ b/lib/jpegencoderhelper.cpp
@@ -0,0 +1,287 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * 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.
+ */
+
+#include <cstring>
+#include <memory>
+#include <string>
+
+#include "ultrahdrcommon.h"
+#include "ultrahdr.h"
+#include "jpegencoderhelper.h"
+
+namespace ultrahdr {
+
+// The destination manager that can access |mResultBuffer| in JpegEncoderHelper.
+struct destination_mgr {
+ struct jpeg_destination_mgr mgr;
+ JpegEncoderHelper* encoder;
+};
+
+JpegEncoderHelper::JpegEncoderHelper() {}
+
+JpegEncoderHelper::~JpegEncoderHelper() {}
+
+bool JpegEncoderHelper::compressImage(const uint8_t* yBuffer, const uint8_t* uvBuffer, int width,
+ int height, int lumaStride, int chromaStride, int quality,
+ const void* iccBuffer, unsigned int iccSize) {
+ mResultBuffer.clear();
+ if (!encode(yBuffer, uvBuffer, width, height, lumaStride, chromaStride, quality, iccBuffer,
+ iccSize)) {
+ return false;
+ }
+ ALOGV("Compressed JPEG: %d[%dx%d] -> %zu bytes", (width * height * 12) / 8, width, height,
+ mResultBuffer.size());
+ return true;
+}
+
+void* JpegEncoderHelper::getCompressedImagePtr() { return mResultBuffer.data(); }
+
+size_t JpegEncoderHelper::getCompressedImageSize() { return mResultBuffer.size(); }
+
+void JpegEncoderHelper::initDestination(j_compress_ptr cinfo) {
+ destination_mgr* dest = reinterpret_cast<destination_mgr*>(cinfo->dest);
+ std::vector<JOCTET>& buffer = dest->encoder->mResultBuffer;
+ buffer.resize(kBlockSize);
+ dest->mgr.next_output_byte = &buffer[0];
+ dest->mgr.free_in_buffer = buffer.size();
+}
+
+boolean JpegEncoderHelper::emptyOutputBuffer(j_compress_ptr cinfo) {
+ destination_mgr* dest = reinterpret_cast<destination_mgr*>(cinfo->dest);
+ std::vector<JOCTET>& buffer = dest->encoder->mResultBuffer;
+ size_t oldsize = buffer.size();
+ buffer.resize(oldsize + kBlockSize);
+ dest->mgr.next_output_byte = &buffer[oldsize];
+ dest->mgr.free_in_buffer = kBlockSize;
+ return true;
+}
+
+void JpegEncoderHelper::terminateDestination(j_compress_ptr cinfo) {
+ destination_mgr* dest = reinterpret_cast<destination_mgr*>(cinfo->dest);
+ std::vector<JOCTET>& buffer = dest->encoder->mResultBuffer;
+ buffer.resize(buffer.size() - dest->mgr.free_in_buffer);
+}
+
+void JpegEncoderHelper::outputErrorMessage(j_common_ptr cinfo) {
+ char buffer[JMSG_LENGTH_MAX];
+
+ /* Create the message */
+ (*cinfo->err->format_message)(cinfo, buffer);
+ ALOGE("%s\n", buffer);
+}
+
+bool JpegEncoderHelper::encode(const uint8_t* yBuffer, const uint8_t* uvBuffer, int width,
+ int height, int lumaStride, int chromaStride, int quality,
+ const void* iccBuffer, unsigned int iccSize) {
+ jpeg_compress_struct cinfo;
+ jpeg_error_mgr jerr;
+
+ cinfo.err = jpeg_std_error(&jerr);
+ cinfo.err->output_message = &outputErrorMessage;
+ jpeg_create_compress(&cinfo);
+ setJpegDestination(&cinfo);
+ setJpegCompressStruct(width, height, quality, &cinfo, uvBuffer == nullptr);
+ jpeg_start_compress(&cinfo, TRUE);
+ if (iccBuffer != nullptr && iccSize > 0) {
+ jpeg_write_marker(&cinfo, JPEG_APP0 + 2, static_cast<const JOCTET*>(iccBuffer), iccSize);
+ }
+ bool status = cinfo.num_components == 1
+ ? compressY(&cinfo, yBuffer, lumaStride)
+ : compressYuv(&cinfo, yBuffer, uvBuffer, lumaStride, chromaStride);
+ jpeg_finish_compress(&cinfo);
+ jpeg_destroy_compress(&cinfo);
+
+ return status;
+}
+
+void JpegEncoderHelper::setJpegDestination(jpeg_compress_struct* cinfo) {
+ destination_mgr* dest = static_cast<struct destination_mgr*>(
+ (*cinfo->mem->alloc_small)((j_common_ptr)cinfo, JPOOL_PERMANENT, sizeof(destination_mgr)));
+ dest->encoder = this;
+ dest->mgr.init_destination = &initDestination;
+ dest->mgr.empty_output_buffer = &emptyOutputBuffer;
+ dest->mgr.term_destination = &terminateDestination;
+ cinfo->dest = reinterpret_cast<struct jpeg_destination_mgr*>(dest);
+}
+
+void JpegEncoderHelper::setJpegCompressStruct(int width, int height, int quality,
+ jpeg_compress_struct* cinfo, bool isSingleChannel) {
+ cinfo->image_width = width;
+ cinfo->image_height = height;
+ cinfo->input_components = isSingleChannel ? 1 : 3;
+ cinfo->in_color_space = isSingleChannel ? JCS_GRAYSCALE : JCS_YCbCr;
+ jpeg_set_defaults(cinfo);
+ jpeg_set_quality(cinfo, quality, TRUE);
+ cinfo->raw_data_in = TRUE;
+ cinfo->dct_method = JDCT_ISLOW;
+ cinfo->comp_info[0].h_samp_factor = cinfo->in_color_space == JCS_GRAYSCALE ? 1 : 2;
+ cinfo->comp_info[0].v_samp_factor = cinfo->in_color_space == JCS_GRAYSCALE ? 1 : 2;
+ for (int i = 1; i < cinfo->num_components; i++) {
+ cinfo->comp_info[i].h_samp_factor = 1;
+ cinfo->comp_info[i].v_samp_factor = 1;
+ }
+}
+
+bool JpegEncoderHelper::compressYuv(jpeg_compress_struct* cinfo, const uint8_t* yBuffer,
+ const uint8_t* uvBuffer, int lumaStride, int chromaStride) {
+ size_t chroma_plane_size = chromaStride * cinfo->image_height / 2;
+ uint8_t* y_plane = const_cast<uint8_t*>(yBuffer);
+ uint8_t* u_plane = const_cast<uint8_t*>(uvBuffer);
+ uint8_t* v_plane = const_cast<uint8_t*>(u_plane + chroma_plane_size);
+
+ const int aligned_width = ALIGNM(cinfo->image_width, kCompressBatchSize);
+ const bool need_luma_padding = (lumaStride < aligned_width);
+ const int aligned_chroma_width = ALIGNM(cinfo->image_width / 2, kCompressBatchSize / 2);
+ const bool need_chroma_padding = (chromaStride < aligned_chroma_width);
+
+ std::unique_ptr<uint8_t[]> empty = nullptr;
+ std::unique_ptr<uint8_t[]> y_mcu_row = nullptr;
+ std::unique_ptr<uint8_t[]> cb_mcu_row = nullptr;
+ std::unique_ptr<uint8_t[]> cr_mcu_row = nullptr;
+ uint8_t* y_mcu_row_ptr = nullptr;
+ uint8_t* cb_mcu_row_ptr = nullptr;
+ uint8_t* cr_mcu_row_ptr = nullptr;
+
+ JSAMPROW y[kCompressBatchSize];
+ JSAMPROW cb[kCompressBatchSize / 2];
+ JSAMPROW cr[kCompressBatchSize / 2];
+ JSAMPARRAY planes[3]{y, cb, cr};
+
+ if (cinfo->image_height % kCompressBatchSize != 0) {
+ empty = std::make_unique<uint8_t[]>(aligned_width);
+ memset(empty.get(), 0, aligned_width);
+ }
+
+ if (need_luma_padding) {
+ size_t mcu_row_size = aligned_width * kCompressBatchSize;
+ y_mcu_row = std::make_unique<uint8_t[]>(mcu_row_size);
+ y_mcu_row_ptr = y_mcu_row.get();
+ uint8_t* tmp = y_mcu_row_ptr;
+ for (int i = 0; i < kCompressBatchSize; ++i, tmp += aligned_width) {
+ memset(tmp + cinfo->image_width, 0, aligned_width - cinfo->image_width);
+ }
+ }
+
+ if (need_chroma_padding) {
+ size_t mcu_row_size = aligned_chroma_width * kCompressBatchSize / 2;
+ cb_mcu_row = std::make_unique<uint8_t[]>(mcu_row_size);
+ cb_mcu_row_ptr = cb_mcu_row.get();
+ cr_mcu_row = std::make_unique<uint8_t[]>(mcu_row_size);
+ cr_mcu_row_ptr = cr_mcu_row.get();
+ uint8_t* tmp1 = cb_mcu_row_ptr;
+ uint8_t* tmp2 = cr_mcu_row_ptr;
+ for (int i = 0; i < kCompressBatchSize / 2;
+ ++i, tmp1 += aligned_chroma_width, tmp2 += aligned_chroma_width) {
+ memset(tmp1 + cinfo->image_width / 2, 0, aligned_chroma_width - (cinfo->image_width / 2));
+ memset(tmp2 + cinfo->image_width / 2, 0, aligned_chroma_width - (cinfo->image_width / 2));
+ }
+ }
+
+ while (cinfo->next_scanline < cinfo->image_height) {
+ for (int i = 0; i < kCompressBatchSize; ++i) {
+ size_t scanline = cinfo->next_scanline + i;
+ if (scanline < cinfo->image_height) {
+ y[i] = y_plane + scanline * lumaStride;
+ if (need_luma_padding) {
+ uint8_t* tmp = y_mcu_row_ptr + i * aligned_width;
+ memcpy(tmp, y[i], cinfo->image_width);
+ y[i] = tmp;
+ }
+ } else {
+ y[i] = empty.get();
+ }
+ }
+ // cb, cr only have half scanlines
+ for (int i = 0; i < kCompressBatchSize / 2; ++i) {
+ size_t scanline = cinfo->next_scanline / 2 + i;
+ if (scanline < cinfo->image_height / 2) {
+ int offset = scanline * chromaStride;
+ cb[i] = u_plane + offset;
+ cr[i] = v_plane + offset;
+ if (need_chroma_padding) {
+ uint8_t* tmp = cb_mcu_row_ptr + i * aligned_chroma_width;
+ memcpy(tmp, cb[i], cinfo->image_width / 2);
+ cb[i] = tmp;
+ tmp = cr_mcu_row_ptr + i * aligned_chroma_width;
+ memcpy(tmp, cr[i], cinfo->image_width / 2);
+ cr[i] = tmp;
+ }
+ } else {
+ cb[i] = cr[i] = empty.get();
+ }
+ }
+ int processed = jpeg_write_raw_data(cinfo, planes, kCompressBatchSize);
+ if (processed != kCompressBatchSize) {
+ ALOGE("Number of processed lines does not equal input lines.");
+ return false;
+ }
+ }
+ return true;
+}
+
+bool JpegEncoderHelper::compressY(jpeg_compress_struct* cinfo, const uint8_t* yBuffer,
+ int lumaStride) {
+ uint8_t* y_plane = const_cast<uint8_t*>(yBuffer);
+
+ const int aligned_luma_width = ALIGNM(cinfo->image_width, kCompressBatchSize);
+ const bool need_luma_padding = (lumaStride < aligned_luma_width);
+
+ std::unique_ptr<uint8_t[]> empty = nullptr;
+ std::unique_ptr<uint8_t[]> y_mcu_row = nullptr;
+ uint8_t* y_mcu_row_ptr = nullptr;
+
+ JSAMPROW y[kCompressBatchSize];
+ JSAMPARRAY planes[1]{y};
+
+ if (cinfo->image_height % kCompressBatchSize != 0) {
+ empty = std::make_unique<uint8_t[]>(aligned_luma_width);
+ memset(empty.get(), 0, aligned_luma_width);
+ }
+
+ if (need_luma_padding) {
+ size_t mcu_row_size = aligned_luma_width * kCompressBatchSize;
+ y_mcu_row = std::make_unique<uint8_t[]>(mcu_row_size);
+ y_mcu_row_ptr = y_mcu_row.get();
+ uint8_t* tmp = y_mcu_row_ptr;
+ for (int i = 0; i < kCompressBatchSize; ++i, tmp += aligned_luma_width) {
+ memset(tmp + cinfo->image_width, 0, aligned_luma_width - cinfo->image_width);
+ }
+ }
+
+ while (cinfo->next_scanline < cinfo->image_height) {
+ for (int i = 0; i < kCompressBatchSize; ++i) {
+ size_t scanline = cinfo->next_scanline + i;
+ if (scanline < cinfo->image_height) {
+ y[i] = y_plane + scanline * lumaStride;
+ if (need_luma_padding) {
+ uint8_t* tmp = y_mcu_row_ptr + i * aligned_luma_width;
+ memcpy(tmp, y[i], cinfo->image_width);
+ y[i] = tmp;
+ }
+ } else {
+ y[i] = empty.get();
+ }
+ }
+ int processed = jpeg_write_raw_data(cinfo, planes, kCompressBatchSize);
+ if (processed != kCompressBatchSize / 2) {
+ ALOGE("Number of processed lines does not equal input lines.");
+ return false;
+ }
+ }
+ return true;
+}
+
+} // namespace ultrahdr
diff --git a/lib/jpegencoderhelper.h b/lib/jpegencoderhelper.h
new file mode 100644
index 0000000..e988578
--- /dev/null
+++ b/lib/jpegencoderhelper.h
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * 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.
+ */
+
+#ifndef ULTRAHDR_JPEGENCODERHELPER_H
+#define ULTRAHDR_JPEGENCODERHELPER_H
+
+#include <stdio.h> // For jpeglib.h.
+
+// C++ build requires extern C for jpeg internals.
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <jerror.h>
+#include <jpeglib.h>
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#include <cstdint>
+#include <vector>
+
+namespace ultrahdr {
+
+/*
+ * Encapsulates a converter from raw image (YUV420planer or grey-scale) to JPEG format.
+ * This class is not thread-safe.
+ */
+class JpegEncoderHelper {
+ public:
+ JpegEncoderHelper();
+ ~JpegEncoderHelper();
+
+ /*
+ * Compresses YUV420Planer image to JPEG format. After calling this method, call
+ * getCompressedImage() to get the image. |quality| is the jpeg image quality parameter to use.
+ * It ranges from 1 (poorest quality) to 100 (highest quality). |iccBuffer| is the buffer of
+ * ICC segment which will be added to the compressed image.
+ * Returns false if errors occur during compression.
+ */
+ bool compressImage(const uint8_t* yBuffer, const uint8_t* uvBuffer, int width, int height,
+ int lumaStride, int chromaStride, int quality, const void* iccBuffer,
+ unsigned int iccSize);
+
+ /*
+ * Returns the compressed JPEG buffer pointer. This method must be called only after calling
+ * compressImage().
+ */
+ void* getCompressedImagePtr();
+
+ /*
+ * Returns the compressed JPEG buffer size. This method must be called only after calling
+ * compressImage().
+ */
+ size_t getCompressedImageSize();
+
+ /*
+ * Process 16 lines of Y and 16 lines of U/V each time.
+ * We must pass at least 16 scanlines according to libjpeg documentation.
+ */
+ static const int kCompressBatchSize = 16;
+
+ private:
+ // initDestination(), emptyOutputBuffer() and emptyOutputBuffer() are callback functions to be
+ // passed into jpeg library.
+ static void initDestination(j_compress_ptr cinfo);
+ static boolean emptyOutputBuffer(j_compress_ptr cinfo);
+ static void terminateDestination(j_compress_ptr cinfo);
+ static void outputErrorMessage(j_common_ptr cinfo);
+
+ // Returns false if errors occur.
+ bool encode(const uint8_t* yBuffer, const uint8_t* uvBuffer, int width, int height,
+ int lumaStride, int chromaStride, int quality, const void* iccBuffer,
+ unsigned int iccSize);
+ void setJpegDestination(jpeg_compress_struct* cinfo);
+ void setJpegCompressStruct(int width, int height, int quality, jpeg_compress_struct* cinfo,
+ bool isSingleChannel);
+ // Returns false if errors occur.
+ bool compressYuv(jpeg_compress_struct* cinfo, const uint8_t* yBuffer, const uint8_t* uvBuffer,
+ int lumaStride, int chromaStride);
+ bool compressY(jpeg_compress_struct* cinfo, const uint8_t* yBuffer, int lumaStride);
+
+ // The block size for encoded jpeg image buffer.
+ static const int kBlockSize = 16384;
+
+ // The buffer that holds the compressed result.
+ std::vector<JOCTET> mResultBuffer;
+};
+
+} /* namespace ultrahdr */
+
+#endif // ULTRAHDR_JPEGENCODERHELPER_H
diff --git a/jpegr.cpp b/lib/jpegr.cpp
index 3a88e38..015ffce 100644
--- a/jpegr.cpp
+++ b/lib/jpegr.cpp
@@ -14,7 +14,12 @@
* limitations under the License.
*/
+#ifdef _WIN32
+#include <Windows.h>
+#include <sysinfoapi.h>
+#else
#include <unistd.h>
+#endif
#include <condition_variable>
#include <deque>
@@ -22,10 +27,10 @@
#include <mutex>
#include <thread>
-#include "ultrahdr/ultrahdrcommon.h"
-#include "ultrahdr/jpegr.h"
-#include "ultrahdr/icc.h"
-#include "ultrahdr/multipictureformat.h"
+#include "ultrahdrcommon.h"
+#include "jpegr.h"
+#include "icc.h"
+#include "multipictureformat.h"
#include "image_io/base/data_segment_data_source.h"
#include "image_io/jpeg/jpeg_info.h"
@@ -45,28 +50,33 @@ namespace ultrahdr {
#define USE_PQ_INVOETF_LUT 1
#define USE_APPLY_GAIN_LUT 1
-#define JPEGR_CHECK(x) \
- { \
- status_t status = (x); \
- if ((status) != NO_ERROR) { \
- return status; \
- } \
+#define JPEGR_CHECK(x) \
+ { \
+ status_t status = (x); \
+ if ((status) != JPEGR_NO_ERROR) { \
+ return status; \
+ } \
}
// JPEG compress quality (0 ~ 100) for gain map
static const int kMapCompressQuality = 85;
-#define CONFIG_MULTITHREAD 1
int GetCPUCoreCount() {
int cpuCoreCount = 1;
-#if CONFIG_MULTITHREAD
-#if defined(_SC_NPROCESSORS_ONLN)
+
+#if defined(_WIN32)
+ SYSTEM_INFO system_info;
+ ZeroMemory(&system_info, sizeof(system_info));
+ GetSystemInfo(&system_info);
+ cpuCoreCount = (size_t)system_info.dwNumberOfProcessors;
+#elif defined(_SC_NPROCESSORS_ONLN)
cpuCoreCount = sysconf(_SC_NPROCESSORS_ONLN);
+#elif defined(_SC_NPROCESSORS_CONF)
+ cpuCoreCount = sysconf(_SC_NPROCESSORS_CONF);
#else
- // _SC_NPROC_ONLN must be defined...
- cpuCoreCount = sysconf(_SC_NPROC_ONLN);
-#endif
+#error platform-specific implementation for GetCPUCoreCount() missing.
#endif
+ if (cpuCoreCount <= 0) cpuCoreCount = 1;
return cpuCoreCount;
}
@@ -92,7 +102,7 @@ class AlogMessageWriter : public MessageWriter {
*/
static void copyJpegWithoutExif(jr_compressed_ptr pDest, jr_compressed_ptr pSource, size_t exif_pos,
size_t exif_size) {
- const size_t exif_offset = 4; // exif_pos has 4 bytes offset to the FF sign
+ const size_t exif_offset = 4; // exif_pos has 4 bytes offset to the FF sign
pDest->length = pSource->length - exif_size - exif_offset;
pDest->data = new uint8_t[pDest->length];
pDest->maxLength = pDest->length;
@@ -108,65 +118,65 @@ status_t JpegR::areInputArgumentsValid(jr_uncompressed_ptr p010_image_ptr,
jr_compressed_ptr dest_ptr) {
if (p010_image_ptr == nullptr || p010_image_ptr->data == nullptr) {
ALOGE("Received nullptr for input p010 image");
- return ERROR_JPEGR_INVALID_NULL_PTR;
+ return ERROR_JPEGR_BAD_PTR;
}
if (p010_image_ptr->width % 2 != 0 || p010_image_ptr->height % 2 != 0) {
ALOGE("Image dimensions cannot be odd, image dimensions %zux%zu", p010_image_ptr->width,
p010_image_ptr->height);
- return ERROR_JPEGR_INVALID_INPUT_TYPE;
+ return ERROR_JPEGR_UNSUPPORTED_WIDTH_HEIGHT;
}
if (p010_image_ptr->width < kMinWidth || p010_image_ptr->height < kMinHeight) {
ALOGE("Image dimensions cannot be less than %dx%d, image dimensions %zux%zu", kMinWidth,
kMinHeight, p010_image_ptr->width, p010_image_ptr->height);
- return ERROR_JPEGR_INVALID_INPUT_TYPE;
+ return ERROR_JPEGR_UNSUPPORTED_WIDTH_HEIGHT;
}
if (p010_image_ptr->width > kMaxWidth || p010_image_ptr->height > kMaxHeight) {
ALOGE("Image dimensions cannot be larger than %dx%d, image dimensions %zux%zu", kMaxWidth,
kMaxHeight, p010_image_ptr->width, p010_image_ptr->height);
- return ERROR_JPEGR_INVALID_INPUT_TYPE;
+ return ERROR_JPEGR_UNSUPPORTED_WIDTH_HEIGHT;
}
if (p010_image_ptr->colorGamut <= ULTRAHDR_COLORGAMUT_UNSPECIFIED ||
p010_image_ptr->colorGamut > ULTRAHDR_COLORGAMUT_MAX) {
ALOGE("Unrecognized p010 color gamut %d", p010_image_ptr->colorGamut);
- return ERROR_JPEGR_INVALID_INPUT_TYPE;
+ return ERROR_JPEGR_INVALID_COLORGAMUT;
}
if (p010_image_ptr->luma_stride != 0 && p010_image_ptr->luma_stride < p010_image_ptr->width) {
ALOGE("Luma stride must not be smaller than width, stride=%zu, width=%zu",
p010_image_ptr->luma_stride, p010_image_ptr->width);
- return ERROR_JPEGR_INVALID_INPUT_TYPE;
+ return ERROR_JPEGR_INVALID_STRIDE;
}
if (p010_image_ptr->chroma_data != nullptr &&
p010_image_ptr->chroma_stride < p010_image_ptr->width) {
ALOGE("Chroma stride must not be smaller than width, stride=%zu, width=%zu",
p010_image_ptr->chroma_stride, p010_image_ptr->width);
- return ERROR_JPEGR_INVALID_INPUT_TYPE;
+ return ERROR_JPEGR_INVALID_STRIDE;
}
if (dest_ptr == nullptr || dest_ptr->data == nullptr) {
ALOGE("Received nullptr for destination");
- return ERROR_JPEGR_INVALID_NULL_PTR;
+ return ERROR_JPEGR_BAD_PTR;
}
if (hdr_tf <= ULTRAHDR_TF_UNSPECIFIED || hdr_tf > ULTRAHDR_TF_MAX || hdr_tf == ULTRAHDR_TF_SRGB) {
ALOGE("Invalid hdr transfer function %d", hdr_tf);
- return ERROR_JPEGR_INVALID_INPUT_TYPE;
+ return ERROR_JPEGR_INVALID_TRANS_FUNC;
}
if (yuv420_image_ptr == nullptr) {
- return NO_ERROR;
+ return JPEGR_NO_ERROR;
}
if (yuv420_image_ptr->data == nullptr) {
ALOGE("Received nullptr for uncompressed 420 image");
- return ERROR_JPEGR_INVALID_NULL_PTR;
+ return ERROR_JPEGR_BAD_PTR;
}
if (yuv420_image_ptr->luma_stride != 0 &&
yuv420_image_ptr->luma_stride < yuv420_image_ptr->width) {
ALOGE("Luma stride must not be smaller than width, stride=%zu, width=%zu",
yuv420_image_ptr->luma_stride, yuv420_image_ptr->width);
- return ERROR_JPEGR_INVALID_INPUT_TYPE;
+ return ERROR_JPEGR_INVALID_STRIDE;
}
if (yuv420_image_ptr->chroma_data != nullptr &&
yuv420_image_ptr->chroma_stride < yuv420_image_ptr->width / 2) {
ALOGE("Chroma stride must not be smaller than (width / 2), stride=%zu, width=%zu",
yuv420_image_ptr->chroma_stride, yuv420_image_ptr->width);
- return ERROR_JPEGR_INVALID_INPUT_TYPE;
+ return ERROR_JPEGR_INVALID_STRIDE;
}
if (p010_image_ptr->width != yuv420_image_ptr->width ||
p010_image_ptr->height != yuv420_image_ptr->height) {
@@ -177,9 +187,9 @@ status_t JpegR::areInputArgumentsValid(jr_uncompressed_ptr p010_image_ptr,
if (yuv420_image_ptr->colorGamut <= ULTRAHDR_COLORGAMUT_UNSPECIFIED ||
yuv420_image_ptr->colorGamut > ULTRAHDR_COLORGAMUT_MAX) {
ALOGE("Unrecognized 420 color gamut %d", yuv420_image_ptr->colorGamut);
- return ERROR_JPEGR_INVALID_INPUT_TYPE;
+ return ERROR_JPEGR_INVALID_COLORGAMUT;
}
- return NO_ERROR;
+ return JPEGR_NO_ERROR;
}
status_t JpegR::areInputArgumentsValid(jr_uncompressed_ptr p010_image_ptr,
@@ -188,7 +198,7 @@ status_t JpegR::areInputArgumentsValid(jr_uncompressed_ptr p010_image_ptr,
jr_compressed_ptr dest_ptr, int quality) {
if (quality < 0 || quality > 100) {
ALOGE("quality factor is out side range [0-100], quality factor : %d", quality);
- return ERROR_JPEGR_INVALID_INPUT_TYPE;
+ return ERROR_JPEGR_INVALID_QUALITY_FACTOR;
}
return areInputArgumentsValid(p010_image_ptr, yuv420_image_ptr, hdr_tf, dest_ptr);
}
@@ -197,13 +207,10 @@ status_t JpegR::areInputArgumentsValid(jr_uncompressed_ptr p010_image_ptr,
status_t JpegR::encodeJPEGR(jr_uncompressed_ptr p010_image_ptr, ultrahdr_transfer_function hdr_tf,
jr_compressed_ptr dest, int quality, jr_exif_ptr exif) {
// validate input arguments
- if (auto ret = areInputArgumentsValid(p010_image_ptr, nullptr, hdr_tf, dest, quality);
- ret != NO_ERROR) {
- return ret;
- }
+ JPEGR_CHECK(areInputArgumentsValid(p010_image_ptr, nullptr, hdr_tf, dest, quality));
if (exif != nullptr && exif->data == nullptr) {
ALOGE("received nullptr for exif metadata");
- return ERROR_JPEGR_INVALID_NULL_PTR;
+ return ERROR_JPEGR_BAD_PTR;
}
// clean up input structure for later usage
@@ -217,14 +224,15 @@ status_t JpegR::encodeJPEGR(jr_uncompressed_ptr p010_image_ptr, ultrahdr_transfe
const size_t yu420_luma_stride = ALIGNM(p010_image.width, JpegEncoderHelper::kCompressBatchSize);
unique_ptr<uint8_t[]> yuv420_image_data =
- make_unique<uint8_t[]>(yu420_luma_stride * p010_image.height * 3 / 2);
- jpegr_uncompressed_struct yuv420_image = {.data = yuv420_image_data.get(),
- .width = p010_image.width,
- .height = p010_image.height,
- .colorGamut = p010_image.colorGamut,
- .chroma_data = nullptr,
- .luma_stride = yu420_luma_stride,
- .chroma_stride = yu420_luma_stride >> 1};
+ make_unique<uint8_t[]>(yu420_luma_stride * p010_image.height * 3 / 2);
+ jpegr_uncompressed_struct yuv420_image;
+ yuv420_image.data = yuv420_image_data.get();
+ yuv420_image.width = p010_image.width;
+ yuv420_image.height = p010_image.height;
+ yuv420_image.colorGamut = p010_image.colorGamut;
+ yuv420_image.chroma_data = nullptr;
+ yuv420_image.luma_stride = yu420_luma_stride;
+ yuv420_image.chroma_stride = yu420_luma_stride >> 1;
uint8_t* data = reinterpret_cast<uint8_t*>(yuv420_image.data);
yuv420_image.chroma_data = data + yuv420_image.luma_stride * yuv420_image.height;
@@ -232,7 +240,8 @@ status_t JpegR::encodeJPEGR(jr_uncompressed_ptr p010_image_ptr, ultrahdr_transfe
JPEGR_CHECK(toneMap(&p010_image, &yuv420_image));
// gain map
- ultrahdr_metadata_struct metadata = {.version = kJpegrVersion};
+ ultrahdr_metadata_struct metadata;
+ metadata.version = kJpegrVersion;
jpegr_uncompressed_struct gainmap_image;
JPEGR_CHECK(generateGainMap(&yuv420_image, &p010_image, hdr_tf, &metadata, &gainmap_image));
std::unique_ptr<uint8_t[]> map_data;
@@ -241,15 +250,14 @@ status_t JpegR::encodeJPEGR(jr_uncompressed_ptr p010_image_ptr, ultrahdr_transfe
// compress gain map
JpegEncoderHelper jpeg_enc_obj_gm;
JPEGR_CHECK(compressGainMap(&gainmap_image, &jpeg_enc_obj_gm));
- jpegr_compressed_struct compressed_map = {.data = jpeg_enc_obj_gm.getCompressedImagePtr(),
- .length = static_cast<int>(
- jpeg_enc_obj_gm.getCompressedImageSize()),
- .maxLength = static_cast<int>(
- jpeg_enc_obj_gm.getCompressedImageSize()),
- .colorGamut = ULTRAHDR_COLORGAMUT_UNSPECIFIED};
+ jpegr_compressed_struct compressed_map;
+ compressed_map.data = jpeg_enc_obj_gm.getCompressedImagePtr();
+ compressed_map.length = static_cast<int>(jpeg_enc_obj_gm.getCompressedImageSize());
+ compressed_map.maxLength = static_cast<int>(jpeg_enc_obj_gm.getCompressedImageSize());
+ compressed_map.colorGamut = ULTRAHDR_COLORGAMUT_UNSPECIFIED;
std::shared_ptr<DataStruct> icc =
- IccHelper::writeIccProfile(ULTRAHDR_TF_SRGB, yuv420_image.colorGamut);
+ IccHelper::writeIccProfile(ULTRAHDR_TF_SRGB, yuv420_image.colorGamut);
// convert to Bt601 YUV encoding for JPEG encode
if (yuv420_image.colorGamut != ULTRAHDR_COLORGAMUT_P3) {
@@ -265,18 +273,17 @@ status_t JpegR::encodeJPEGR(jr_uncompressed_ptr p010_image_ptr, ultrahdr_transfe
quality, icc->getData(), icc->getLength())) {
return ERROR_JPEGR_ENCODE_ERROR;
}
- jpegr_compressed_struct jpeg = {.data = jpeg_enc_obj_yuv420.getCompressedImagePtr(),
- .length = static_cast<int>(
- jpeg_enc_obj_yuv420.getCompressedImageSize()),
- .maxLength = static_cast<int>(
- jpeg_enc_obj_yuv420.getCompressedImageSize()),
- .colorGamut = yuv420_image.colorGamut};
+ jpegr_compressed_struct jpeg;
+ jpeg.data = jpeg_enc_obj_yuv420.getCompressedImagePtr();
+ jpeg.length = static_cast<int>(jpeg_enc_obj_yuv420.getCompressedImageSize());
+ jpeg.maxLength = static_cast<int>(jpeg_enc_obj_yuv420.getCompressedImageSize());
+ jpeg.colorGamut = yuv420_image.colorGamut;
// append gain map, no ICC since JPEG encode already did it
JPEGR_CHECK(appendGainMap(&jpeg, &compressed_map, exif, /* icc */ nullptr, /* icc size */ 0,
&metadata, dest));
- return NO_ERROR;
+ return JPEGR_NO_ERROR;
}
/* Encode API-1 */
@@ -286,16 +293,13 @@ status_t JpegR::encodeJPEGR(jr_uncompressed_ptr p010_image_ptr,
// validate input arguments
if (yuv420_image_ptr == nullptr) {
ALOGE("received nullptr for uncompressed 420 image");
- return ERROR_JPEGR_INVALID_NULL_PTR;
+ return ERROR_JPEGR_BAD_PTR;
}
if (exif != nullptr && exif->data == nullptr) {
ALOGE("received nullptr for exif metadata");
- return ERROR_JPEGR_INVALID_NULL_PTR;
- }
- if (auto ret = areInputArgumentsValid(p010_image_ptr, yuv420_image_ptr, hdr_tf, dest, quality);
- ret != NO_ERROR) {
- return ret;
+ return ERROR_JPEGR_BAD_PTR;
}
+ JPEGR_CHECK(areInputArgumentsValid(p010_image_ptr, yuv420_image_ptr, hdr_tf, dest, quality))
// clean up input structure for later usage
jpegr_uncompressed_struct p010_image = *p010_image_ptr;
@@ -314,7 +318,8 @@ status_t JpegR::encodeJPEGR(jr_uncompressed_ptr p010_image_ptr,
}
// gain map
- ultrahdr_metadata_struct metadata = {.version = kJpegrVersion};
+ ultrahdr_metadata_struct metadata;
+ metadata.version = kJpegrVersion;
jpegr_uncompressed_struct gainmap_image;
JPEGR_CHECK(generateGainMap(&yuv420_image, &p010_image, hdr_tf, &metadata, &gainmap_image));
std::unique_ptr<uint8_t[]> map_data;
@@ -323,24 +328,23 @@ status_t JpegR::encodeJPEGR(jr_uncompressed_ptr p010_image_ptr,
// compress gain map
JpegEncoderHelper jpeg_enc_obj_gm;
JPEGR_CHECK(compressGainMap(&gainmap_image, &jpeg_enc_obj_gm));
- jpegr_compressed_struct compressed_map = {.data = jpeg_enc_obj_gm.getCompressedImagePtr(),
- .length = static_cast<int>(
- jpeg_enc_obj_gm.getCompressedImageSize()),
- .maxLength = static_cast<int>(
- jpeg_enc_obj_gm.getCompressedImageSize()),
- .colorGamut = ULTRAHDR_COLORGAMUT_UNSPECIFIED};
+ jpegr_compressed_struct compressed_map;
+ compressed_map.data = jpeg_enc_obj_gm.getCompressedImagePtr();
+ compressed_map.length = static_cast<int>(jpeg_enc_obj_gm.getCompressedImageSize());
+ compressed_map.maxLength = static_cast<int>(jpeg_enc_obj_gm.getCompressedImageSize());
+ compressed_map.colorGamut = ULTRAHDR_COLORGAMUT_UNSPECIFIED;
std::shared_ptr<DataStruct> icc =
- IccHelper::writeIccProfile(ULTRAHDR_TF_SRGB, yuv420_image.colorGamut);
+ IccHelper::writeIccProfile(ULTRAHDR_TF_SRGB, yuv420_image.colorGamut);
jpegr_uncompressed_struct yuv420_bt601_image = yuv420_image;
unique_ptr<uint8_t[]> yuv_420_bt601_data;
// Convert to bt601 YUV encoding for JPEG encode
if (yuv420_image.colorGamut != ULTRAHDR_COLORGAMUT_P3) {
const size_t yuv_420_bt601_luma_stride =
- ALIGNM(yuv420_image.width, JpegEncoderHelper::kCompressBatchSize);
+ ALIGNM(yuv420_image.width, JpegEncoderHelper::kCompressBatchSize);
yuv_420_bt601_data =
- make_unique<uint8_t[]>(yuv_420_bt601_luma_stride * yuv420_image.height * 3 / 2);
+ make_unique<uint8_t[]>(yuv_420_bt601_luma_stride * yuv420_image.height * 3 / 2);
yuv420_bt601_image.data = yuv_420_bt601_data.get();
yuv420_bt601_image.colorGamut = yuv420_image.colorGamut;
yuv420_bt601_image.luma_stride = yuv_420_bt601_luma_stride;
@@ -398,26 +402,24 @@ status_t JpegR::encodeJPEGR(jr_uncompressed_ptr p010_image_ptr,
// compress 420 image
JpegEncoderHelper jpeg_enc_obj_yuv420;
- if (!jpeg_enc_obj_yuv420.compressImage(reinterpret_cast<uint8_t*>(yuv420_bt601_image.data),
- reinterpret_cast<uint8_t*>(yuv420_bt601_image.chroma_data),
- yuv420_bt601_image.width, yuv420_bt601_image.height,
- yuv420_bt601_image.luma_stride,
- yuv420_bt601_image.chroma_stride, quality, icc->getData(),
- icc->getLength())) {
+ if (!jpeg_enc_obj_yuv420.compressImage(
+ reinterpret_cast<uint8_t*>(yuv420_bt601_image.data),
+ reinterpret_cast<uint8_t*>(yuv420_bt601_image.chroma_data), yuv420_bt601_image.width,
+ yuv420_bt601_image.height, yuv420_bt601_image.luma_stride,
+ yuv420_bt601_image.chroma_stride, quality, icc->getData(), icc->getLength())) {
return ERROR_JPEGR_ENCODE_ERROR;
}
- jpegr_compressed_struct jpeg = {.data = jpeg_enc_obj_yuv420.getCompressedImagePtr(),
- .length = static_cast<int>(
- jpeg_enc_obj_yuv420.getCompressedImageSize()),
- .maxLength = static_cast<int>(
- jpeg_enc_obj_yuv420.getCompressedImageSize()),
- .colorGamut = yuv420_image.colorGamut};
+ jpegr_compressed_struct jpeg;
+ jpeg.data = jpeg_enc_obj_yuv420.getCompressedImagePtr();
+ jpeg.length = static_cast<int>(jpeg_enc_obj_yuv420.getCompressedImageSize());
+ jpeg.maxLength = static_cast<int>(jpeg_enc_obj_yuv420.getCompressedImageSize());
+ jpeg.colorGamut = yuv420_image.colorGamut;
// append gain map, no ICC since JPEG encode already did it
JPEGR_CHECK(appendGainMap(&jpeg, &compressed_map, exif, /* icc */ nullptr, /* icc size */ 0,
&metadata, dest));
- return NO_ERROR;
+ return JPEGR_NO_ERROR;
}
/* Encode API-2 */
@@ -428,16 +430,13 @@ status_t JpegR::encodeJPEGR(jr_uncompressed_ptr p010_image_ptr,
// validate input arguments
if (yuv420_image_ptr == nullptr) {
ALOGE("received nullptr for uncompressed 420 image");
- return ERROR_JPEGR_INVALID_NULL_PTR;
+ return ERROR_JPEGR_BAD_PTR;
}
if (yuv420jpg_image_ptr == nullptr || yuv420jpg_image_ptr->data == nullptr) {
ALOGE("received nullptr for compressed jpeg image");
- return ERROR_JPEGR_INVALID_NULL_PTR;
- }
- if (auto ret = areInputArgumentsValid(p010_image_ptr, yuv420_image_ptr, hdr_tf, dest);
- ret != NO_ERROR) {
- return ret;
+ return ERROR_JPEGR_BAD_PTR;
}
+ JPEGR_CHECK(areInputArgumentsValid(p010_image_ptr, yuv420_image_ptr, hdr_tf, dest))
// clean up input structure for later usage
jpegr_uncompressed_struct p010_image = *p010_image_ptr;
@@ -456,7 +455,8 @@ status_t JpegR::encodeJPEGR(jr_uncompressed_ptr p010_image_ptr,
}
// gain map
- ultrahdr_metadata_struct metadata = {.version = kJpegrVersion};
+ ultrahdr_metadata_struct metadata;
+ metadata.version = kJpegrVersion;
jpegr_uncompressed_struct gainmap_image;
JPEGR_CHECK(generateGainMap(&yuv420_image, &p010_image, hdr_tf, &metadata, &gainmap_image));
std::unique_ptr<uint8_t[]> map_data;
@@ -465,12 +465,11 @@ status_t JpegR::encodeJPEGR(jr_uncompressed_ptr p010_image_ptr,
// compress gain map
JpegEncoderHelper jpeg_enc_obj_gm;
JPEGR_CHECK(compressGainMap(&gainmap_image, &jpeg_enc_obj_gm));
- jpegr_compressed_struct gainmapjpg_image = {.data = jpeg_enc_obj_gm.getCompressedImagePtr(),
- .length = static_cast<int>(
- jpeg_enc_obj_gm.getCompressedImageSize()),
- .maxLength = static_cast<int>(
- jpeg_enc_obj_gm.getCompressedImageSize()),
- .colorGamut = ULTRAHDR_COLORGAMUT_UNSPECIFIED};
+ jpegr_compressed_struct gainmapjpg_image;
+ gainmapjpg_image.data = jpeg_enc_obj_gm.getCompressedImagePtr();
+ gainmapjpg_image.length = static_cast<int>(jpeg_enc_obj_gm.getCompressedImageSize());
+ gainmapjpg_image.maxLength = static_cast<int>(jpeg_enc_obj_gm.getCompressedImageSize());
+ gainmapjpg_image.colorGamut = ULTRAHDR_COLORGAMUT_UNSPECIFIED;
return encodeJPEGR(yuv420jpg_image_ptr, &gainmapjpg_image, &metadata, dest);
}
@@ -482,11 +481,9 @@ status_t JpegR::encodeJPEGR(jr_uncompressed_ptr p010_image_ptr,
// validate input arguments
if (yuv420jpg_image_ptr == nullptr || yuv420jpg_image_ptr->data == nullptr) {
ALOGE("received nullptr for compressed jpeg image");
- return ERROR_JPEGR_INVALID_NULL_PTR;
- }
- if (auto ret = areInputArgumentsValid(p010_image_ptr, nullptr, hdr_tf, dest); ret != NO_ERROR) {
- return ret;
+ return ERROR_JPEGR_BAD_PTR;
}
+ JPEGR_CHECK(areInputArgumentsValid(p010_image_ptr, nullptr, hdr_tf, dest))
// clean up input structure for later usage
jpegr_uncompressed_struct p010_image = *p010_image_ptr;
@@ -521,7 +518,8 @@ status_t JpegR::encodeJPEGR(jr_uncompressed_ptr p010_image_ptr,
}
// gain map
- ultrahdr_metadata_struct metadata = {.version = kJpegrVersion};
+ ultrahdr_metadata_struct metadata;
+ metadata.version = kJpegrVersion;
jpegr_uncompressed_struct gainmap_image;
JPEGR_CHECK(generateGainMap(&yuv420_image, &p010_image, hdr_tf, &metadata, &gainmap_image,
true /* sdr_is_601 */));
@@ -531,12 +529,11 @@ status_t JpegR::encodeJPEGR(jr_uncompressed_ptr p010_image_ptr,
// compress gain map
JpegEncoderHelper jpeg_enc_obj_gm;
JPEGR_CHECK(compressGainMap(&gainmap_image, &jpeg_enc_obj_gm));
- jpegr_compressed_struct gainmapjpg_image = {.data = jpeg_enc_obj_gm.getCompressedImagePtr(),
- .length = static_cast<int>(
- jpeg_enc_obj_gm.getCompressedImageSize()),
- .maxLength = static_cast<int>(
- jpeg_enc_obj_gm.getCompressedImageSize()),
- .colorGamut = ULTRAHDR_COLORGAMUT_UNSPECIFIED};
+ jpegr_compressed_struct gainmapjpg_image;
+ gainmapjpg_image.data = jpeg_enc_obj_gm.getCompressedImagePtr();
+ gainmapjpg_image.length = static_cast<int>(jpeg_enc_obj_gm.getCompressedImageSize());
+ gainmapjpg_image.maxLength = static_cast<int>(jpeg_enc_obj_gm.getCompressedImageSize());
+ gainmapjpg_image.colorGamut = ULTRAHDR_COLORGAMUT_UNSPECIFIED;
return encodeJPEGR(yuv420jpg_image_ptr, &gainmapjpg_image, &metadata, dest);
}
@@ -547,15 +544,15 @@ status_t JpegR::encodeJPEGR(jr_compressed_ptr yuv420jpg_image_ptr,
jr_compressed_ptr dest) {
if (yuv420jpg_image_ptr == nullptr || yuv420jpg_image_ptr->data == nullptr) {
ALOGE("received nullptr for compressed jpeg image");
- return ERROR_JPEGR_INVALID_NULL_PTR;
+ return ERROR_JPEGR_BAD_PTR;
}
if (gainmapjpg_image_ptr == nullptr || gainmapjpg_image_ptr->data == nullptr) {
ALOGE("received nullptr for compressed gain map");
- return ERROR_JPEGR_INVALID_NULL_PTR;
+ return ERROR_JPEGR_BAD_PTR;
}
if (dest == nullptr || dest->data == nullptr) {
ALOGE("received nullptr for destination");
- return ERROR_JPEGR_INVALID_NULL_PTR;
+ return ERROR_JPEGR_BAD_PTR;
}
// We just want to check if ICC is present, so don't do a full decode. Note,
@@ -572,36 +569,35 @@ status_t JpegR::encodeJPEGR(jr_compressed_ptr yuv420jpg_image_ptr,
/* icc */ nullptr, /* icc size */ 0, metadata, dest));
} else {
std::shared_ptr<DataStruct> newIcc =
- IccHelper::writeIccProfile(ULTRAHDR_TF_SRGB, yuv420jpg_image_ptr->colorGamut);
+ IccHelper::writeIccProfile(ULTRAHDR_TF_SRGB, yuv420jpg_image_ptr->colorGamut);
JPEGR_CHECK(appendGainMap(yuv420jpg_image_ptr, gainmapjpg_image_ptr, /* exif */ nullptr,
newIcc->getData(), newIcc->getLength(), metadata, dest));
}
- return NO_ERROR;
+ return JPEGR_NO_ERROR;
}
status_t JpegR::getJPEGRInfo(jr_compressed_ptr jpegr_image_ptr, jr_info_ptr jpeg_image_info_ptr) {
if (jpegr_image_ptr == nullptr || jpegr_image_ptr->data == nullptr) {
ALOGE("received nullptr for compressed jpegr image");
- return ERROR_JPEGR_INVALID_NULL_PTR;
+ return ERROR_JPEGR_BAD_PTR;
}
if (jpeg_image_info_ptr == nullptr) {
ALOGE("received nullptr for compressed jpegr info struct");
- return ERROR_JPEGR_INVALID_NULL_PTR;
+ return ERROR_JPEGR_BAD_PTR;
}
jpegr_compressed_struct primary_image, gainmap_image;
status_t status = extractPrimaryImageAndGainMap(jpegr_image_ptr, &primary_image, &gainmap_image);
- if (status != NO_ERROR && status != ERROR_JPEGR_GAIN_MAP_IMAGE_NOT_FOUND) {
+ if (status != JPEGR_NO_ERROR && status != ERROR_JPEGR_GAIN_MAP_IMAGE_NOT_FOUND) {
return status;
}
JpegDecoderHelper jpeg_dec_obj_hdr;
- if (!jpeg_dec_obj_hdr.getCompressedImageParameters(primary_image.data, primary_image.length,
- &jpeg_image_info_ptr->width,
- &jpeg_image_info_ptr->height,
- jpeg_image_info_ptr->iccData,
- jpeg_image_info_ptr->exifData)) {
+ if (!jpeg_dec_obj_hdr.getCompressedImageParameters(
+ primary_image.data, primary_image.length, &jpeg_image_info_ptr->width,
+ &jpeg_image_info_ptr->height, jpeg_image_info_ptr->iccData,
+ jpeg_image_info_ptr->exifData)) {
return ERROR_JPEGR_DECODE_ERROR;
}
@@ -615,29 +611,29 @@ status_t JpegR::decodeJPEGR(jr_compressed_ptr jpegr_image_ptr, jr_uncompressed_p
jr_uncompressed_ptr gainmap_image_ptr, ultrahdr_metadata_ptr metadata) {
if (jpegr_image_ptr == nullptr || jpegr_image_ptr->data == nullptr) {
ALOGE("received nullptr for compressed jpegr image");
- return ERROR_JPEGR_INVALID_NULL_PTR;
+ return ERROR_JPEGR_BAD_PTR;
}
if (dest == nullptr || dest->data == nullptr) {
ALOGE("received nullptr for dest image");
- return ERROR_JPEGR_INVALID_NULL_PTR;
+ return ERROR_JPEGR_BAD_PTR;
}
if (max_display_boost < 1.0f) {
ALOGE("received bad value for max_display_boost %f", max_display_boost);
- return ERROR_JPEGR_INVALID_INPUT_TYPE;
+ return ERROR_JPEGR_INVALID_DISPLAY_BOOST;
}
if (exif != nullptr && exif->data == nullptr) {
ALOGE("received nullptr address for exif data");
- return ERROR_JPEGR_INVALID_INPUT_TYPE;
+ return ERROR_JPEGR_BAD_PTR;
}
if (output_format <= ULTRAHDR_OUTPUT_UNSPECIFIED || output_format > ULTRAHDR_OUTPUT_MAX) {
ALOGE("received bad value for output format %d", output_format);
- return ERROR_JPEGR_INVALID_INPUT_TYPE;
+ return ERROR_JPEGR_INVALID_OUTPUT_FORMAT;
}
jpegr_compressed_struct primary_jpeg_image, gainmap_jpeg_image;
status_t status =
- extractPrimaryImageAndGainMap(jpegr_image_ptr, &primary_jpeg_image, &gainmap_jpeg_image);
- if (status != NO_ERROR) {
+ extractPrimaryImageAndGainMap(jpegr_image_ptr, &primary_jpeg_image, &gainmap_jpeg_image);
+ if (status != JPEGR_NO_ERROR) {
if (output_format != ULTRAHDR_OUTPUT_SDR || status != ERROR_JPEGR_GAIN_MAP_IMAGE_NOT_FOUND) {
ALOGE("received invalid compressed jpegr image");
return status;
@@ -654,19 +650,19 @@ status_t JpegR::decodeJPEGR(jr_compressed_ptr jpegr_image_ptr, jr_uncompressed_p
if ((jpeg_dec_obj_yuv420.getDecompressedImageWidth() *
jpeg_dec_obj_yuv420.getDecompressedImageHeight() * 4) >
jpeg_dec_obj_yuv420.getDecompressedImageSize()) {
- return ERROR_JPEGR_CALCULATION_ERROR;
+ return ERROR_JPEGR_DECODE_ERROR;
}
} else {
if ((jpeg_dec_obj_yuv420.getDecompressedImageWidth() *
jpeg_dec_obj_yuv420.getDecompressedImageHeight() * 3 / 2) >
jpeg_dec_obj_yuv420.getDecompressedImageSize()) {
- return ERROR_JPEGR_CALCULATION_ERROR;
+ return ERROR_JPEGR_DECODE_ERROR;
}
}
if (exif != nullptr) {
if (exif->data == nullptr) {
- return ERROR_JPEGR_INVALID_NULL_PTR;
+ return ERROR_JPEGR_BAD_PTR;
}
if (exif->length < jpeg_dec_obj_yuv420.getEXIFSize()) {
return ERROR_JPEGR_BUFFER_TOO_SMALL;
@@ -680,7 +676,7 @@ status_t JpegR::decodeJPEGR(jr_compressed_ptr jpegr_image_ptr, jr_uncompressed_p
dest->height = jpeg_dec_obj_yuv420.getDecompressedImageHeight();
memcpy(dest->data, jpeg_dec_obj_yuv420.getDecompressedImagePtr(),
dest->width * dest->height * 4);
- return NO_ERROR;
+ return JPEGR_NO_ERROR;
}
JpegDecoderHelper jpeg_dec_obj_gm;
@@ -689,7 +685,7 @@ status_t JpegR::decodeJPEGR(jr_compressed_ptr jpegr_image_ptr, jr_uncompressed_p
}
if ((jpeg_dec_obj_gm.getDecompressedImageWidth() * jpeg_dec_obj_gm.getDecompressedImageHeight()) >
jpeg_dec_obj_gm.getDecompressedImageSize()) {
- return ERROR_JPEGR_CALCULATION_ERROR;
+ return ERROR_JPEGR_DECODE_ERROR;
}
jpegr_uncompressed_struct gainmap_image;
@@ -708,7 +704,7 @@ status_t JpegR::decodeJPEGR(jr_compressed_ptr jpegr_image_ptr, jr_uncompressed_p
ultrahdr_metadata_struct uhdr_metadata;
if (!getMetadataFromXMP(static_cast<uint8_t*>(jpeg_dec_obj_gm.getXMPPtr()),
jpeg_dec_obj_gm.getXMPSize(), &uhdr_metadata)) {
- return ERROR_JPEGR_INVALID_METADATA;
+ return ERROR_JPEGR_METADATA_ERROR;
}
if (metadata != nullptr) {
@@ -735,13 +731,13 @@ status_t JpegR::decodeJPEGR(jr_compressed_ptr jpegr_image_ptr, jr_uncompressed_p
JPEGR_CHECK(applyGainMap(&yuv420_image, &gainmap_image, &uhdr_metadata, output_format,
max_display_boost, dest));
- return NO_ERROR;
+ return JPEGR_NO_ERROR;
}
status_t JpegR::compressGainMap(jr_uncompressed_ptr gainmap_image_ptr,
JpegEncoderHelper* jpeg_enc_obj_ptr) {
if (gainmap_image_ptr == nullptr || jpeg_enc_obj_ptr == nullptr) {
- return ERROR_JPEGR_INVALID_NULL_PTR;
+ return ERROR_JPEGR_BAD_PTR;
}
// Don't need to convert YUV to Bt601 since single channel
@@ -752,7 +748,7 @@ status_t JpegR::compressGainMap(jr_uncompressed_ptr gainmap_image_ptr,
return ERROR_JPEGR_ENCODE_ERROR;
}
- return NO_ERROR;
+ return JPEGR_NO_ERROR;
}
const int kJobSzInRows = 16;
@@ -760,13 +756,13 @@ static_assert(kJobSzInRows > 0 && kJobSzInRows % kMapDimensionScaleFactor == 0,
"align job size to kMapDimensionScaleFactor");
class JobQueue {
-public:
+ public:
bool dequeueJob(size_t& rowStart, size_t& rowEnd);
void enqueueJob(size_t rowStart, size_t rowEnd);
void markQueueForEnd();
void reset();
-private:
+ private:
bool mQueuedAllJobs = false;
std::deque<std::tuple<size_t, size_t>> mJobs;
std::mutex mMutex;
@@ -821,7 +817,7 @@ status_t JpegR::generateGainMap(jr_uncompressed_ptr yuv420_image_ptr,
dest == nullptr || yuv420_image_ptr->data == nullptr ||
yuv420_image_ptr->chroma_data == nullptr || p010_image_ptr->data == nullptr ||
p010_image_ptr->chroma_data == nullptr) {
- return ERROR_JPEGR_INVALID_NULL_PTR;
+ return ERROR_JPEGR_BAD_PTR;
}
if (yuv420_image_ptr->width != p010_image_ptr->width ||
yuv420_image_ptr->height != p010_image_ptr->height) {
@@ -889,7 +885,7 @@ status_t JpegR::generateGainMap(jr_uncompressed_ptr yuv420_image_ptr,
float log2MaxBoost = log2(metadata->maxContentBoost);
ColorTransformFn hdrGamutConversionFn =
- getHdrConversionFn(yuv420_image_ptr->colorGamut, p010_image_ptr->colorGamut);
+ getHdrConversionFn(yuv420_image_ptr->colorGamut, p010_image_ptr->colorGamut);
ColorCalculationFn luminanceFn = nullptr;
ColorTransformFn sdrYuvToRgbFn = nullptr;
@@ -930,7 +926,7 @@ status_t JpegR::generateGainMap(jr_uncompressed_ptr yuv420_image_ptr,
return ERROR_JPEGR_INVALID_COLORGAMUT;
}
- const int threads = std::clamp(GetCPUCoreCount(), 1, 4);
+ const int threads = (std::min)(GetCPUCoreCount(), 4);
size_t rowStep = threads == 1 ? image_height : kJobSzInRows;
JobQueue jobQueue;
@@ -960,7 +956,7 @@ status_t JpegR::generateGainMap(jr_uncompressed_ptr yuv420_image_ptr,
size_t pixel_idx = x + y * dest->width;
reinterpret_cast<uint8_t*>(dest->data)[pixel_idx] =
- encodeGain(sdr_y_nits, hdr_y_nits, metadata, log2MinBoost, log2MaxBoost);
+ encodeGain(sdr_y_nits, hdr_y_nits, metadata, log2MinBoost, log2MaxBoost);
}
}
}
@@ -974,7 +970,7 @@ status_t JpegR::generateGainMap(jr_uncompressed_ptr yuv420_image_ptr,
rowStep = (threads == 1 ? image_height : kJobSzInRows) / kMapDimensionScaleFactor;
for (size_t rowStart = 0; rowStart < map_height;) {
- size_t rowEnd = std::min(rowStart + rowStep, map_height);
+ size_t rowEnd = (std::min)(rowStart + rowStep, map_height);
jobQueue.enqueueJob(rowStart, rowEnd);
rowStart = rowEnd;
}
@@ -983,7 +979,7 @@ status_t JpegR::generateGainMap(jr_uncompressed_ptr yuv420_image_ptr,
std::for_each(workers.begin(), workers.end(), [](std::thread& t) { t.join(); });
map_data.release();
- return NO_ERROR;
+ return JPEGR_NO_ERROR;
}
status_t JpegR::applyGainMap(jr_uncompressed_ptr yuv420_image_ptr,
@@ -993,25 +989,25 @@ status_t JpegR::applyGainMap(jr_uncompressed_ptr yuv420_image_ptr,
if (yuv420_image_ptr == nullptr || gainmap_image_ptr == nullptr || metadata == nullptr ||
dest == nullptr || yuv420_image_ptr->data == nullptr ||
yuv420_image_ptr->chroma_data == nullptr || gainmap_image_ptr->data == nullptr) {
- return ERROR_JPEGR_INVALID_NULL_PTR;
+ return ERROR_JPEGR_BAD_PTR;
}
if (metadata->version.compare(kJpegrVersion)) {
ALOGE("Unsupported metadata version: %s", metadata->version.c_str());
- return ERROR_JPEGR_UNSUPPORTED_METADATA;
+ return ERROR_JPEGR_BAD_METADATA;
}
if (metadata->gamma != 1.0f) {
ALOGE("Unsupported metadata gamma: %f", metadata->gamma);
- return ERROR_JPEGR_UNSUPPORTED_METADATA;
+ return ERROR_JPEGR_BAD_METADATA;
}
if (metadata->offsetSdr != 0.0f || metadata->offsetHdr != 0.0f) {
ALOGE("Unsupported metadata offset sdr, hdr: %f, %f", metadata->offsetSdr, metadata->offsetHdr);
- return ERROR_JPEGR_UNSUPPORTED_METADATA;
+ return ERROR_JPEGR_BAD_METADATA;
}
if (metadata->hdrCapacityMin != metadata->minContentBoost ||
metadata->hdrCapacityMax != metadata->maxContentBoost) {
ALOGE("Unsupported metadata hdr capacity min, max: %f, %f", metadata->hdrCapacityMin,
metadata->hdrCapacityMax);
- return ERROR_JPEGR_UNSUPPORTED_METADATA;
+ return ERROR_JPEGR_BAD_METADATA;
}
// TODO: remove once map scaling factor is computed based on actual map dims
@@ -1020,21 +1016,22 @@ status_t JpegR::applyGainMap(jr_uncompressed_ptr yuv420_image_ptr,
size_t map_width = image_width / kMapDimensionScaleFactor;
size_t map_height = image_height / kMapDimensionScaleFactor;
if (map_width != gainmap_image_ptr->width || map_height != gainmap_image_ptr->height) {
- ALOGE("gain map dimensions and primary image dimensions are not to scale, computed gain map "
- "resolution is %zux%zu, received gain map resolution is %zux%zu",
- map_width, map_height, gainmap_image_ptr->width, gainmap_image_ptr->height);
- return ERROR_JPEGR_INVALID_INPUT_TYPE;
+ ALOGE(
+ "gain map dimensions and primary image dimensions are not to scale, computed gain map "
+ "resolution is %zux%zu, received gain map resolution is %zux%zu",
+ map_width, map_height, gainmap_image_ptr->width, gainmap_image_ptr->height);
+ return ERROR_JPEGR_RESOLUTION_MISMATCH;
}
dest->width = yuv420_image_ptr->width;
dest->height = yuv420_image_ptr->height;
ShepardsIDW idwTable(kMapDimensionScaleFactor);
- float display_boost = std::min(max_display_boost, metadata->maxContentBoost);
+ float display_boost = (std::min)(max_display_boost, metadata->maxContentBoost);
GainLUT gainLUT(metadata, display_boost);
JobQueue jobQueue;
- std::function<void()> applyRecMap = [yuv420_image_ptr, gainmap_image_ptr, dest,
- &jobQueue, &idwTable, output_format, &gainLUT,
+ std::function<void()> applyRecMap = [yuv420_image_ptr, gainmap_image_ptr, dest, &jobQueue,
+ &idwTable, output_format, &gainLUT,
display_boost]() -> void {
size_t width = yuv420_image_ptr->width;
@@ -1108,36 +1105,35 @@ status_t JpegR::applyGainMap(jr_uncompressed_ptr yuv420_image_ptr,
}
};
- const int threads = std::clamp(GetCPUCoreCount(), 1, 4);
+ const int threads = (std::min)(GetCPUCoreCount(), 4);
std::vector<std::thread> workers;
for (int th = 0; th < threads - 1; th++) {
workers.push_back(std::thread(applyRecMap));
}
const int rowStep = threads == 1 ? yuv420_image_ptr->height : kJobSzInRows;
for (size_t rowStart = 0; rowStart < yuv420_image_ptr->height;) {
- int rowEnd = std::min(rowStart + rowStep, yuv420_image_ptr->height);
+ int rowEnd = (std::min)(rowStart + rowStep, yuv420_image_ptr->height);
jobQueue.enqueueJob(rowStart, rowEnd);
rowStart = rowEnd;
}
jobQueue.markQueueForEnd();
applyRecMap();
std::for_each(workers.begin(), workers.end(), [](std::thread& t) { t.join(); });
- return NO_ERROR;
+ return JPEGR_NO_ERROR;
}
status_t JpegR::extractPrimaryImageAndGainMap(jr_compressed_ptr jpegr_image_ptr,
jr_compressed_ptr primary_jpg_image_ptr,
jr_compressed_ptr gainmap_jpg_image_ptr) {
if (jpegr_image_ptr == nullptr) {
- return ERROR_JPEGR_INVALID_NULL_PTR;
+ return ERROR_JPEGR_BAD_PTR;
}
MessageHandler msg_handler;
msg_handler.SetMessageWriter(make_unique<AlogMessageWriter>(AlogMessageWriter()));
- std::shared_ptr<DataSegment> seg =
- DataSegment::Create(DataRange(0, jpegr_image_ptr->length),
- static_cast<const uint8_t*>(jpegr_image_ptr->data),
- DataSegment::BufferDispositionPolicy::kDontDelete);
+ std::shared_ptr<DataSegment> seg = DataSegment::Create(
+ DataRange(0, jpegr_image_ptr->length), static_cast<const uint8_t*>(jpegr_image_ptr->data),
+ DataSegment::BufferDispositionPolicy::kDontDelete);
DataSegmentDataSource data_source(seg);
JpegInfoBuilder jpeg_info_builder;
jpeg_info_builder.SetImageLimit(2);
@@ -1146,19 +1142,19 @@ status_t JpegR::extractPrimaryImageAndGainMap(jr_compressed_ptr jpegr_image_ptr,
data_source.Reset();
if (jpeg_scanner.HasError()) {
- return ERROR_JPEGR_INVALID_INPUT_TYPE;
+ return JPEGR_UNKNOWN_ERROR;
}
const auto& jpeg_info = jpeg_info_builder.GetInfo();
const auto& image_ranges = jpeg_info.GetImageRanges();
if (image_ranges.empty()) {
- return ERROR_JPEGR_INVALID_INPUT_TYPE;
+ return ERROR_JPEGR_NO_IMAGES_FOUND;
}
if (primary_jpg_image_ptr != nullptr) {
primary_jpg_image_ptr->data =
- static_cast<uint8_t*>(jpegr_image_ptr->data) + image_ranges[0].GetBegin();
+ static_cast<uint8_t*>(jpegr_image_ptr->data) + image_ranges[0].GetBegin();
primary_jpg_image_ptr->length = image_ranges[0].GetLength();
}
@@ -1168,7 +1164,7 @@ status_t JpegR::extractPrimaryImageAndGainMap(jr_compressed_ptr jpegr_image_ptr,
if (gainmap_jpg_image_ptr != nullptr) {
gainmap_jpg_image_ptr->data =
- static_cast<uint8_t*>(jpegr_image_ptr->data) + image_ranges[1].GetBegin();
+ static_cast<uint8_t*>(jpegr_image_ptr->data) + image_ranges[1].GetBegin();
gainmap_jpg_image_ptr->length = image_ranges[1].GetLength();
}
@@ -1178,7 +1174,7 @@ status_t JpegR::extractPrimaryImageAndGainMap(jr_compressed_ptr jpegr_image_ptr,
(int)image_ranges.size());
}
- return NO_ERROR;
+ return JPEGR_NO_ERROR;
}
// JPEG/R structure:
@@ -1221,42 +1217,43 @@ status_t JpegR::appendGainMap(jr_compressed_ptr primary_jpg_image_ptr,
jr_compressed_ptr dest) {
if (primary_jpg_image_ptr == nullptr || gainmap_jpg_image_ptr == nullptr || metadata == nullptr ||
dest == nullptr) {
- return ERROR_JPEGR_INVALID_NULL_PTR;
+ return ERROR_JPEGR_BAD_PTR;
}
if (metadata->version.compare("1.0")) {
ALOGE("received bad value for version: %s", metadata->version.c_str());
- return ERROR_JPEGR_INVALID_INPUT_TYPE;
+ return ERROR_JPEGR_BAD_METADATA;
}
if (metadata->maxContentBoost < metadata->minContentBoost) {
ALOGE("received bad value for content boost min %f, max %f", metadata->minContentBoost,
metadata->maxContentBoost);
- return ERROR_JPEGR_INVALID_INPUT_TYPE;
+ return ERROR_JPEGR_BAD_METADATA;
}
if (metadata->hdrCapacityMax < metadata->hdrCapacityMin || metadata->hdrCapacityMin < 1.0f) {
ALOGE("received bad value for hdr capacity min %f, max %f", metadata->hdrCapacityMin,
metadata->hdrCapacityMax);
- return ERROR_JPEGR_INVALID_INPUT_TYPE;
+ return ERROR_JPEGR_BAD_METADATA;
}
if (metadata->offsetSdr < 0.0f || metadata->offsetHdr < 0.0f) {
ALOGE("received bad value for offset sdr %f, hdr %f", metadata->offsetSdr, metadata->offsetHdr);
- return ERROR_JPEGR_INVALID_INPUT_TYPE;
+ return ERROR_JPEGR_BAD_METADATA;
}
if (metadata->gamma <= 0.0f) {
ALOGE("received bad value for gamma %f", metadata->gamma);
- return ERROR_JPEGR_INVALID_INPUT_TYPE;
+ return ERROR_JPEGR_BAD_METADATA;
}
const string nameSpace = "http://ns.adobe.com/xap/1.0/";
- const int nameSpaceLength = nameSpace.size() + 1; // need to count the null terminator
+ const int nameSpaceLength = nameSpace.size() + 1; // need to count the null terminator
// calculate secondary image length first, because the length will be written into the primary
// image xmp
const string xmp_secondary = generateXmpForSecondaryImage(*metadata);
- const int xmp_secondary_length = 2 /* 2 bytes representing the length of the package */
- + nameSpaceLength /* 29 bytes length of name space including \0 */
- + xmp_secondary.size(); /* length of xmp packet */
+ // xmp_secondary_length = 2 bytes representing the length of the package +
+ // + nameSpaceLength = 29 bytes length
+ // + length of xmp packet = xmp_secondary.size()
+ const int xmp_secondary_length = 2 + nameSpaceLength + xmp_secondary.size();
const int secondary_image_size = 2 /* 2 bytes length of APP1 sign */
- + xmp_secondary_length + gainmap_jpg_image_ptr->length;
+ + xmp_secondary_length + gainmap_jpg_image_ptr->length;
// primary image
const string xmp_primary = generateXmpForPrimaryImage(secondary_image_size, *metadata);
// same as primary
@@ -1268,16 +1265,19 @@ status_t JpegR::appendGainMap(jr_compressed_ptr primary_jpg_image_ptr,
if (!decoder.extractEXIF(primary_jpg_image_ptr->data, primary_jpg_image_ptr->length)) {
return ERROR_JPEGR_DECODE_ERROR;
}
- jpegr_exif_struct exif_from_jpg = {.data = nullptr, .length = 0};
- jpegr_compressed_struct new_jpg_image = {.data = nullptr,
- .length = 0,
- .maxLength = 0,
- .colorGamut = ULTRAHDR_COLORGAMUT_UNSPECIFIED};
+ jpegr_exif_struct exif_from_jpg;
+ exif_from_jpg.data = nullptr;
+ exif_from_jpg.length = 0;
+ jpegr_compressed_struct new_jpg_image;
+ new_jpg_image.data = nullptr;
+ new_jpg_image.length = 0;
+ new_jpg_image.maxLength = 0;
+ new_jpg_image.colorGamut = ULTRAHDR_COLORGAMUT_UNSPECIFIED;
std::unique_ptr<uint8_t[]> dest_data;
if (decoder.getEXIFPos() >= 0) {
if (pExif != nullptr) {
ALOGE("received EXIF from outside while the primary image already contains EXIF");
- return ERROR_JPEGR_INVALID_INPUT_TYPE;
+ return ERROR_JPEGR_MULTIPLE_EXIFS_RECEIVED;
}
copyJpegWithoutExif(&new_jpg_image, primary_jpg_image_ptr, decoder.getEXIFPos(),
decoder.getEXIFSize());
@@ -1288,7 +1288,7 @@ status_t JpegR::appendGainMap(jr_compressed_ptr primary_jpg_image_ptr,
}
jr_compressed_ptr final_primary_jpg_image_ptr =
- new_jpg_image.length == 0 ? primary_jpg_image_ptr : &new_jpg_image;
+ new_jpg_image.length == 0 ? primary_jpg_image_ptr : &new_jpg_image;
int pos = 0;
// Begin primary image
@@ -1384,15 +1384,15 @@ status_t JpegR::appendGainMap(jr_compressed_ptr primary_jpg_image_ptr,
dest->length = pos;
// Done!
- return NO_ERROR;
+ return JPEGR_NO_ERROR;
}
status_t JpegR::toneMap(jr_uncompressed_ptr src, jr_uncompressed_ptr dest) {
if (src == nullptr || dest == nullptr) {
- return ERROR_JPEGR_INVALID_NULL_PTR;
+ return ERROR_JPEGR_BAD_PTR;
}
if (src->width != dest->width || src->height != dest->height) {
- return ERROR_JPEGR_INVALID_INPUT_TYPE;
+ return ERROR_JPEGR_RESOLUTION_MISMATCH;
}
uint16_t* src_y_data = reinterpret_cast<uint16_t*>(src->data);
uint8_t* dst_y_data = reinterpret_cast<uint8_t*>(dest->data);
@@ -1427,13 +1427,13 @@ status_t JpegR::toneMap(jr_uncompressed_ptr src, jr_uncompressed_ptr dest) {
}
}
dest->colorGamut = src->colorGamut;
- return NO_ERROR;
+ return JPEGR_NO_ERROR;
}
status_t JpegR::convertYuv(jr_uncompressed_ptr image, ultrahdr_color_gamut src_encoding,
ultrahdr_color_gamut dest_encoding) {
if (image == nullptr) {
- return ERROR_JPEGR_INVALID_NULL_PTR;
+ return ERROR_JPEGR_BAD_PTR;
}
if (src_encoding == ULTRAHDR_COLORGAMUT_UNSPECIFIED ||
dest_encoding == ULTRAHDR_COLORGAMUT_UNSPECIFIED) {
@@ -1445,7 +1445,7 @@ status_t JpegR::convertYuv(jr_uncompressed_ptr image, ultrahdr_color_gamut src_e
case ULTRAHDR_COLORGAMUT_BT709:
switch (dest_encoding) {
case ULTRAHDR_COLORGAMUT_BT709:
- return NO_ERROR;
+ return JPEGR_NO_ERROR;
case ULTRAHDR_COLORGAMUT_P3:
conversionFn = yuv709To601;
break;
@@ -1463,7 +1463,7 @@ status_t JpegR::convertYuv(jr_uncompressed_ptr image, ultrahdr_color_gamut src_e
conversionFn = yuv601To709;
break;
case ULTRAHDR_COLORGAMUT_P3:
- return NO_ERROR;
+ return JPEGR_NO_ERROR;
case ULTRAHDR_COLORGAMUT_BT2100:
conversionFn = yuv601To2100;
break;
@@ -1481,7 +1481,7 @@ status_t JpegR::convertYuv(jr_uncompressed_ptr image, ultrahdr_color_gamut src_e
conversionFn = yuv2100To601;
break;
case ULTRAHDR_COLORGAMUT_BT2100:
- return NO_ERROR;
+ return JPEGR_NO_ERROR;
default:
// Should be impossible to hit after input validation
return ERROR_JPEGR_INVALID_COLORGAMUT;
@@ -1503,7 +1503,7 @@ status_t JpegR::convertYuv(jr_uncompressed_ptr image, ultrahdr_color_gamut src_e
}
}
- return NO_ERROR;
+ return JPEGR_NO_ERROR;
}
-} // namespace ultrahdr
+} // namespace ultrahdr
diff --git a/lib/jpegr.h b/lib/jpegr.h
new file mode 100644
index 0000000..5c8f9c3
--- /dev/null
+++ b/lib/jpegr.h
@@ -0,0 +1,464 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * 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.
+ */
+
+#ifndef ULTRAHDR_JPEGR_H
+#define ULTRAHDR_JPEGR_H
+
+#include <cfloat>
+
+#include "ultrahdr.h"
+#include "jpegdecoderhelper.h"
+#include "jpegencoderhelper.h"
+
+namespace ultrahdr {
+
+// The current JPEGR version that we encode to
+static const char* const kJpegrVersion = "1.0";
+
+// Map is quarter res / sixteenth size
+static const size_t kMapDimensionScaleFactor = 4;
+
+// Gain Map width is (image_width / kMapDimensionScaleFactor). If we were to
+// compress 420 GainMap in jpeg, then we need at least 2 samples. For Grayscale
+// 1 sample is sufficient. We are using 2 here anyways
+static const int kMinWidth = 2 * kMapDimensionScaleFactor;
+static const int kMinHeight = 2 * kMapDimensionScaleFactor;
+
+typedef enum {
+ JPEGR_NO_ERROR = 0,
+ JPEGR_UNKNOWN_ERROR = -1,
+
+ JPEGR_IO_ERROR_BASE = -10000,
+ ERROR_JPEGR_BAD_PTR = JPEGR_IO_ERROR_BASE - 1,
+ ERROR_JPEGR_UNSUPPORTED_WIDTH_HEIGHT = JPEGR_IO_ERROR_BASE - 2,
+ ERROR_JPEGR_INVALID_COLORGAMUT = JPEGR_IO_ERROR_BASE - 3,
+ ERROR_JPEGR_INVALID_STRIDE = JPEGR_IO_ERROR_BASE - 4,
+ ERROR_JPEGR_INVALID_TRANS_FUNC = JPEGR_IO_ERROR_BASE - 5,
+ ERROR_JPEGR_RESOLUTION_MISMATCH = JPEGR_IO_ERROR_BASE - 6,
+ ERROR_JPEGR_INVALID_QUALITY_FACTOR = JPEGR_IO_ERROR_BASE - 7,
+ ERROR_JPEGR_INVALID_DISPLAY_BOOST = JPEGR_IO_ERROR_BASE - 8,
+ ERROR_JPEGR_INVALID_OUTPUT_FORMAT = JPEGR_IO_ERROR_BASE - 9,
+ ERROR_JPEGR_BAD_METADATA = JPEGR_IO_ERROR_BASE - 10,
+
+ JPEGR_RUNTIME_ERROR_BASE = -20000,
+ ERROR_JPEGR_ENCODE_ERROR = JPEGR_RUNTIME_ERROR_BASE - 1,
+ ERROR_JPEGR_DECODE_ERROR = JPEGR_RUNTIME_ERROR_BASE - 2,
+ ERROR_JPEGR_GAIN_MAP_IMAGE_NOT_FOUND = JPEGR_RUNTIME_ERROR_BASE - 3,
+ ERROR_JPEGR_BUFFER_TOO_SMALL = JPEGR_RUNTIME_ERROR_BASE - 4,
+ ERROR_JPEGR_METADATA_ERROR = JPEGR_RUNTIME_ERROR_BASE - 5,
+ ERROR_JPEGR_NO_IMAGES_FOUND = JPEGR_RUNTIME_ERROR_BASE - 6,
+ ERROR_JPEGR_MULTIPLE_EXIFS_RECEIVED = JPEGR_RUNTIME_ERROR_BASE - 7,
+
+ ERROR_JPEGR_UNSUPPORTED_FEATURE = -30000,
+} status_t;
+
+/*
+ * Holds information of jpegr image
+ */
+struct jpegr_info_struct {
+ size_t width;
+ size_t height;
+ std::vector<uint8_t>* iccData;
+ std::vector<uint8_t>* exifData;
+};
+
+/*
+ * Holds information for uncompressed image or gain map.
+ */
+struct jpegr_uncompressed_struct {
+ // Pointer to the data location.
+ void* data;
+ // Width of the gain map or the luma plane of the image in pixels.
+ size_t width;
+ // Height of the gain map or the luma plane of the image in pixels.
+ size_t height;
+ // Color gamut.
+ ultrahdr_color_gamut colorGamut;
+
+ // Values below are optional
+ // Pointer to chroma data, if it's NULL, chroma plane is considered to be immediately
+ // after the luma plane.
+ void* chroma_data = nullptr;
+ // Stride of Y plane in number of pixels. 0 indicates the member is uninitialized. If
+ // non-zero this value must be larger than or equal to luma width. If stride is
+ // uninitialized then it is assumed to be equal to luma width.
+ size_t luma_stride = 0;
+ // Stride of UV plane in number of pixels.
+ // 1. If this handle points to P010 image then this value must be larger than
+ // or equal to luma width.
+ // 2. If this handle points to 420 image then this value must be larger than
+ // or equal to (luma width / 2).
+ // NOTE: if chroma_data is nullptr, chroma_stride is irrelevant. Just as the way,
+ // chroma_data is derived from luma ptr, chroma stride is derived from luma stride.
+ size_t chroma_stride = 0;
+};
+
+/*
+ * Holds information for compressed image or gain map.
+ */
+struct jpegr_compressed_struct {
+ // Pointer to the data location.
+ void* data;
+ // Used data length in bytes.
+ int length;
+ // Maximum available data length in bytes.
+ int maxLength;
+ // Color gamut.
+ ultrahdr_color_gamut colorGamut;
+};
+
+/*
+ * Holds information for EXIF metadata.
+ */
+struct jpegr_exif_struct {
+ // Pointer to the data location.
+ void* data;
+ // Data length;
+ size_t length;
+};
+
+typedef struct jpegr_uncompressed_struct* jr_uncompressed_ptr;
+typedef struct jpegr_compressed_struct* jr_compressed_ptr;
+typedef struct jpegr_exif_struct* jr_exif_ptr;
+typedef struct jpegr_info_struct* jr_info_ptr;
+
+class JpegR {
+ public:
+ /*
+ * Experimental only
+ *
+ * Encode API-0
+ * Compress JPEGR image from 10-bit HDR YUV.
+ *
+ * Tonemap the HDR input to a SDR image, generate gain map from the HDR and SDR images,
+ * compress SDR YUV to 8-bit JPEG and append the gain map to the end of the compressed
+ * JPEG.
+ * @param p010_image_ptr uncompressed HDR image in P010 color format
+ * @param hdr_tf transfer function of the HDR image
+ * @param dest destination of the compressed JPEGR image. Please note that {@code maxLength}
+ * represents the maximum available size of the destination buffer, and it must be
+ * set before calling this method. If the encoded JPEGR size exceeds
+ * {@code maxLength}, this method will return {@code ERROR_JPEGR_BUFFER_TOO_SMALL}.
+ * @param quality target quality of the JPEG encoding, must be in range of 0-100 where 100 is
+ * the highest quality
+ * @param exif pointer to the exif metadata.
+ * @return NO_ERROR if encoding succeeds, error code if error occurs.
+ */
+ status_t encodeJPEGR(jr_uncompressed_ptr p010_image_ptr, ultrahdr_transfer_function hdr_tf,
+ jr_compressed_ptr dest, int quality, jr_exif_ptr exif);
+
+ /*
+ * Encode API-1
+ * Compress JPEGR image from 10-bit HDR YUV and 8-bit SDR YUV.
+ *
+ * Generate gain map from the HDR and SDR inputs, compress SDR YUV to 8-bit JPEG and append
+ * the gain map to the end of the compressed JPEG. HDR and SDR inputs must be the same
+ * resolution. SDR input is assumed to use the sRGB transfer function.
+ * @param p010_image_ptr uncompressed HDR image in P010 color format
+ * @param yuv420_image_ptr uncompressed SDR image in YUV_420 color format
+ * @param hdr_tf transfer function of the HDR image
+ * @param dest destination of the compressed JPEGR image. Please note that {@code maxLength}
+ * represents the maximum available size of the desitination buffer, and it must be
+ * set before calling this method. If the encoded JPEGR size exceeds
+ * {@code maxLength}, this method will return {@code ERROR_JPEGR_BUFFER_TOO_SMALL}.
+ * @param quality target quality of the JPEG encoding, must be in range of 0-100 where 100 is
+ * the highest quality
+ * @param exif pointer to the exif metadata.
+ * @return NO_ERROR if encoding succeeds, error code if error occurs.
+ */
+ status_t encodeJPEGR(jr_uncompressed_ptr p010_image_ptr, jr_uncompressed_ptr yuv420_image_ptr,
+ ultrahdr_transfer_function hdr_tf, jr_compressed_ptr dest, int quality,
+ jr_exif_ptr exif);
+
+ /*
+ * Encode API-2
+ * Compress JPEGR image from 10-bit HDR YUV, 8-bit SDR YUV and compressed 8-bit JPEG.
+ *
+ * This method requires HAL Hardware JPEG encoder.
+ *
+ * Generate gain map from the HDR and SDR inputs, append the gain map to the end of the
+ * compressed JPEG. Adds an ICC profile if one isn't present in the input JPEG image. HDR and
+ * SDR inputs must be the same resolution and color space. SDR image is assumed to use the sRGB
+ * transfer function.
+ * @param p010_image_ptr uncompressed HDR image in P010 color format
+ * @param yuv420_image_ptr uncompressed SDR image in YUV_420 color format
+ * @param yuv420jpg_image_ptr SDR image compressed in jpeg format
+ * @param hdr_tf transfer function of the HDR image
+ * @param dest destination of the compressed JPEGR image. Please note that {@code maxLength}
+ * represents the maximum available size of the desitination buffer, and it must be
+ * set before calling this method. If the encoded JPEGR size exceeds
+ * {@code maxLength}, this method will return {@code ERROR_JPEGR_BUFFER_TOO_SMALL}.
+ * @return NO_ERROR if encoding succeeds, error code if error occurs.
+ */
+ status_t encodeJPEGR(jr_uncompressed_ptr p010_image_ptr, jr_uncompressed_ptr yuv420_image_ptr,
+ jr_compressed_ptr yuv420jpg_image_ptr, ultrahdr_transfer_function hdr_tf,
+ jr_compressed_ptr dest);
+
+ /*
+ * Encode API-3
+ * Compress JPEGR image from 10-bit HDR YUV and 8-bit SDR YUV.
+ *
+ * This method requires HAL Hardware JPEG encoder.
+ *
+ * Decode the compressed 8-bit JPEG image to YUV SDR, generate gain map from the HDR input
+ * and the decoded SDR result, append the gain map to the end of the compressed JPEG. Adds an
+ * ICC profile if one isn't present in the input JPEG image. HDR and SDR inputs must be the same
+ * resolution. JPEG image is assumed to use the sRGB transfer function.
+ * @param p010_image_ptr uncompressed HDR image in P010 color format
+ * @param yuv420jpg_image_ptr SDR image compressed in jpeg format
+ * @param hdr_tf transfer function of the HDR image
+ * @param dest destination of the compressed JPEGR image. Please note that {@code maxLength}
+ * represents the maximum available size of the desitination buffer, and it must be
+ * set before calling this method. If the encoded JPEGR size exceeds
+ * {@code maxLength}, this method will return {@code ERROR_JPEGR_BUFFER_TOO_SMALL}.
+ * @return NO_ERROR if encoding succeeds, error code if error occurs.
+ */
+ status_t encodeJPEGR(jr_uncompressed_ptr p010_image_ptr, jr_compressed_ptr yuv420jpg_image_ptr,
+ ultrahdr_transfer_function hdr_tf, jr_compressed_ptr dest);
+
+ /*
+ * Encode API-4
+ * Assemble JPEGR image from SDR JPEG and gainmap JPEG.
+ *
+ * Assemble the primary JPEG image, the gain map and the metadata to JPEG/R format. Adds an ICC
+ * profile if one isn't present in the input JPEG image.
+ * @param yuv420jpg_image_ptr SDR image compressed in jpeg format
+ * @param gainmapjpg_image_ptr gain map image compressed in jpeg format
+ * @param metadata metadata to be written in XMP of the primary jpeg
+ * @param dest destination of the compressed JPEGR image. Please note that {@code maxLength}
+ * represents the maximum available size of the desitination buffer, and it must be
+ * set before calling this method. If the encoded JPEGR size exceeds
+ * {@code maxLength}, this method will return {@code ERROR_JPEGR_BUFFER_TOO_SMALL}.
+ * @return NO_ERROR if encoding succeeds, error code if error occurs.
+ */
+ status_t encodeJPEGR(jr_compressed_ptr yuv420jpg_image_ptr,
+ jr_compressed_ptr gainmapjpg_image_ptr, ultrahdr_metadata_ptr metadata,
+ jr_compressed_ptr dest);
+
+ /*
+ * Decode API
+ * Decompress JPEGR image.
+ *
+ * This method assumes that the JPEGR image contains an ICC profile with primaries that match
+ * those of a color gamut that this library is aware of; Bt.709, Display-P3, or Bt.2100. It also
+ * assumes the base image uses the sRGB transfer function.
+ *
+ * This method only supports single gain map metadata values for fields that allow multi-channel
+ * metadata values.
+ * @param jpegr_image_ptr compressed JPEGR image.
+ * @param dest destination of the uncompressed JPEGR image.
+ * @param max_display_boost (optional) the maximum available boost supported by a display,
+ * the value must be greater than or equal to 1.0.
+ * @param exif destination of the decoded EXIF metadata. The default value is NULL where the
+ decoder will do nothing about it. If configured not NULL the decoder will write
+ EXIF data into this structure. The format is defined in {@code jpegr_exif_struct}
+ * @param output_format flag for setting output color format. Its value configures the output
+ color format. The default value is {@code JPEGR_OUTPUT_HDR_LINEAR}.
+ ----------------------------------------------------------------------
+ | output_format | decoded color format to be written |
+ ----------------------------------------------------------------------
+ | JPEGR_OUTPUT_SDR | RGBA_8888 |
+ ----------------------------------------------------------------------
+ | JPEGR_OUTPUT_HDR_LINEAR | (default)RGBA_F16 linear |
+ ----------------------------------------------------------------------
+ | JPEGR_OUTPUT_HDR_PQ | RGBA_1010102 PQ |
+ ----------------------------------------------------------------------
+ | JPEGR_OUTPUT_HDR_HLG | RGBA_1010102 HLG |
+ ----------------------------------------------------------------------
+ * @param gainmap_image_ptr destination of the decoded gain map. The default value is NULL
+ where the decoder will do nothing about it. If configured not NULL
+ the decoder will write the decoded gain_map data into this
+ structure. The format is defined in
+ {@code jpegr_uncompressed_struct}.
+ * @param metadata destination of the decoded metadata. The default value is NULL where the
+ decoder will do nothing about it. If configured not NULL the decoder will
+ write metadata into this structure. the format of metadata is defined in
+ {@code ultrahdr_metadata_struct}.
+ * @return NO_ERROR if decoding succeeds, error code if error occurs.
+ */
+ status_t decodeJPEGR(jr_compressed_ptr jpegr_image_ptr, jr_uncompressed_ptr dest,
+ float max_display_boost = FLT_MAX, jr_exif_ptr exif = nullptr,
+ ultrahdr_output_format output_format = ULTRAHDR_OUTPUT_HDR_LINEAR,
+ jr_uncompressed_ptr gainmap_image_ptr = nullptr,
+ ultrahdr_metadata_ptr metadata = nullptr);
+
+ /*
+ * Gets Info from JPEGR file without decoding it.
+ *
+ * This method only supports single gain map metadata values for fields that allow multi-channel
+ * metadata values.
+ *
+ * The output is filled jpegr_info structure
+ * @param jpegr_image_ptr compressed JPEGR image
+ * @param jpeg_image_info_ptr pointer to jpegr info struct. Members of jpegr_info
+ * are owned by the caller
+ * @return NO_ERROR if JPEGR parsing succeeds, error code otherwise
+ */
+ status_t getJPEGRInfo(jr_compressed_ptr jpegr_image_ptr, jr_info_ptr jpeg_image_info_ptr);
+
+ protected:
+ /*
+ * This method is called in the encoding pipeline. It will take the uncompressed 8-bit and
+ * 10-bit yuv images as input, and calculate the uncompressed gain map. The input images
+ * must be the same resolution. The SDR input is assumed to use the sRGB transfer function.
+ *
+ * @param yuv420_image_ptr uncompressed SDR image in YUV_420 color format
+ * @param p010_image_ptr uncompressed HDR image in P010 color format
+ * @param hdr_tf transfer function of the HDR image
+ * @param metadata everything but "version" is filled in this struct
+ * @param dest location at which gain map image is stored (caller responsible for memory
+ of data).
+ * @param sdr_is_601 if true, then use BT.601 decoding of YUV regardless of SDR image gamut
+ * @return NO_ERROR if calculation succeeds, error code if error occurs.
+ */
+ status_t generateGainMap(jr_uncompressed_ptr yuv420_image_ptr, jr_uncompressed_ptr p010_image_ptr,
+ ultrahdr_transfer_function hdr_tf, ultrahdr_metadata_ptr metadata,
+ jr_uncompressed_ptr dest, bool sdr_is_601 = false);
+
+ /*
+ * This method is called in the decoding pipeline. It will take the uncompressed (decoded)
+ * 8-bit yuv image, the uncompressed (decoded) gain map, and extracted JPEG/R metadata as
+ * input, and calculate the 10-bit recovered image. The recovered output image is the same
+ * color gamut as the SDR image, with HLG transfer function, and is in RGBA1010102 data format.
+ * The SDR image is assumed to use the sRGB transfer function. The SDR image is also assumed to
+ * be a decoded JPEG for the purpose of YUV interpration.
+ *
+ * @param yuv420_image_ptr uncompressed SDR image in YUV_420 color format
+ * @param gainmap_image_ptr pointer to uncompressed gain map image struct.
+ * @param metadata JPEG/R metadata extracted from XMP.
+ * @param output_format flag for setting output color format. if set to
+ * {@code JPEGR_OUTPUT_SDR}, decoder will only decode the primary image
+ * which is SDR. Default value is JPEGR_OUTPUT_HDR_LINEAR.
+ * @param max_display_boost the maximum available boost supported by a display
+ * @param dest reconstructed HDR image
+ * @return NO_ERROR if calculation succeeds, error code if error occurs.
+ */
+ status_t applyGainMap(jr_uncompressed_ptr yuv420_image_ptr, jr_uncompressed_ptr gainmap_image_ptr,
+ ultrahdr_metadata_ptr metadata, ultrahdr_output_format output_format,
+ float max_display_boost, jr_uncompressed_ptr dest);
+
+ private:
+ /*
+ * This method is called in the encoding pipeline. It will encode the gain map.
+ *
+ * @param gainmap_image_ptr pointer to uncompressed gain map image struct
+ * @param jpeg_enc_obj_ptr helper resource to compress gain map
+ * @return NO_ERROR if encoding succeeds, error code if error occurs.
+ */
+ status_t compressGainMap(jr_uncompressed_ptr gainmap_image_ptr,
+ JpegEncoderHelper* jpeg_enc_obj_ptr);
+
+ /*
+ * This method is called to separate primary image and gain map image from JPEGR
+ *
+ * @param jpegr_image_ptr pointer to compressed JPEGR image.
+ * @param primary_jpg_image_ptr destination of primary image
+ * @param gainmap_jpg_image_ptr destination of compressed gain map image
+ * @return NO_ERROR if calculation succeeds, error code if error occurs.
+ */
+ status_t extractPrimaryImageAndGainMap(jr_compressed_ptr jpegr_image_ptr,
+ jr_compressed_ptr primary_jpg_image_ptr,
+ jr_compressed_ptr gainmap_jpg_image_ptr);
+
+ /*
+ * This method is called in the encoding pipeline. It will take the standard 8-bit JPEG image,
+ * the compressed gain map and optionally the exif package as inputs, and generate the XMP
+ * metadata, and finally append everything in the order of:
+ * SOI, APP2(EXIF) (if EXIF is from outside), APP2(XMP), primary image, gain map
+ *
+ * Note that in the final JPEG/R output, EXIF package will appear if ONLY ONE of the following
+ * conditions is fulfilled:
+ * (1) EXIF package is available from outside input. I.e. pExif != nullptr.
+ * (2) Input JPEG has EXIF.
+ * If both conditions are fulfilled, this method will return ERROR_JPEGR_INVALID_INPUT_TYPE
+ *
+ * @param primary_jpg_image_ptr destination of primary image
+ * @param gainmap_jpg_image_ptr destination of compressed gain map image
+ * @param (nullable) pExif EXIF package
+ * @param (nullable) pIcc ICC package
+ * @param icc_size length in bytes of ICC package
+ * @param metadata JPEG/R metadata to encode in XMP of the jpeg
+ * @param dest compressed JPEGR image
+ * @return NO_ERROR if calculation succeeds, error code if error occurs.
+ */
+ status_t appendGainMap(jr_compressed_ptr primary_jpg_image_ptr,
+ jr_compressed_ptr gainmap_jpg_image_ptr, jr_exif_ptr pExif, void* pIcc,
+ size_t icc_size, ultrahdr_metadata_ptr metadata, jr_compressed_ptr dest);
+
+ /*
+ * This method will tone map a HDR image to an SDR image.
+ *
+ * @param src pointer to uncompressed HDR image struct. HDR image is expected to be
+ * in p010 color format
+ * @param dest pointer to store tonemapped SDR image
+ */
+ status_t toneMap(jr_uncompressed_ptr src, jr_uncompressed_ptr dest);
+
+ /*
+ * This method will convert a YUV420 image from one YUV encoding to another in-place (eg.
+ * Bt.709 to Bt.601 YUV encoding).
+ *
+ * src_encoding and dest_encoding indicate the encoding via the YUV conversion defined for that
+ * gamut. P3 indicates Rec.601, since this is how DataSpace encodes Display-P3 YUV data.
+ *
+ * @param image the YUV420 image to convert
+ * @param src_encoding input YUV encoding
+ * @param dest_encoding output YUV encoding
+ * @return NO_ERROR if calculation succeeds, error code if error occurs.
+ */
+ status_t convertYuv(jr_uncompressed_ptr image, ultrahdr_color_gamut src_encoding,
+ ultrahdr_color_gamut dest_encoding);
+
+ /*
+ * This method will check the validity of the input arguments.
+ *
+ * @param p010_image_ptr uncompressed HDR image in P010 color format
+ * @param yuv420_image_ptr pointer to uncompressed SDR image struct. HDR image is expected to
+ * be in 420p color format
+ * @param hdr_tf transfer function of the HDR image
+ * @param dest destination of the compressed JPEGR image. Please note that {@code maxLength}
+ * represents the maximum available size of the desitination buffer, and it must be
+ * set before calling this method. If the encoded JPEGR size exceeds
+ * {@code maxLength}, this method will return {@code ERROR_JPEGR_BUFFER_TOO_SMALL}.
+ * @return NO_ERROR if the input args are valid, error code is not valid.
+ */
+ status_t areInputArgumentsValid(jr_uncompressed_ptr p010_image_ptr,
+ jr_uncompressed_ptr yuv420_image_ptr,
+ ultrahdr_transfer_function hdr_tf, jr_compressed_ptr dest_ptr);
+
+ /*
+ * This method will check the validity of the input arguments.
+ *
+ * @param p010_image_ptr uncompressed HDR image in P010 color format
+ * @param yuv420_image_ptr pointer to uncompressed SDR image struct. HDR image is expected to
+ * be in 420p color format
+ * @param hdr_tf transfer function of the HDR image
+ * @param dest destination of the compressed JPEGR image. Please note that {@code maxLength}
+ * represents the maximum available size of the destination buffer, and it must be
+ * set before calling this method. If the encoded JPEGR size exceeds
+ * {@code maxLength}, this method will return {@code ERROR_JPEGR_BUFFER_TOO_SMALL}.
+ * @param quality target quality of the JPEG encoding, must be in range of 0-100 where 100 is
+ * the highest quality
+ * @return NO_ERROR if the input args are valid, error code is not valid.
+ */
+ status_t areInputArgumentsValid(jr_uncompressed_ptr p010_image_ptr,
+ jr_uncompressed_ptr yuv420_image_ptr,
+ ultrahdr_transfer_function hdr_tf, jr_compressed_ptr dest,
+ int quality);
+};
+} // namespace ultrahdr
+
+#endif // ULTRAHDR_JPEGR_H
diff --git a/lib/jpegrutils.cpp b/lib/jpegrutils.cpp
new file mode 100644
index 0000000..2fdc347
--- /dev/null
+++ b/lib/jpegrutils.cpp
@@ -0,0 +1,583 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * 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.
+ */
+
+#include <algorithm>
+#include <cmath>
+
+#include "ultrahdrcommon.h"
+#include "jpegr.h"
+#include "jpegrutils.h"
+
+#include "image_io/xml/xml_reader.h"
+#include "image_io/xml/xml_writer.h"
+#include "image_io/base/message_handler.h"
+#include "image_io/xml/xml_element_rules.h"
+#include "image_io/xml/xml_handler.h"
+#include "image_io/xml/xml_rule.h"
+
+using namespace photos_editing_formats::image_io;
+using namespace std;
+
+namespace ultrahdr {
+/*
+ * Helper function used for generating XMP metadata.
+ *
+ * @param prefix The prefix part of the name.
+ * @param suffix The suffix part of the name.
+ * @return A name of the form "prefix:suffix".
+ */
+static inline string Name(const string& prefix, const string& suffix) {
+ std::stringstream ss;
+ ss << prefix << ":" << suffix;
+ return ss.str();
+}
+
+DataStruct::DataStruct(int s) {
+ data = malloc(s);
+ length = s;
+ memset(data, 0, s);
+ writePos = 0;
+}
+
+DataStruct::~DataStruct() {
+ if (data != nullptr) {
+ free(data);
+ }
+}
+
+void* DataStruct::getData() { return data; }
+
+int DataStruct::getLength() { return length; }
+
+int DataStruct::getBytesWritten() { return writePos; }
+
+bool DataStruct::write8(uint8_t value) {
+ uint8_t v = value;
+ return write(&v, 1);
+}
+
+bool DataStruct::write16(uint16_t value) {
+ uint16_t v = value;
+ return write(&v, 2);
+}
+bool DataStruct::write32(uint32_t value) {
+ uint32_t v = value;
+ return write(&v, 4);
+}
+
+bool DataStruct::write(const void* src, int size) {
+ if (writePos + size > length) {
+ ALOGE("Writing out of boundary: write position: %d, size: %d, capacity: %d", writePos, size,
+ length);
+ return false;
+ }
+ memcpy((uint8_t*)data + writePos, src, size);
+ writePos += size;
+ return true;
+}
+
+/*
+ * Helper function used for writing data to destination.
+ */
+status_t Write(jr_compressed_ptr destination, const void* source, size_t length, int& position) {
+ if (position + length > destination->maxLength) {
+ return ERROR_JPEGR_BUFFER_TOO_SMALL;
+ }
+
+ memcpy((uint8_t*)destination->data + sizeof(uint8_t) * position, source, length);
+ position += length;
+ return JPEGR_NO_ERROR;
+}
+
+// Extremely simple XML Handler - just searches for interesting elements
+class XMPXmlHandler : public XmlHandler {
+ public:
+ XMPXmlHandler() : XmlHandler() {
+ state = NotStrarted;
+ versionFound = false;
+ minContentBoostFound = false;
+ maxContentBoostFound = false;
+ gammaFound = false;
+ offsetSdrFound = false;
+ offsetHdrFound = false;
+ hdrCapacityMinFound = false;
+ hdrCapacityMaxFound = false;
+ baseRenditionIsHdrFound = false;
+ }
+
+ enum ParseState { NotStrarted, Started, Done };
+
+ virtual DataMatchResult StartElement(const XmlTokenContext& context) {
+ string val;
+ if (context.BuildTokenValue(&val)) {
+ if (!val.compare(containerName)) {
+ state = Started;
+ } else {
+ if (state != Done) {
+ state = NotStrarted;
+ }
+ }
+ }
+ return context.GetResult();
+ }
+
+ virtual DataMatchResult FinishElement(const XmlTokenContext& context) {
+ if (state == Started) {
+ state = Done;
+ lastAttributeName = "";
+ }
+ return context.GetResult();
+ }
+
+ virtual DataMatchResult AttributeName(const XmlTokenContext& context) {
+ string val;
+ if (state == Started) {
+ if (context.BuildTokenValue(&val)) {
+ if (!val.compare(versionAttrName)) {
+ lastAttributeName = versionAttrName;
+ } else if (!val.compare(maxContentBoostAttrName)) {
+ lastAttributeName = maxContentBoostAttrName;
+ } else if (!val.compare(minContentBoostAttrName)) {
+ lastAttributeName = minContentBoostAttrName;
+ } else if (!val.compare(gammaAttrName)) {
+ lastAttributeName = gammaAttrName;
+ } else if (!val.compare(offsetSdrAttrName)) {
+ lastAttributeName = offsetSdrAttrName;
+ } else if (!val.compare(offsetHdrAttrName)) {
+ lastAttributeName = offsetHdrAttrName;
+ } else if (!val.compare(hdrCapacityMinAttrName)) {
+ lastAttributeName = hdrCapacityMinAttrName;
+ } else if (!val.compare(hdrCapacityMaxAttrName)) {
+ lastAttributeName = hdrCapacityMaxAttrName;
+ } else if (!val.compare(baseRenditionIsHdrAttrName)) {
+ lastAttributeName = baseRenditionIsHdrAttrName;
+ } else {
+ lastAttributeName = "";
+ }
+ }
+ }
+ return context.GetResult();
+ }
+
+ virtual DataMatchResult AttributeValue(const XmlTokenContext& context) {
+ string val;
+ if (state == Started) {
+ if (context.BuildTokenValue(&val, true)) {
+ if (!lastAttributeName.compare(versionAttrName)) {
+ versionStr = val;
+ versionFound = true;
+ } else if (!lastAttributeName.compare(maxContentBoostAttrName)) {
+ maxContentBoostStr = val;
+ maxContentBoostFound = true;
+ } else if (!lastAttributeName.compare(minContentBoostAttrName)) {
+ minContentBoostStr = val;
+ minContentBoostFound = true;
+ } else if (!lastAttributeName.compare(gammaAttrName)) {
+ gammaStr = val;
+ gammaFound = true;
+ } else if (!lastAttributeName.compare(offsetSdrAttrName)) {
+ offsetSdrStr = val;
+ offsetSdrFound = true;
+ } else if (!lastAttributeName.compare(offsetHdrAttrName)) {
+ offsetHdrStr = val;
+ offsetHdrFound = true;
+ } else if (!lastAttributeName.compare(hdrCapacityMinAttrName)) {
+ hdrCapacityMinStr = val;
+ hdrCapacityMinFound = true;
+ } else if (!lastAttributeName.compare(hdrCapacityMaxAttrName)) {
+ hdrCapacityMaxStr = val;
+ hdrCapacityMaxFound = true;
+ } else if (!lastAttributeName.compare(baseRenditionIsHdrAttrName)) {
+ baseRenditionIsHdrStr = val;
+ baseRenditionIsHdrFound = true;
+ }
+ }
+ }
+ return context.GetResult();
+ }
+
+ bool getVersion(string* version, bool* present) {
+ if (state == Done) {
+ *version = versionStr;
+ *present = versionFound;
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ bool getMaxContentBoost(float* max_content_boost, bool* present) {
+ if (state == Done) {
+ *present = maxContentBoostFound;
+ stringstream ss(maxContentBoostStr);
+ float val;
+ if (ss >> val) {
+ *max_content_boost = exp2(val);
+ return true;
+ } else {
+ return false;
+ }
+ } else {
+ return false;
+ }
+ }
+
+ bool getMinContentBoost(float* min_content_boost, bool* present) {
+ if (state == Done) {
+ *present = minContentBoostFound;
+ stringstream ss(minContentBoostStr);
+ float val;
+ if (ss >> val) {
+ *min_content_boost = exp2(val);
+ return true;
+ } else {
+ return false;
+ }
+ } else {
+ return false;
+ }
+ }
+
+ bool getGamma(float* gamma, bool* present) {
+ if (state == Done) {
+ *present = gammaFound;
+ stringstream ss(gammaStr);
+ float val;
+ if (ss >> val) {
+ *gamma = val;
+ return true;
+ } else {
+ return false;
+ }
+ } else {
+ return false;
+ }
+ }
+
+ bool getOffsetSdr(float* offset_sdr, bool* present) {
+ if (state == Done) {
+ *present = offsetSdrFound;
+ stringstream ss(offsetSdrStr);
+ float val;
+ if (ss >> val) {
+ *offset_sdr = val;
+ return true;
+ } else {
+ return false;
+ }
+ } else {
+ return false;
+ }
+ }
+
+ bool getOffsetHdr(float* offset_hdr, bool* present) {
+ if (state == Done) {
+ *present = offsetHdrFound;
+ stringstream ss(offsetHdrStr);
+ float val;
+ if (ss >> val) {
+ *offset_hdr = val;
+ return true;
+ } else {
+ return false;
+ }
+ } else {
+ return false;
+ }
+ }
+
+ bool getHdrCapacityMin(float* hdr_capacity_min, bool* present) {
+ if (state == Done) {
+ *present = hdrCapacityMinFound;
+ stringstream ss(hdrCapacityMinStr);
+ float val;
+ if (ss >> val) {
+ *hdr_capacity_min = exp2(val);
+ return true;
+ } else {
+ return false;
+ }
+ } else {
+ return false;
+ }
+ }
+
+ bool getHdrCapacityMax(float* hdr_capacity_max, bool* present) {
+ if (state == Done) {
+ *present = hdrCapacityMaxFound;
+ stringstream ss(hdrCapacityMaxStr);
+ float val;
+ if (ss >> val) {
+ *hdr_capacity_max = exp2(val);
+ return true;
+ } else {
+ return false;
+ }
+ } else {
+ return false;
+ }
+ }
+
+ bool getBaseRenditionIsHdr(bool* base_rendition_is_hdr, bool* present) {
+ if (state == Done) {
+ *present = baseRenditionIsHdrFound;
+ if (!baseRenditionIsHdrStr.compare("False")) {
+ *base_rendition_is_hdr = false;
+ return true;
+ } else if (!baseRenditionIsHdrStr.compare("True")) {
+ *base_rendition_is_hdr = true;
+ return true;
+ } else {
+ return false;
+ }
+ } else {
+ return false;
+ }
+ }
+
+ private:
+ static const string containerName;
+
+ static const string versionAttrName;
+ string versionStr;
+ bool versionFound;
+ static const string maxContentBoostAttrName;
+ string maxContentBoostStr;
+ bool maxContentBoostFound;
+ static const string minContentBoostAttrName;
+ string minContentBoostStr;
+ bool minContentBoostFound;
+ static const string gammaAttrName;
+ string gammaStr;
+ bool gammaFound;
+ static const string offsetSdrAttrName;
+ string offsetSdrStr;
+ bool offsetSdrFound;
+ static const string offsetHdrAttrName;
+ string offsetHdrStr;
+ bool offsetHdrFound;
+ static const string hdrCapacityMinAttrName;
+ string hdrCapacityMinStr;
+ bool hdrCapacityMinFound;
+ static const string hdrCapacityMaxAttrName;
+ string hdrCapacityMaxStr;
+ bool hdrCapacityMaxFound;
+ static const string baseRenditionIsHdrAttrName;
+ string baseRenditionIsHdrStr;
+ bool baseRenditionIsHdrFound;
+
+ string lastAttributeName;
+ ParseState state;
+};
+
+// GContainer XMP constants - URI and namespace prefix
+const string kContainerUri = "http://ns.google.com/photos/1.0/container/";
+const string kContainerPrefix = "Container";
+
+// GContainer XMP constants - element and attribute names
+const string kConDirectory = Name(kContainerPrefix, "Directory");
+const string kConItem = Name(kContainerPrefix, "Item");
+
+// GContainer XMP constants - names for XMP handlers
+const string XMPXmlHandler::containerName = "rdf:Description";
+// Item XMP constants - URI and namespace prefix
+const string kItemUri = "http://ns.google.com/photos/1.0/container/item/";
+const string kItemPrefix = "Item";
+
+// Item XMP constants - element and attribute names
+const string kItemLength = Name(kItemPrefix, "Length");
+const string kItemMime = Name(kItemPrefix, "Mime");
+const string kItemSemantic = Name(kItemPrefix, "Semantic");
+
+// Item XMP constants - element and attribute values
+const string kSemanticPrimary = "Primary";
+const string kSemanticGainMap = "GainMap";
+const string kMimeImageJpeg = "image/jpeg";
+
+// GainMap XMP constants - URI and namespace prefix
+const string kGainMapUri = "http://ns.adobe.com/hdr-gain-map/1.0/";
+const string kGainMapPrefix = "hdrgm";
+
+// GainMap XMP constants - element and attribute names
+const string kMapVersion = Name(kGainMapPrefix, "Version");
+const string kMapGainMapMin = Name(kGainMapPrefix, "GainMapMin");
+const string kMapGainMapMax = Name(kGainMapPrefix, "GainMapMax");
+const string kMapGamma = Name(kGainMapPrefix, "Gamma");
+const string kMapOffsetSdr = Name(kGainMapPrefix, "OffsetSDR");
+const string kMapOffsetHdr = Name(kGainMapPrefix, "OffsetHDR");
+const string kMapHDRCapacityMin = Name(kGainMapPrefix, "HDRCapacityMin");
+const string kMapHDRCapacityMax = Name(kGainMapPrefix, "HDRCapacityMax");
+const string kMapBaseRenditionIsHDR = Name(kGainMapPrefix, "BaseRenditionIsHDR");
+
+// GainMap XMP constants - names for XMP handlers
+const string XMPXmlHandler::versionAttrName = kMapVersion;
+const string XMPXmlHandler::minContentBoostAttrName = kMapGainMapMin;
+const string XMPXmlHandler::maxContentBoostAttrName = kMapGainMapMax;
+const string XMPXmlHandler::gammaAttrName = kMapGamma;
+const string XMPXmlHandler::offsetSdrAttrName = kMapOffsetSdr;
+const string XMPXmlHandler::offsetHdrAttrName = kMapOffsetHdr;
+const string XMPXmlHandler::hdrCapacityMinAttrName = kMapHDRCapacityMin;
+const string XMPXmlHandler::hdrCapacityMaxAttrName = kMapHDRCapacityMax;
+const string XMPXmlHandler::baseRenditionIsHdrAttrName = kMapBaseRenditionIsHDR;
+
+bool getMetadataFromXMP(uint8_t* xmp_data, size_t xmp_size, ultrahdr_metadata_struct* metadata) {
+ string nameSpace = "http://ns.adobe.com/xap/1.0/\0";
+
+ if (xmp_size < nameSpace.size() + 2) {
+ // Data too short
+ return false;
+ }
+
+ if (strncmp(reinterpret_cast<char*>(xmp_data), nameSpace.c_str(), nameSpace.size())) {
+ // Not correct namespace
+ return false;
+ }
+
+ // Position the pointers to the start of XMP XML portion
+ xmp_data += nameSpace.size() + 1;
+ xmp_size -= nameSpace.size() + 1;
+ XMPXmlHandler handler;
+
+ // We need to remove tail data until the closing tag. Otherwise parser will throw an error.
+ while (xmp_data[xmp_size - 1] != '>' && xmp_size > 1) {
+ xmp_size--;
+ }
+
+ string str(reinterpret_cast<const char*>(xmp_data), xmp_size);
+ MessageHandler msg_handler;
+ unique_ptr<XmlRule> rule(new XmlElementRule);
+ XmlReader reader(&handler, &msg_handler);
+ reader.StartParse(std::move(rule));
+ reader.Parse(str);
+ reader.FinishParse();
+ if (reader.HasErrors()) {
+ // Parse error
+ return false;
+ }
+
+ // Apply default values to any not-present fields, except for Version,
+ // maxContentBoost, and hdrCapacityMax, which are required. Return false if
+ // we encounter a present field that couldn't be parsed, since this
+ // indicates it is invalid (eg. string where there should be a float).
+ bool present = false;
+ if (!handler.getVersion(&metadata->version, &present) || !present) {
+ return false;
+ }
+ if (!handler.getMaxContentBoost(&metadata->maxContentBoost, &present) || !present) {
+ return false;
+ }
+ if (!handler.getHdrCapacityMax(&metadata->hdrCapacityMax, &present) || !present) {
+ return false;
+ }
+ if (!handler.getMinContentBoost(&metadata->minContentBoost, &present)) {
+ if (present) return false;
+ metadata->minContentBoost = 1.0f;
+ }
+ if (!handler.getGamma(&metadata->gamma, &present)) {
+ if (present) return false;
+ metadata->gamma = 1.0f;
+ }
+ if (!handler.getOffsetSdr(&metadata->offsetSdr, &present)) {
+ if (present) return false;
+ metadata->offsetSdr = 1.0f / 64.0f;
+ }
+ if (!handler.getOffsetHdr(&metadata->offsetHdr, &present)) {
+ if (present) return false;
+ metadata->offsetHdr = 1.0f / 64.0f;
+ }
+ if (!handler.getHdrCapacityMin(&metadata->hdrCapacityMin, &present)) {
+ if (present) return false;
+ metadata->hdrCapacityMin = 1.0f;
+ }
+
+ bool base_rendition_is_hdr;
+ if (!handler.getBaseRenditionIsHdr(&base_rendition_is_hdr, &present)) {
+ if (present) return false;
+ base_rendition_is_hdr = false;
+ }
+ if (base_rendition_is_hdr) {
+ ALOGE("Base rendition of HDR is not supported!");
+ return false;
+ }
+
+ return true;
+}
+
+string generateXmpForPrimaryImage(int secondary_image_length, ultrahdr_metadata_struct& metadata) {
+ const vector<string> kConDirSeq({kConDirectory, string("rdf:Seq")});
+ const vector<string> kLiItem({string("rdf:li"), kConItem});
+
+ std::stringstream ss;
+ photos_editing_formats::image_io::XmlWriter writer(ss);
+ writer.StartWritingElement("x:xmpmeta");
+ writer.WriteXmlns("x", "adobe:ns:meta/");
+ writer.WriteAttributeNameAndValue("x:xmptk", "Adobe XMP Core 5.1.2");
+ writer.StartWritingElement("rdf:RDF");
+ writer.WriteXmlns("rdf", "http://www.w3.org/1999/02/22-rdf-syntax-ns#");
+ writer.StartWritingElement("rdf:Description");
+ writer.WriteXmlns(kContainerPrefix, kContainerUri);
+ writer.WriteXmlns(kItemPrefix, kItemUri);
+ writer.WriteXmlns(kGainMapPrefix, kGainMapUri);
+ writer.WriteAttributeNameAndValue(kMapVersion, metadata.version);
+
+ writer.StartWritingElements(kConDirSeq);
+
+ size_t item_depth = writer.StartWritingElement("rdf:li");
+ writer.WriteAttributeNameAndValue("rdf:parseType", "Resource");
+ writer.StartWritingElement(kConItem);
+ writer.WriteAttributeNameAndValue(kItemSemantic, kSemanticPrimary);
+ writer.WriteAttributeNameAndValue(kItemMime, kMimeImageJpeg);
+ writer.FinishWritingElementsToDepth(item_depth);
+
+ writer.StartWritingElement("rdf:li");
+ writer.WriteAttributeNameAndValue("rdf:parseType", "Resource");
+ writer.StartWritingElement(kConItem);
+ writer.WriteAttributeNameAndValue(kItemSemantic, kSemanticGainMap);
+ writer.WriteAttributeNameAndValue(kItemMime, kMimeImageJpeg);
+ writer.WriteAttributeNameAndValue(kItemLength, secondary_image_length);
+
+ writer.FinishWriting();
+
+ return ss.str();
+}
+
+string generateXmpForSecondaryImage(ultrahdr_metadata_struct& metadata) {
+ const vector<string> kConDirSeq({kConDirectory, string("rdf:Seq")});
+
+ std::stringstream ss;
+ photos_editing_formats::image_io::XmlWriter writer(ss);
+ writer.StartWritingElement("x:xmpmeta");
+ writer.WriteXmlns("x", "adobe:ns:meta/");
+ writer.WriteAttributeNameAndValue("x:xmptk", "Adobe XMP Core 5.1.2");
+ writer.StartWritingElement("rdf:RDF");
+ writer.WriteXmlns("rdf", "http://www.w3.org/1999/02/22-rdf-syntax-ns#");
+ writer.StartWritingElement("rdf:Description");
+ writer.WriteXmlns(kGainMapPrefix, kGainMapUri);
+ writer.WriteAttributeNameAndValue(kMapVersion, metadata.version);
+ writer.WriteAttributeNameAndValue(kMapGainMapMin, log2(metadata.minContentBoost));
+ writer.WriteAttributeNameAndValue(kMapGainMapMax, log2(metadata.maxContentBoost));
+ writer.WriteAttributeNameAndValue(kMapGamma, metadata.gamma);
+ writer.WriteAttributeNameAndValue(kMapOffsetSdr, metadata.offsetSdr);
+ writer.WriteAttributeNameAndValue(kMapOffsetHdr, metadata.offsetHdr);
+ writer.WriteAttributeNameAndValue(kMapHDRCapacityMin, log2(metadata.hdrCapacityMin));
+ writer.WriteAttributeNameAndValue(kMapHDRCapacityMax, log2(metadata.hdrCapacityMax));
+ writer.WriteAttributeNameAndValue(kMapBaseRenditionIsHDR, "False");
+ writer.FinishWriting();
+
+ return ss.str();
+}
+
+} // namespace ultrahdr
diff --git a/include/ultrahdr/jpegrutils.h b/lib/jpegrutils.h
index cdba834..c3fa8ab 100644
--- a/include/ultrahdr/jpegrutils.h
+++ b/lib/jpegrutils.h
@@ -17,19 +17,17 @@
#ifndef ULTRAHDR_JPEGRUTILS_H
#define ULTRAHDR_JPEGRUTILS_H
-#include "ultrahdr/ultrahdr.h"
-#include "ultrahdr/jpegr.h"
+#include "ultrahdr.h"
+#include "jpegr.h"
namespace ultrahdr {
static constexpr uint32_t EndianSwap32(uint32_t value) {
- return ((value & 0xFF) << 24) |
- ((value & 0xFF00) << 8) |
- ((value & 0xFF0000) >> 8) |
- (value >> 24);
+ return ((value & 0xFF) << 24) | ((value & 0xFF00) << 8) | ((value & 0xFF0000) >> 8) |
+ (value >> 24);
}
static inline uint16_t EndianSwap16(uint16_t value) {
- return static_cast<uint16_t>((value >> 8) | ((value & 0xFF) << 8));
+ return static_cast<uint16_t>((value >> 8) | ((value & 0xFF) << 8));
}
struct ultrahdr_metadata_struct;
@@ -37,22 +35,22 @@ struct ultrahdr_metadata_struct;
* Mutable data structure. Holds information for metadata.
*/
class DataStruct {
-private:
- void* data;
- int writePos;
- int length;
+ private:
+ void* data;
+ int writePos;
+ int length;
-public:
- DataStruct(int s);
- ~DataStruct();
+ public:
+ DataStruct(int s);
+ ~DataStruct();
- void* getData();
- int getLength();
- int getBytesWritten();
- bool write8(uint8_t value);
- bool write16(uint16_t value);
- bool write32(uint32_t value);
- bool write(const void* src, int size);
+ void* getData();
+ int getLength();
+ int getBytesWritten();
+ bool write8(uint8_t value);
+ bool write16(uint16_t value);
+ bool write32(uint32_t value);
+ bool write(const void* src, int size);
};
/*
@@ -64,8 +62,7 @@ public:
* @param position cursor in desitination where the data is to be written.
* @return status of succeed or error code.
*/
-status_t Write(jr_compressed_ptr destination, const void* source, size_t length, int &position);
-
+status_t Write(jr_compressed_ptr destination, const void* source, size_t length, int& position);
/*
* Parses XMP packet and fills metadata with data from XMP
@@ -74,7 +71,7 @@ status_t Write(jr_compressed_ptr destination, const void* source, size_t length,
* @param xmp_size size of XMP packet
* @param metadata place to store HDR metadata values
* @return true if metadata is successfully retrieved, false otherwise
-*/
+ */
bool getMetadataFromXMP(uint8_t* xmp_data, size_t xmp_size, ultrahdr_metadata_struct* metadata);
/*
@@ -149,7 +146,7 @@ std::string generateXmpForPrimaryImage(int secondary_image_length,
* @param metadata JPEG/R metadata to encode as XMP
* @return XMP metadata in type of string
*/
- std::string generateXmpForSecondaryImage(ultrahdr_metadata_struct& metadata);
+std::string generateXmpForSecondaryImage(ultrahdr_metadata_struct& metadata);
} // namespace ultrahdr
-#endif //ULTRAHDR_JPEGRUTILS_H
+#endif // ULTRAHDR_JPEGRUTILS_H
diff --git a/lib/multipictureformat.cpp b/lib/multipictureformat.cpp
new file mode 100644
index 0000000..5e38175
--- /dev/null
+++ b/lib/multipictureformat.cpp
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * 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.
+ */
+
+#include "multipictureformat.h"
+
+namespace ultrahdr {
+size_t calculateMpfSize() {
+ return sizeof(kMpfSig) + // Signature
+ kMpEndianSize + // Endianness
+ sizeof(uint32_t) + // Index IFD Offset
+ sizeof(uint16_t) + // Tag count
+ kTagSerializedCount * kTagSize + // 3 tags at 12 bytes each
+ sizeof(uint32_t) + // Attribute IFD offset
+ kNumPictures * kMPEntrySize; // MP Entries for each image
+}
+
+std::shared_ptr<DataStruct> generateMpf(int primary_image_size, int primary_image_offset,
+ int secondary_image_size, int secondary_image_offset) {
+ size_t mpf_size = calculateMpfSize();
+ std::shared_ptr<DataStruct> dataStruct = std::make_shared<DataStruct>(mpf_size);
+
+ dataStruct->write(static_cast<const void*>(kMpfSig), sizeof(kMpfSig));
+#if USE_BIG_ENDIAN
+ dataStruct->write(static_cast<const void*>(kMpBigEndian), kMpEndianSize);
+#else
+ dataStruct->write(static_cast<const void*>(kMpLittleEndian), kMpEndianSize);
+#endif
+
+ // Set the Index IFD offset be the position after the endianness value and this offset.
+ constexpr uint32_t indexIfdOffset = static_cast<uint16_t>(kMpEndianSize + sizeof(kMpfSig));
+ dataStruct->write32(Endian_SwapBE32(indexIfdOffset));
+
+ // We will write 3 tags (version, number of images, MP entries).
+ dataStruct->write16(Endian_SwapBE16(kTagSerializedCount));
+
+ // Write the version tag.
+ dataStruct->write16(Endian_SwapBE16(kVersionTag));
+ dataStruct->write16(Endian_SwapBE16(kVersionType));
+ dataStruct->write32(Endian_SwapBE32(kVersionCount));
+ dataStruct->write(kVersionExpected, kVersionSize);
+
+ // Write the number of images.
+ dataStruct->write16(Endian_SwapBE16(kNumberOfImagesTag));
+ dataStruct->write16(Endian_SwapBE16(kNumberOfImagesType));
+ dataStruct->write32(Endian_SwapBE32(kNumberOfImagesCount));
+ dataStruct->write32(Endian_SwapBE32(kNumPictures));
+
+ // Write the MP entries.
+ dataStruct->write16(Endian_SwapBE16(kMPEntryTag));
+ dataStruct->write16(Endian_SwapBE16(kMPEntryType));
+ dataStruct->write32(Endian_SwapBE32(kMPEntrySize * kNumPictures));
+ const uint32_t mpEntryOffset =
+ static_cast<uint32_t>(dataStruct->getBytesWritten() - // The bytes written so far
+ sizeof(kMpfSig) + // Excluding the MPF signature
+ sizeof(uint32_t) + // The 4 bytes for this offset
+ sizeof(uint32_t)); // The 4 bytes for the attribute IFD offset.
+ dataStruct->write32(Endian_SwapBE32(mpEntryOffset));
+
+ // Write the attribute IFD offset (zero because we don't write it).
+ dataStruct->write32(0);
+
+ // Write the MP entries for primary image
+ dataStruct->write32(Endian_SwapBE32(kMPEntryAttributeFormatJpeg | kMPEntryAttributeTypePrimary));
+ dataStruct->write32(Endian_SwapBE32(primary_image_size));
+ dataStruct->write32(Endian_SwapBE32(primary_image_offset));
+ dataStruct->write16(0);
+ dataStruct->write16(0);
+
+ // Write the MP entries for secondary image
+ dataStruct->write32(Endian_SwapBE32(kMPEntryAttributeFormatJpeg));
+ dataStruct->write32(Endian_SwapBE32(secondary_image_size));
+ dataStruct->write32(Endian_SwapBE32(secondary_image_offset));
+ dataStruct->write16(0);
+ dataStruct->write16(0);
+
+ return dataStruct;
+}
+
+} // namespace ultrahdr
diff --git a/include/ultrahdr/multipictureformat.h b/lib/multipictureformat.h
index 79c494f..06ad6ae 100644
--- a/include/ultrahdr/multipictureformat.h
+++ b/lib/multipictureformat.h
@@ -24,17 +24,17 @@
#endif
#if USE_BIG_ENDIAN_IN_MPF
- #define Endian_SwapBE32(n) EndianSwap32(n)
- #define Endian_SwapBE16(n) EndianSwap16(n)
+#define Endian_SwapBE32(n) EndianSwap32(n)
+#define Endian_SwapBE16(n) EndianSwap16(n)
#else
- #define Endian_SwapBE32(n) (n)
- #define Endian_SwapBE16(n) (n)
+#define Endian_SwapBE32(n) (n)
+#define Endian_SwapBE16(n) (n)
#endif
-#include "ultrahdr/ultrahdr.h"
-#include "ultrahdr/jpegr.h"
-#include "ultrahdr/gainmapmath.h"
-#include "ultrahdr/jpegrutils.h"
+#include "ultrahdr.h"
+#include "jpegr.h"
+#include "gainmapmath.h"
+#include "jpegrutils.h"
namespace ultrahdr {
@@ -73,4 +73,4 @@ std::shared_ptr<DataStruct> generateMpf(int primary_image_size, int primary_imag
} // namespace ultrahdr
-#endif //ULTRAHDR_MULTIPICTUREFORMAT_H
+#endif // ULTRAHDR_MULTIPICTUREFORMAT_H
diff --git a/include/ultrahdr/ultrahdr.h b/lib/ultrahdr.h
index 22829ff..fa69d57 100644
--- a/include/ultrahdr/ultrahdr.h
+++ b/lib/ultrahdr.h
@@ -43,10 +43,10 @@ typedef enum {
// Target output formats for decoder
typedef enum {
ULTRAHDR_OUTPUT_UNSPECIFIED = -1,
- ULTRAHDR_OUTPUT_SDR, // SDR in RGBA_8888 color format
- ULTRAHDR_OUTPUT_HDR_LINEAR, // HDR in F16 color format (linear)
- ULTRAHDR_OUTPUT_HDR_PQ, // HDR in RGBA_1010102 color format (PQ transfer function)
- ULTRAHDR_OUTPUT_HDR_HLG, // HDR in RGBA_1010102 color format (HLG transfer function)
+ ULTRAHDR_OUTPUT_SDR, // SDR in RGBA_8888 color format
+ ULTRAHDR_OUTPUT_HDR_LINEAR, // HDR in F16 color format (linear)
+ ULTRAHDR_OUTPUT_HDR_PQ, // HDR in RGBA_1010102 color format (PQ transfer function)
+ ULTRAHDR_OUTPUT_HDR_HLG, // HDR in RGBA_1010102 color format (HLG transfer function)
ULTRAHDR_OUTPUT_MAX = ULTRAHDR_OUTPUT_HDR_HLG,
} ultrahdr_output_format;
@@ -79,4 +79,4 @@ typedef struct ultrahdr_metadata_struct* ultrahdr_metadata_ptr;
} // namespace ultrahdr
-#endif //ULTRAHDR_ULTRAHDR_H
+#endif // ULTRAHDR_ULTRAHDR_H
diff --git a/lib/ultrahdrcommon.h b/lib/ultrahdrcommon.h
new file mode 100644
index 0000000..ba3a3b8
--- /dev/null
+++ b/lib/ultrahdrcommon.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * 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.
+ */
+
+#ifndef ULTRAHDR_ULTRAHDRCOMMON_H
+#define ULTRAHDR_ULTRAHDRCOMMON_H
+
+//#define LOG_NDEBUG 0
+
+#ifdef __ANDROID__
+#include "log/log.h"
+#else
+#ifdef LOG_NDEBUG
+#include <cstdio>
+
+#define ALOGD(...) \
+ do { \
+ fprintf(stderr, __VA_ARGS__); \
+ fprintf(stderr, "\n"); \
+ } while (0)
+#define ALOGE(...) \
+ do { \
+ fprintf(stderr, __VA_ARGS__); \
+ fprintf(stderr, "\n"); \
+ } while (0)
+#define ALOGI(...) \
+ do { \
+ fprintf(stdout, __VA_ARGS__); \
+ fprintf(stdout, "\n"); \
+ } while (0)
+#define ALOGV(...) \
+ do { \
+ fprintf(stdout, __VA_ARGS__); \
+ fprintf(stdout, "\n"); \
+ } while (0)
+#define ALOGW(...) \
+ do { \
+ fprintf(stderr, __VA_ARGS__); \
+ fprintf(stderr, "\n"); \
+ } while (0)
+#else
+#define ALOGD(...) ((void)0)
+#define ALOGE(...) ((void)0)
+#define ALOGI(...) ((void)0)
+#define ALOGV(...) ((void)0)
+#define ALOGW(...) ((void)0)
+#endif
+#endif
+
+#define ALIGNM(x, m) ((((x) + ((m)-1)) / (m)) * (m))
+
+#endif // ULTRAHDR_ULTRAHDRCOMMON_H
diff --git a/multipictureformat.cpp b/multipictureformat.cpp
deleted file mode 100644
index 153c972..0000000
--- a/multipictureformat.cpp
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * Copyright 2023 The Android Open Source Project
- *
- * 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.
- */
-
-#include "ultrahdr/multipictureformat.h"
-
-namespace ultrahdr {
-size_t calculateMpfSize() {
- return sizeof(kMpfSig) + // Signature
- kMpEndianSize + // Endianness
- sizeof(uint32_t) + // Index IFD Offset
- sizeof(uint16_t) + // Tag count
- kTagSerializedCount * kTagSize + // 3 tags at 12 bytes each
- sizeof(uint32_t) + // Attribute IFD offset
- kNumPictures * kMPEntrySize; // MP Entries for each image
-}
-
-std::shared_ptr<DataStruct> generateMpf(int primary_image_size, int primary_image_offset,
- int secondary_image_size, int secondary_image_offset) {
- size_t mpf_size = calculateMpfSize();
- std::shared_ptr<DataStruct> dataStruct = std::make_shared<DataStruct>(mpf_size);
-
- dataStruct->write(static_cast<const void*>(kMpfSig), sizeof(kMpfSig));
-#if USE_BIG_ENDIAN
- dataStruct->write(static_cast<const void*>(kMpBigEndian), kMpEndianSize);
-#else
- dataStruct->write(static_cast<const void*>(kMpLittleEndian), kMpEndianSize);
-#endif
-
- // Set the Index IFD offset be the position after the endianness value and this offset.
- constexpr uint32_t indexIfdOffset =
- static_cast<uint16_t>(kMpEndianSize + sizeof(kMpfSig));
- dataStruct->write32(Endian_SwapBE32(indexIfdOffset));
-
- // We will write 3 tags (version, number of images, MP entries).
- dataStruct->write16(Endian_SwapBE16(kTagSerializedCount));
-
- // Write the version tag.
- dataStruct->write16(Endian_SwapBE16(kVersionTag));
- dataStruct->write16(Endian_SwapBE16(kVersionType));
- dataStruct->write32(Endian_SwapBE32(kVersionCount));
- dataStruct->write(kVersionExpected, kVersionSize);
-
- // Write the number of images.
- dataStruct->write16(Endian_SwapBE16(kNumberOfImagesTag));
- dataStruct->write16(Endian_SwapBE16(kNumberOfImagesType));
- dataStruct->write32(Endian_SwapBE32(kNumberOfImagesCount));
- dataStruct->write32(Endian_SwapBE32(kNumPictures));
-
- // Write the MP entries.
- dataStruct->write16(Endian_SwapBE16(kMPEntryTag));
- dataStruct->write16(Endian_SwapBE16(kMPEntryType));
- dataStruct->write32(Endian_SwapBE32(kMPEntrySize * kNumPictures));
- const uint32_t mpEntryOffset =
- static_cast<uint32_t>(dataStruct->getBytesWritten() - // The bytes written so far
- sizeof(kMpfSig) + // Excluding the MPF signature
- sizeof(uint32_t) + // The 4 bytes for this offset
- sizeof(uint32_t)); // The 4 bytes for the attribute IFD offset.
- dataStruct->write32(Endian_SwapBE32(mpEntryOffset));
-
- // Write the attribute IFD offset (zero because we don't write it).
- dataStruct->write32(0);
-
- // Write the MP entries for primary image
- dataStruct->write32(
- Endian_SwapBE32(kMPEntryAttributeFormatJpeg | kMPEntryAttributeTypePrimary));
- dataStruct->write32(Endian_SwapBE32(primary_image_size));
- dataStruct->write32(Endian_SwapBE32(primary_image_offset));
- dataStruct->write16(0);
- dataStruct->write16(0);
-
- // Write the MP entries for secondary image
- dataStruct->write32(Endian_SwapBE32(kMPEntryAttributeFormatJpeg));
- dataStruct->write32(Endian_SwapBE32(secondary_image_size));
- dataStruct->write32(Endian_SwapBE32(secondary_image_offset));
- dataStruct->write16(0);
- dataStruct->write16(0);
-
- return dataStruct;
-}
-
-} // namespace ultrahdr
diff --git a/tests/Android.bp b/tests/Android.bp
index e175155..8fe1621 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -22,27 +22,6 @@ package {
}
cc_test {
- name: "ultrahdr_sample_app",
- host_supported: true,
- gtest: false,
- test_suites: ["device-tests"],
- srcs: [
- "ultrahdr_app.cpp",
- ],
- shared_libs: [
- "libbase",
- "libimage_io",
- "libjpeg",
- "liblog",
- ],
- static_libs: [
- "libjpegdecoder",
- "libjpegencoder",
- "libultrahdr",
- ],
-}
-
-cc_test {
name: "ultrahdr_unit_test",
test_suites: ["device-tests"],
srcs: [
diff --git a/third_party/data/LICENSE b/tests/data/LICENSE
index 27dfefa..27dfefa 100644
--- a/third_party/data/LICENSE
+++ b/tests/data/LICENSE
diff --git a/third_party/data/jpeg_image.jpg b/tests/data/jpeg_image.jpg
index e285742..e285742 100644
--- a/third_party/data/jpeg_image.jpg
+++ b/tests/data/jpeg_image.jpg
Binary files differ
diff --git a/third_party/data/minnie-318x240.yu12 b/tests/data/minnie-318x240.yu12
index 7b2fc71..7b2fc71 100644
--- a/third_party/data/minnie-318x240.yu12
+++ b/tests/data/minnie-318x240.yu12
diff --git a/third_party/data/minnie-320x240-y.jpg b/tests/data/minnie-320x240-y.jpg
index 20b5a2c..20b5a2c 100644
--- a/third_party/data/minnie-320x240-y.jpg
+++ b/tests/data/minnie-320x240-y.jpg
Binary files differ
diff --git a/third_party/data/minnie-320x240-yuv-icc.jpg b/tests/data/minnie-320x240-yuv-icc.jpg
index c7f4538..c7f4538 100644
--- a/third_party/data/minnie-320x240-yuv-icc.jpg
+++ b/tests/data/minnie-320x240-yuv-icc.jpg
Binary files differ
diff --git a/third_party/data/minnie-320x240-yuv.jpg b/tests/data/minnie-320x240-yuv.jpg
index 41300f4..41300f4 100644
--- a/third_party/data/minnie-320x240-yuv.jpg
+++ b/tests/data/minnie-320x240-yuv.jpg
Binary files differ
diff --git a/third_party/data/minnie-320x240.y b/tests/data/minnie-320x240.y
index f9d8371..f9d8371 100644
--- a/third_party/data/minnie-320x240.y
+++ b/tests/data/minnie-320x240.y
diff --git a/third_party/data/minnie-320x240.yu12 b/tests/data/minnie-320x240.yu12
index 0d66f53..0d66f53 100644
--- a/third_party/data/minnie-320x240.yu12
+++ b/tests/data/minnie-320x240.yu12
diff --git a/third_party/data/raw_p010_image.p010 b/tests/data/raw_p010_image.p010
index 01673bf..01673bf 100644
--- a/third_party/data/raw_p010_image.p010
+++ b/tests/data/raw_p010_image.p010
Binary files differ
diff --git a/third_party/data/raw_yuv420_image.yuv420 b/tests/data/raw_yuv420_image.yuv420
index c043da6..c043da6 100644
--- a/third_party/data/raw_yuv420_image.yuv420
+++ b/tests/data/raw_yuv420_image.yuv420
diff --git a/tests/gainmapmath_test.cpp b/tests/gainmapmath_test.cpp
index e73419c..b474bd6 100644
--- a/tests/gainmapmath_test.cpp
+++ b/tests/gainmapmath_test.cpp
@@ -17,12 +17,12 @@
#include <gtest/gtest.h>
#include <gmock/gmock.h>
-#include "ultrahdr/gainmapmath.h"
+#include "gainmapmath.h"
namespace ultrahdr {
class GainMapMathTest : public testing::Test {
-public:
+ public:
GainMapMathTest();
~GainMapMathTest();
@@ -31,50 +31,47 @@ public:
float YuvConversionEpsilon() { return 1.0f / (255.0f * 2.0f); }
Color Yuv420(uint8_t y, uint8_t u, uint8_t v) {
- return {{{ static_cast<float>(y) / 255.0f,
- (static_cast<float>(u) - 128.0f) / 255.0f,
- (static_cast<float>(v) - 128.0f) / 255.0f }}};
+ return {{{static_cast<float>(y) / 255.0f, (static_cast<float>(u) - 128.0f) / 255.0f,
+ (static_cast<float>(v) - 128.0f) / 255.0f}}};
}
Color P010(uint16_t y, uint16_t u, uint16_t v) {
- return {{{ (static_cast<float>(y) - 64.0f) / 876.0f,
- (static_cast<float>(u) - 64.0f) / 896.0f - 0.5f,
- (static_cast<float>(v) - 64.0f) / 896.0f - 0.5f }}};
+ return {
+ {{(static_cast<float>(y) - 64.0f) / 876.0f, (static_cast<float>(u) - 64.0f) / 896.0f - 0.5f,
+ (static_cast<float>(v) - 64.0f) / 896.0f - 0.5f}}};
}
- float Map(uint8_t e) {
- return static_cast<float>(e) / 255.0f;
- }
+ float Map(uint8_t e) { return static_cast<float>(e) / 255.0f; }
Color ColorMin(Color e1, Color e2) {
- return {{{ fminf(e1.r, e2.r), fminf(e1.g, e2.g), fminf(e1.b, e2.b) }}};
+ return {{{fminf(e1.r, e2.r), fminf(e1.g, e2.g), fminf(e1.b, e2.b)}}};
}
Color ColorMax(Color e1, Color e2) {
- return {{{ fmaxf(e1.r, e2.r), fmaxf(e1.g, e2.g), fmaxf(e1.b, e2.b) }}};
+ return {{{fmaxf(e1.r, e2.r), fmaxf(e1.g, e2.g), fmaxf(e1.b, e2.b)}}};
}
- Color RgbBlack() { return {{{ 0.0f, 0.0f, 0.0f }}}; }
- Color RgbWhite() { return {{{ 1.0f, 1.0f, 1.0f }}}; }
+ Color RgbBlack() { return {{{0.0f, 0.0f, 0.0f}}}; }
+ Color RgbWhite() { return {{{1.0f, 1.0f, 1.0f}}}; }
- Color RgbRed() { return {{{ 1.0f, 0.0f, 0.0f }}}; }
- Color RgbGreen() { return {{{ 0.0f, 1.0f, 0.0f }}}; }
- Color RgbBlue() { return {{{ 0.0f, 0.0f, 1.0f }}}; }
+ Color RgbRed() { return {{{1.0f, 0.0f, 0.0f}}}; }
+ Color RgbGreen() { return {{{0.0f, 1.0f, 0.0f}}}; }
+ Color RgbBlue() { return {{{0.0f, 0.0f, 1.0f}}}; }
- Color YuvBlack() { return {{{ 0.0f, 0.0f, 0.0f }}}; }
- Color YuvWhite() { return {{{ 1.0f, 0.0f, 0.0f }}}; }
+ Color YuvBlack() { return {{{0.0f, 0.0f, 0.0f}}}; }
+ Color YuvWhite() { return {{{1.0f, 0.0f, 0.0f}}}; }
- Color SrgbYuvRed() { return {{{ 0.2126f, -0.11457f, 0.5f }}}; }
- Color SrgbYuvGreen() { return {{{ 0.7152f, -0.38543f, -0.45415f }}}; }
- Color SrgbYuvBlue() { return {{{ 0.0722f, 0.5f, -0.04585f }}}; }
+ Color SrgbYuvRed() { return {{{0.2126f, -0.11457f, 0.5f}}}; }
+ Color SrgbYuvGreen() { return {{{0.7152f, -0.38543f, -0.45415f}}}; }
+ Color SrgbYuvBlue() { return {{{0.0722f, 0.5f, -0.04585f}}}; }
- Color P3YuvRed() { return {{{ 0.299f, -0.16874f, 0.5f }}}; }
- Color P3YuvGreen() { return {{{ 0.587f, -0.33126f, -0.41869f }}}; }
- Color P3YuvBlue() { return {{{ 0.114f, 0.5f, -0.08131f }}}; }
+ Color P3YuvRed() { return {{{0.299f, -0.16874f, 0.5f}}}; }
+ Color P3YuvGreen() { return {{{0.587f, -0.33126f, -0.41869f}}}; }
+ Color P3YuvBlue() { return {{{0.114f, 0.5f, -0.08131f}}}; }
- Color Bt2100YuvRed() { return {{{ 0.2627f, -0.13963f, 0.5f }}}; }
- Color Bt2100YuvGreen() { return {{{ 0.6780f, -0.36037f, -0.45979f }}}; }
- Color Bt2100YuvBlue() { return {{{ 0.0593f, 0.5f, -0.04021f }}}; }
+ Color Bt2100YuvRed() { return {{{0.2627f, -0.13963f, 0.5f}}}; }
+ Color Bt2100YuvGreen() { return {{{0.6780f, -0.36037f, -0.45979f}}}; }
+ Color Bt2100YuvBlue() { return {{{0.0593f, 0.5f, -0.04021f}}}; }
float SrgbYuvToLuminance(Color yuv_gamma, ColorCalculationFn luminanceFn) {
Color rgb_gamma = srgbYuvToRgb(yuv_gamma);
@@ -108,99 +105,168 @@ public:
jpegr_uncompressed_struct Yuv420Image() {
static uint8_t pixels[] = {
- // Y
- 0x00, 0x10, 0x20, 0x30,
- 0x01, 0x11, 0x21, 0x31,
- 0x02, 0x12, 0x22, 0x32,
- 0x03, 0x13, 0x23, 0x33,
- // U
- 0xA0, 0xA1,
- 0xA2, 0xA3,
- // V
- 0xB0, 0xB1,
- 0xB2, 0xB3,
+ // Y
+ 0x00,
+ 0x10,
+ 0x20,
+ 0x30,
+ 0x01,
+ 0x11,
+ 0x21,
+ 0x31,
+ 0x02,
+ 0x12,
+ 0x22,
+ 0x32,
+ 0x03,
+ 0x13,
+ 0x23,
+ 0x33,
+ // U
+ 0xA0,
+ 0xA1,
+ 0xA2,
+ 0xA3,
+ // V
+ 0xB0,
+ 0xB1,
+ 0xB2,
+ 0xB3,
};
- return { pixels, 4, 4, ULTRAHDR_COLORGAMUT_BT709, pixels + 16, 4, 2 };
+ return {pixels, 4, 4, ULTRAHDR_COLORGAMUT_BT709, pixels + 16, 4, 2};
}
Color (*Yuv420Colors())[4] {
static Color colors[4][4] = {
- {
- Yuv420(0x00, 0xA0, 0xB0), Yuv420(0x10, 0xA0, 0xB0),
- Yuv420(0x20, 0xA1, 0xB1), Yuv420(0x30, 0xA1, 0xB1),
- }, {
- Yuv420(0x01, 0xA0, 0xB0), Yuv420(0x11, 0xA0, 0xB0),
- Yuv420(0x21, 0xA1, 0xB1), Yuv420(0x31, 0xA1, 0xB1),
- }, {
- Yuv420(0x02, 0xA2, 0xB2), Yuv420(0x12, 0xA2, 0xB2),
- Yuv420(0x22, 0xA3, 0xB3), Yuv420(0x32, 0xA3, 0xB3),
- }, {
- Yuv420(0x03, 0xA2, 0xB2), Yuv420(0x13, 0xA2, 0xB2),
- Yuv420(0x23, 0xA3, 0xB3), Yuv420(0x33, 0xA3, 0xB3),
- },
+ {
+ Yuv420(0x00, 0xA0, 0xB0),
+ Yuv420(0x10, 0xA0, 0xB0),
+ Yuv420(0x20, 0xA1, 0xB1),
+ Yuv420(0x30, 0xA1, 0xB1),
+ },
+ {
+ Yuv420(0x01, 0xA0, 0xB0),
+ Yuv420(0x11, 0xA0, 0xB0),
+ Yuv420(0x21, 0xA1, 0xB1),
+ Yuv420(0x31, 0xA1, 0xB1),
+ },
+ {
+ Yuv420(0x02, 0xA2, 0xB2),
+ Yuv420(0x12, 0xA2, 0xB2),
+ Yuv420(0x22, 0xA3, 0xB3),
+ Yuv420(0x32, 0xA3, 0xB3),
+ },
+ {
+ Yuv420(0x03, 0xA2, 0xB2),
+ Yuv420(0x13, 0xA2, 0xB2),
+ Yuv420(0x23, 0xA3, 0xB3),
+ Yuv420(0x33, 0xA3, 0xB3),
+ },
};
return colors;
}
jpegr_uncompressed_struct P010Image() {
static uint16_t pixels[] = {
- // Y
- 0x00 << 6, 0x10 << 6, 0x20 << 6, 0x30 << 6,
- 0x01 << 6, 0x11 << 6, 0x21 << 6, 0x31 << 6,
- 0x02 << 6, 0x12 << 6, 0x22 << 6, 0x32 << 6,
- 0x03 << 6, 0x13 << 6, 0x23 << 6, 0x33 << 6,
- // UV
- 0xA0 << 6, 0xB0 << 6, 0xA1 << 6, 0xB1 << 6,
- 0xA2 << 6, 0xB2 << 6, 0xA3 << 6, 0xB3 << 6,
+ // Y
+ 0x00 << 6,
+ 0x10 << 6,
+ 0x20 << 6,
+ 0x30 << 6,
+ 0x01 << 6,
+ 0x11 << 6,
+ 0x21 << 6,
+ 0x31 << 6,
+ 0x02 << 6,
+ 0x12 << 6,
+ 0x22 << 6,
+ 0x32 << 6,
+ 0x03 << 6,
+ 0x13 << 6,
+ 0x23 << 6,
+ 0x33 << 6,
+ // UV
+ 0xA0 << 6,
+ 0xB0 << 6,
+ 0xA1 << 6,
+ 0xB1 << 6,
+ 0xA2 << 6,
+ 0xB2 << 6,
+ 0xA3 << 6,
+ 0xB3 << 6,
};
- return { pixels, 4, 4, ULTRAHDR_COLORGAMUT_BT709, pixels + 16, 4, 4 };
+ return {pixels, 4, 4, ULTRAHDR_COLORGAMUT_BT709, pixels + 16, 4, 4};
}
Color (*P010Colors())[4] {
static Color colors[4][4] = {
- {
- P010(0x00, 0xA0, 0xB0), P010(0x10, 0xA0, 0xB0),
- P010(0x20, 0xA1, 0xB1), P010(0x30, 0xA1, 0xB1),
- }, {
- P010(0x01, 0xA0, 0xB0), P010(0x11, 0xA0, 0xB0),
- P010(0x21, 0xA1, 0xB1), P010(0x31, 0xA1, 0xB1),
- }, {
- P010(0x02, 0xA2, 0xB2), P010(0x12, 0xA2, 0xB2),
- P010(0x22, 0xA3, 0xB3), P010(0x32, 0xA3, 0xB3),
- }, {
- P010(0x03, 0xA2, 0xB2), P010(0x13, 0xA2, 0xB2),
- P010(0x23, 0xA3, 0xB3), P010(0x33, 0xA3, 0xB3),
- },
+ {
+ P010(0x00, 0xA0, 0xB0),
+ P010(0x10, 0xA0, 0xB0),
+ P010(0x20, 0xA1, 0xB1),
+ P010(0x30, 0xA1, 0xB1),
+ },
+ {
+ P010(0x01, 0xA0, 0xB0),
+ P010(0x11, 0xA0, 0xB0),
+ P010(0x21, 0xA1, 0xB1),
+ P010(0x31, 0xA1, 0xB1),
+ },
+ {
+ P010(0x02, 0xA2, 0xB2),
+ P010(0x12, 0xA2, 0xB2),
+ P010(0x22, 0xA3, 0xB3),
+ P010(0x32, 0xA3, 0xB3),
+ },
+ {
+ P010(0x03, 0xA2, 0xB2),
+ P010(0x13, 0xA2, 0xB2),
+ P010(0x23, 0xA3, 0xB3),
+ P010(0x33, 0xA3, 0xB3),
+ },
};
return colors;
}
jpegr_uncompressed_struct MapImage() {
static uint8_t pixels[] = {
- 0x00, 0x10, 0x20, 0x30,
- 0x01, 0x11, 0x21, 0x31,
- 0x02, 0x12, 0x22, 0x32,
- 0x03, 0x13, 0x23, 0x33,
+ 0x00, 0x10, 0x20, 0x30, 0x01, 0x11, 0x21, 0x31,
+ 0x02, 0x12, 0x22, 0x32, 0x03, 0x13, 0x23, 0x33,
};
- return { pixels, 4, 4, ULTRAHDR_COLORGAMUT_UNSPECIFIED };
+ return {pixels, 4, 4, ULTRAHDR_COLORGAMUT_UNSPECIFIED};
}
float (*MapValues())[4] {
static float values[4][4] = {
- {
- Map(0x00), Map(0x10), Map(0x20), Map(0x30),
- }, {
- Map(0x01), Map(0x11), Map(0x21), Map(0x31),
- }, {
- Map(0x02), Map(0x12), Map(0x22), Map(0x32),
- }, {
- Map(0x03), Map(0x13), Map(0x23), Map(0x33),
- },
+ {
+ Map(0x00),
+ Map(0x10),
+ Map(0x20),
+ Map(0x30),
+ },
+ {
+ Map(0x01),
+ Map(0x11),
+ Map(0x21),
+ Map(0x31),
+ },
+ {
+ Map(0x02),
+ Map(0x12),
+ Map(0x22),
+ Map(0x32),
+ },
+ {
+ Map(0x03),
+ Map(0x13),
+ Map(0x23),
+ Map(0x33),
+ },
};
return values;
}
-protected:
+ protected:
virtual void SetUp();
virtual void TearDown();
};
@@ -211,9 +277,9 @@ GainMapMathTest::~GainMapMathTest() {}
void GainMapMathTest::SetUp() {}
void GainMapMathTest::TearDown() {}
-#define EXPECT_RGB_EQ(e1, e2) \
- EXPECT_FLOAT_EQ((e1).r, (e2).r); \
- EXPECT_FLOAT_EQ((e1).g, (e2).g); \
+#define EXPECT_RGB_EQ(e1, e2) \
+ EXPECT_FLOAT_EQ((e1).r, (e2).r); \
+ EXPECT_FLOAT_EQ((e1).g, (e2).g); \
EXPECT_FLOAT_EQ((e1).b, (e2).b)
#define EXPECT_RGB_NEAR(e1, e2) \
@@ -226,9 +292,9 @@ void GainMapMathTest::TearDown() {}
EXPECT_NEAR((e1).g, (e2).g, ComparisonEpsilon() * 10.0f); \
EXPECT_NEAR((e1).b, (e2).b, ComparisonEpsilon() * 10.0f)
-#define EXPECT_YUV_EQ(e1, e2) \
- EXPECT_FLOAT_EQ((e1).y, (e2).y); \
- EXPECT_FLOAT_EQ((e1).u, (e2).u); \
+#define EXPECT_YUV_EQ(e1, e2) \
+ EXPECT_FLOAT_EQ((e1).y, (e2).y); \
+ EXPECT_FLOAT_EQ((e1).u, (e2).u); \
EXPECT_FLOAT_EQ((e1).v, (e2).v)
#define EXPECT_YUV_NEAR(e1, e2) \
@@ -244,7 +310,7 @@ void GainMapMathTest::TearDown() {}
// TODO: a bunch of these tests can be parameterized.
TEST_F(GainMapMathTest, ColorConstruct) {
- Color e1 = {{{ 0.1f, 0.2f, 0.3f }}};
+ Color e1 = {{{0.1f, 0.2f, 0.3f}}};
EXPECT_FLOAT_EQ(e1.r, 0.1f);
EXPECT_FLOAT_EQ(e1.g, 0.2f);
@@ -256,7 +322,7 @@ TEST_F(GainMapMathTest, ColorConstruct) {
}
TEST_F(GainMapMathTest, ColorAddColor) {
- Color e1 = {{{ 0.1f, 0.2f, 0.3f }}};
+ Color e1 = {{{0.1f, 0.2f, 0.3f}}};
Color e2 = e1 + e1;
EXPECT_FLOAT_EQ(e2.r, e1.r * 2.0f);
@@ -270,7 +336,7 @@ TEST_F(GainMapMathTest, ColorAddColor) {
}
TEST_F(GainMapMathTest, ColorAddFloat) {
- Color e1 = {{{ 0.1f, 0.2f, 0.3f }}};
+ Color e1 = {{{0.1f, 0.2f, 0.3f}}};
Color e2 = e1 + 0.1f;
EXPECT_FLOAT_EQ(e2.r, e1.r + 0.1f);
@@ -284,7 +350,7 @@ TEST_F(GainMapMathTest, ColorAddFloat) {
}
TEST_F(GainMapMathTest, ColorSubtractColor) {
- Color e1 = {{{ 0.1f, 0.2f, 0.3f }}};
+ Color e1 = {{{0.1f, 0.2f, 0.3f}}};
Color e2 = e1 - e1;
EXPECT_FLOAT_EQ(e2.r, 0.0f);
@@ -298,7 +364,7 @@ TEST_F(GainMapMathTest, ColorSubtractColor) {
}
TEST_F(GainMapMathTest, ColorSubtractFloat) {
- Color e1 = {{{ 0.1f, 0.2f, 0.3f }}};
+ Color e1 = {{{0.1f, 0.2f, 0.3f}}};
Color e2 = e1 - 0.1f;
EXPECT_FLOAT_EQ(e2.r, e1.r - 0.1f);
@@ -312,7 +378,7 @@ TEST_F(GainMapMathTest, ColorSubtractFloat) {
}
TEST_F(GainMapMathTest, ColorMultiplyFloat) {
- Color e1 = {{{ 0.1f, 0.2f, 0.3f }}};
+ Color e1 = {{{0.1f, 0.2f, 0.3f}}};
Color e2 = e1 * 2.0f;
EXPECT_FLOAT_EQ(e2.r, e1.r * 2.0f);
@@ -326,7 +392,7 @@ TEST_F(GainMapMathTest, ColorMultiplyFloat) {
}
TEST_F(GainMapMathTest, ColorDivideFloat) {
- Color e1 = {{{ 0.1f, 0.2f, 0.3f }}};
+ Color e1 = {{{0.1f, 0.2f, 0.3f}}};
Color e2 = e1 / 2.0f;
EXPECT_FLOAT_EQ(e2.r, e1.r / 2.0f);
@@ -626,8 +692,8 @@ TEST_F(GainMapMathTest, Bt2100ToBt601YuvConversion) {
}
TEST_F(GainMapMathTest, TransformYuv420) {
- ColorTransformFn transforms[] = { yuv709To601, yuv709To2100, yuv601To709, yuv601To2100,
- yuv2100To709, yuv2100To601 };
+ ColorTransformFn transforms[] = {yuv709To601, yuv709To2100, yuv601To709,
+ yuv601To2100, yuv2100To709, yuv2100To601};
for (const ColorTransformFn& transform : transforms) {
jpegr_uncompressed_struct input = Yuv420Image();
@@ -655,10 +721,10 @@ TEST_F(GainMapMathTest, TransformYuv420) {
}
// modified pixels should be updated as intended by the transformYuv420 algorithm
- Color in1 = getYuv420Pixel(&input, 2, 2);
- Color in2 = getYuv420Pixel(&input, 3, 2);
- Color in3 = getYuv420Pixel(&input, 2, 3);
- Color in4 = getYuv420Pixel(&input, 3, 3);
+ Color in1 = getYuv420Pixel(&input, 2, 2);
+ Color in2 = getYuv420Pixel(&input, 3, 2);
+ Color in3 = getYuv420Pixel(&input, 2, 3);
+ Color in4 = getYuv420Pixel(&input, 3, 3);
Color out1 = getYuv420Pixel(&output, 2, 2);
Color out2 = getYuv420Pixel(&output, 3, 2);
Color out3 = getYuv420Pixel(&output, 2, 3);
@@ -690,8 +756,8 @@ TEST_F(GainMapMathTest, HlgOetf) {
EXPECT_NEAR(hlgOetf(0.5f), 0.87164f, ComparisonEpsilon());
EXPECT_FLOAT_EQ(hlgOetf(1.0f), 1.0f);
- Color e = {{{ 0.04167f, 0.08333f, 0.5f }}};
- Color e_gamma = {{{ 0.35357f, 0.5f, 0.87164f }}};
+ Color e = {{{0.04167f, 0.08333f, 0.5f}}};
+ Color e_gamma = {{{0.35357f, 0.5f, 0.87164f}}};
EXPECT_RGB_NEAR(hlgOetf(e), e_gamma);
}
@@ -702,8 +768,8 @@ TEST_F(GainMapMathTest, HlgInvOetf) {
EXPECT_NEAR(hlgInvOetf(0.75f), 0.26496f, ComparisonEpsilon());
EXPECT_FLOAT_EQ(hlgInvOetf(1.0f), 1.0f);
- Color e_gamma = {{{ 0.25f, 0.5f, 0.75f }}};
- Color e = {{{ 0.02083f, 0.08333f, 0.26496f }}};
+ Color e_gamma = {{{0.25f, 0.5f, 0.75f}}};
+ Color e = {{{0.02083f, 0.08333f, 0.26496f}}};
EXPECT_RGB_NEAR(hlgInvOetf(e_gamma), e);
}
@@ -722,8 +788,8 @@ TEST_F(GainMapMathTest, PqOetf) {
EXPECT_NEAR(pqOetf(0.99f), 0.99895f, ComparisonEpsilon());
EXPECT_FLOAT_EQ(pqOetf(1.0f), 1.0f);
- Color e = {{{ 0.01f, 0.5f, 0.99f }}};
- Color e_gamma = {{{ 0.50808f, 0.92655f, 0.99895f }}};
+ Color e = {{{0.01f, 0.5f, 0.99f}}};
+ Color e_gamma = {{{0.50808f, 0.92655f, 0.99895f}}};
EXPECT_RGB_NEAR(pqOetf(e), e_gamma);
}
@@ -734,50 +800,52 @@ TEST_F(GainMapMathTest, PqInvOetf) {
EXPECT_NEAR(pqInvOetf(0.99f), 0.90903f, ComparisonEpsilon());
EXPECT_FLOAT_EQ(pqInvOetf(1.0f), 1.0f);
- Color e_gamma = {{{ 0.01f, 0.5f, 0.99f }}};
- Color e = {{{ 2.31017e-7f, 0.00922f, 0.90903f }}};
+ Color e_gamma = {{{0.01f, 0.5f, 0.99f}}};
+ Color e = {{{2.31017e-7f, 0.00922f, 0.90903f}}};
EXPECT_RGB_NEAR(pqInvOetf(e_gamma), e);
}
TEST_F(GainMapMathTest, PqInvOetfLUT) {
- for (size_t idx = 0; idx < kPqInvOETFNumEntries; idx++) {
- float value = static_cast<float>(idx) / static_cast<float>(kPqInvOETFNumEntries - 1);
- EXPECT_FLOAT_EQ(pqInvOetf(value), pqInvOetfLUT(value));
- }
+ for (size_t idx = 0; idx < kPqInvOETFNumEntries; idx++) {
+ float value = static_cast<float>(idx) / static_cast<float>(kPqInvOETFNumEntries - 1);
+ EXPECT_FLOAT_EQ(pqInvOetf(value), pqInvOetfLUT(value));
+ }
}
TEST_F(GainMapMathTest, HlgInvOetfLUT) {
- for (size_t idx = 0; idx < kHlgInvOETFNumEntries; idx++) {
- float value = static_cast<float>(idx) / static_cast<float>(kHlgInvOETFNumEntries - 1);
- EXPECT_FLOAT_EQ(hlgInvOetf(value), hlgInvOetfLUT(value));
- }
+ for (size_t idx = 0; idx < kHlgInvOETFNumEntries; idx++) {
+ float value = static_cast<float>(idx) / static_cast<float>(kHlgInvOETFNumEntries - 1);
+ EXPECT_FLOAT_EQ(hlgInvOetf(value), hlgInvOetfLUT(value));
+ }
}
TEST_F(GainMapMathTest, pqOetfLUT) {
- for (size_t idx = 0; idx < kPqOETFNumEntries; idx++) {
- float value = static_cast<float>(idx) / static_cast<float>(kPqOETFNumEntries - 1);
- EXPECT_FLOAT_EQ(pqOetf(value), pqOetfLUT(value));
- }
+ for (size_t idx = 0; idx < kPqOETFNumEntries; idx++) {
+ float value = static_cast<float>(idx) / static_cast<float>(kPqOETFNumEntries - 1);
+ EXPECT_FLOAT_EQ(pqOetf(value), pqOetfLUT(value));
+ }
}
TEST_F(GainMapMathTest, hlgOetfLUT) {
- for (size_t idx = 0; idx < kHlgOETFNumEntries; idx++) {
- float value = static_cast<float>(idx) / static_cast<float>(kHlgOETFNumEntries - 1);
- EXPECT_FLOAT_EQ(hlgOetf(value), hlgOetfLUT(value));
- }
+ for (size_t idx = 0; idx < kHlgOETFNumEntries; idx++) {
+ float value = static_cast<float>(idx) / static_cast<float>(kHlgOETFNumEntries - 1);
+ EXPECT_FLOAT_EQ(hlgOetf(value), hlgOetfLUT(value));
+ }
}
TEST_F(GainMapMathTest, srgbInvOetfLUT) {
- for (size_t idx = 0; idx < kSrgbInvOETFNumEntries; idx++) {
- float value = static_cast<float>(idx) / static_cast<float>(kSrgbInvOETFNumEntries - 1);
- EXPECT_FLOAT_EQ(srgbInvOetf(value), srgbInvOetfLUT(value));
- }
+ for (size_t idx = 0; idx < kSrgbInvOETFNumEntries; idx++) {
+ float value = static_cast<float>(idx) / static_cast<float>(kSrgbInvOETFNumEntries - 1);
+ EXPECT_FLOAT_EQ(srgbInvOetf(value), srgbInvOetfLUT(value));
+ }
}
TEST_F(GainMapMathTest, applyGainLUT) {
for (int boost = 1; boost <= 10; boost++) {
- ultrahdr_metadata_struct metadata = { .maxContentBoost = static_cast<float>(boost),
- .minContentBoost = 1.0f / static_cast<float>(boost) };
+ ultrahdr_metadata_struct metadata;
+
+ metadata.minContentBoost = 1.0f / static_cast<float>(boost);
+ metadata.maxContentBoost = static_cast<float>(boost);
GainLUT gainLUT(&metadata);
GainLUT gainLUTWithBoost(&metadata, metadata.maxContentBoost);
for (size_t idx = 0; idx < kGainFactorNumEntries; idx++) {
@@ -806,8 +874,10 @@ TEST_F(GainMapMathTest, applyGainLUT) {
}
for (int boost = 1; boost <= 10; boost++) {
- ultrahdr_metadata_struct metadata = { .maxContentBoost = static_cast<float>(boost),
- .minContentBoost = 1.0f };
+ ultrahdr_metadata_struct metadata;
+
+ metadata.minContentBoost = 1.0f;
+ metadata.maxContentBoost = static_cast<float>(boost);
GainLUT gainLUT(&metadata);
GainLUT gainLUTWithBoost(&metadata, metadata.maxContentBoost);
for (size_t idx = 0; idx < kGainFactorNumEntries; idx++) {
@@ -836,9 +906,10 @@ TEST_F(GainMapMathTest, applyGainLUT) {
}
for (int boost = 1; boost <= 10; boost++) {
- ultrahdr_metadata_struct metadata = {.maxContentBoost = static_cast<float>(boost),
- .minContentBoost = 1.0f /
- powf(static_cast<float>(boost), 1.0f / 3.0f)};
+ ultrahdr_metadata_struct metadata;
+
+ metadata.minContentBoost = 1.0f / powf(static_cast<float>(boost), 1.0f / 3.0f);
+ metadata.maxContentBoost = static_cast<float>(boost);
GainLUT gainLUT(&metadata);
GainLUT gainLUTWithBoost(&metadata, metadata.maxContentBoost);
for (size_t idx = 0; idx < kGainFactorNumEntries; idx++) {
@@ -880,26 +951,20 @@ TEST_F(GainMapMathTest, ColorConversionLookup) {
nullptr);
EXPECT_EQ(getHdrConversionFn(ULTRAHDR_COLORGAMUT_BT709, ULTRAHDR_COLORGAMUT_BT709),
identityConversion);
- EXPECT_EQ(getHdrConversionFn(ULTRAHDR_COLORGAMUT_BT709, ULTRAHDR_COLORGAMUT_P3),
- p3ToBt709);
+ EXPECT_EQ(getHdrConversionFn(ULTRAHDR_COLORGAMUT_BT709, ULTRAHDR_COLORGAMUT_P3), p3ToBt709);
EXPECT_EQ(getHdrConversionFn(ULTRAHDR_COLORGAMUT_BT709, ULTRAHDR_COLORGAMUT_BT2100),
bt2100ToBt709);
- EXPECT_EQ(getHdrConversionFn(ULTRAHDR_COLORGAMUT_P3, ULTRAHDR_COLORGAMUT_UNSPECIFIED),
- nullptr);
- EXPECT_EQ(getHdrConversionFn(ULTRAHDR_COLORGAMUT_P3, ULTRAHDR_COLORGAMUT_BT709),
- bt709ToP3);
- EXPECT_EQ(getHdrConversionFn(ULTRAHDR_COLORGAMUT_P3, ULTRAHDR_COLORGAMUT_P3),
- identityConversion);
- EXPECT_EQ(getHdrConversionFn(ULTRAHDR_COLORGAMUT_P3, ULTRAHDR_COLORGAMUT_BT2100),
- bt2100ToP3);
+ EXPECT_EQ(getHdrConversionFn(ULTRAHDR_COLORGAMUT_P3, ULTRAHDR_COLORGAMUT_UNSPECIFIED), nullptr);
+ EXPECT_EQ(getHdrConversionFn(ULTRAHDR_COLORGAMUT_P3, ULTRAHDR_COLORGAMUT_BT709), bt709ToP3);
+ EXPECT_EQ(getHdrConversionFn(ULTRAHDR_COLORGAMUT_P3, ULTRAHDR_COLORGAMUT_P3), identityConversion);
+ EXPECT_EQ(getHdrConversionFn(ULTRAHDR_COLORGAMUT_P3, ULTRAHDR_COLORGAMUT_BT2100), bt2100ToP3);
EXPECT_EQ(getHdrConversionFn(ULTRAHDR_COLORGAMUT_BT2100, ULTRAHDR_COLORGAMUT_UNSPECIFIED),
nullptr);
EXPECT_EQ(getHdrConversionFn(ULTRAHDR_COLORGAMUT_BT2100, ULTRAHDR_COLORGAMUT_BT709),
bt709ToBt2100);
- EXPECT_EQ(getHdrConversionFn(ULTRAHDR_COLORGAMUT_BT2100, ULTRAHDR_COLORGAMUT_P3),
- p3ToBt2100);
+ EXPECT_EQ(getHdrConversionFn(ULTRAHDR_COLORGAMUT_BT2100, ULTRAHDR_COLORGAMUT_P3), p3ToBt2100);
EXPECT_EQ(getHdrConversionFn(ULTRAHDR_COLORGAMUT_BT2100, ULTRAHDR_COLORGAMUT_BT2100),
identityConversion);
@@ -907,15 +972,16 @@ TEST_F(GainMapMathTest, ColorConversionLookup) {
nullptr);
EXPECT_EQ(getHdrConversionFn(ULTRAHDR_COLORGAMUT_UNSPECIFIED, ULTRAHDR_COLORGAMUT_BT709),
nullptr);
- EXPECT_EQ(getHdrConversionFn(ULTRAHDR_COLORGAMUT_UNSPECIFIED, ULTRAHDR_COLORGAMUT_P3),
- nullptr);
+ EXPECT_EQ(getHdrConversionFn(ULTRAHDR_COLORGAMUT_UNSPECIFIED, ULTRAHDR_COLORGAMUT_P3), nullptr);
EXPECT_EQ(getHdrConversionFn(ULTRAHDR_COLORGAMUT_UNSPECIFIED, ULTRAHDR_COLORGAMUT_BT2100),
nullptr);
}
TEST_F(GainMapMathTest, EncodeGain) {
- ultrahdr_metadata_struct metadata = { .maxContentBoost = 4.0f,
- .minContentBoost = 1.0f / 4.0f };
+ ultrahdr_metadata_struct metadata;
+
+ metadata.minContentBoost = 1.0f / 4.0f;
+ metadata.maxContentBoost = 4.0f;
EXPECT_EQ(encodeGain(0.0f, 0.0f, &metadata), 127);
EXPECT_EQ(encodeGain(0.0f, 1.0f, &metadata), 127);
@@ -972,8 +1038,10 @@ TEST_F(GainMapMathTest, EncodeGain) {
}
TEST_F(GainMapMathTest, ApplyGain) {
- ultrahdr_metadata_struct metadata = { .maxContentBoost = 4.0f,
- .minContentBoost = 1.0f / 4.0f };
+ ultrahdr_metadata_struct metadata;
+
+ metadata.minContentBoost = 1.0f / 4.0f;
+ metadata.maxContentBoost = 4.0f;
float displayBoost = metadata.maxContentBoost;
EXPECT_RGB_NEAR(applyGain(RgbBlack(), 0.0f, &metadata), RgbBlack());
@@ -1021,7 +1089,7 @@ TEST_F(GainMapMathTest, ApplyGain) {
EXPECT_RGB_NEAR(applyGain(RgbWhite(), 0.75f, &metadata), RgbWhite() * 4.0f);
EXPECT_RGB_NEAR(applyGain(RgbWhite(), 1.0f, &metadata), RgbWhite() * 8.0f);
- Color e = {{{ 0.0f, 0.5f, 1.0f }}};
+ Color e = {{{0.0f, 0.5f, 1.0f}}};
metadata.maxContentBoost = 4.0f;
metadata.minContentBoost = 1.0f / 4.0f;
@@ -1041,13 +1109,12 @@ TEST_F(GainMapMathTest, ApplyGain) {
applyGain(RgbGreen(), 1.0f, &metadata, displayBoost));
EXPECT_RGB_EQ(applyGain(RgbBlue(), 1.0f, &metadata),
applyGain(RgbBlue(), 1.0f, &metadata, displayBoost));
- EXPECT_RGB_EQ(applyGain(e, 1.0f, &metadata),
- applyGain(e, 1.0f, &metadata, displayBoost));
+ EXPECT_RGB_EQ(applyGain(e, 1.0f, &metadata), applyGain(e, 1.0f, &metadata, displayBoost));
}
TEST_F(GainMapMathTest, GetYuv420Pixel) {
jpegr_uncompressed_struct image = Yuv420Image();
- Color (*colors)[4] = Yuv420Colors();
+ Color(*colors)[4] = Yuv420Colors();
for (size_t y = 0; y < 4; ++y) {
for (size_t x = 0; x < 4; ++x) {
@@ -1058,7 +1125,7 @@ TEST_F(GainMapMathTest, GetYuv420Pixel) {
TEST_F(GainMapMathTest, GetP010Pixel) {
jpegr_uncompressed_struct image = P010Image();
- Color (*colors)[4] = P010Colors();
+ Color(*colors)[4] = P010Colors();
for (size_t y = 0; y < 4; ++y) {
for (size_t x = 0; x < 4; ++x) {
@@ -1069,13 +1136,13 @@ TEST_F(GainMapMathTest, GetP010Pixel) {
TEST_F(GainMapMathTest, SampleYuv420) {
jpegr_uncompressed_struct image = Yuv420Image();
- Color (*colors)[4] = Yuv420Colors();
+ Color(*colors)[4] = Yuv420Colors();
static const size_t kMapScaleFactor = 2;
for (size_t y = 0; y < 4 / kMapScaleFactor; ++y) {
for (size_t x = 0; x < 4 / kMapScaleFactor; ++x) {
- Color min = {{{ 1.0f, 1.0f, 1.0f }}};
- Color max = {{{ -1.0f, -1.0f, -1.0f }}};
+ Color min = {{{1.0f, 1.0f, 1.0f}}};
+ Color max = {{{-1.0f, -1.0f, -1.0f}}};
for (size_t dy = 0; dy < kMapScaleFactor; ++dy) {
for (size_t dx = 0; dx < kMapScaleFactor; ++dx) {
@@ -1095,13 +1162,13 @@ TEST_F(GainMapMathTest, SampleYuv420) {
TEST_F(GainMapMathTest, SampleP010) {
jpegr_uncompressed_struct image = P010Image();
- Color (*colors)[4] = P010Colors();
+ Color(*colors)[4] = P010Colors();
static const size_t kMapScaleFactor = 2;
for (size_t y = 0; y < 4 / kMapScaleFactor; ++y) {
for (size_t x = 0; x < 4 / kMapScaleFactor; ++x) {
- Color min = {{{ 1.0f, 1.0f, 1.0f }}};
- Color max = {{{ -1.0f, -1.0f, -1.0f }}};
+ Color min = {{{1.0f, 1.0f, 1.0f}}};
+ Color max = {{{-1.0f, -1.0f, -1.0f}}};
for (size_t dy = 0; dy < kMapScaleFactor; ++dy) {
for (size_t dx = 0; dx < kMapScaleFactor; ++dx) {
@@ -1121,7 +1188,7 @@ TEST_F(GainMapMathTest, SampleP010) {
TEST_F(GainMapMathTest, SampleMap) {
jpegr_uncompressed_struct image = MapImage();
- float (*values)[4] = MapValues();
+ float(*values)[4] = MapValues();
static const size_t kMapScaleFactor = 2;
ShepardsIDW idwTable(kMapScaleFactor);
@@ -1166,22 +1233,21 @@ TEST_F(GainMapMathTest, ColorToRgba1010102) {
EXPECT_EQ(colorToRgba1010102(RgbGreen()), 0x3 << 30 | 0x3ff << 10);
EXPECT_EQ(colorToRgba1010102(RgbBlue()), 0x3 << 30 | 0x3ff << 20);
- Color e_gamma = {{{ 0.1f, 0.2f, 0.3f }}};
+ Color e_gamma = {{{0.1f, 0.2f, 0.3f}}};
EXPECT_EQ(colorToRgba1010102(e_gamma),
- 0x3 << 30
- | static_cast<uint32_t>(0.1f * static_cast<float>(0x3ff))
- | static_cast<uint32_t>(0.2f * static_cast<float>(0x3ff)) << 10
- | static_cast<uint32_t>(0.3f * static_cast<float>(0x3ff)) << 20);
+ 0x3 << 30 | static_cast<uint32_t>(0.1f * static_cast<float>(0x3ff)) |
+ static_cast<uint32_t>(0.2f * static_cast<float>(0x3ff)) << 10 |
+ static_cast<uint32_t>(0.3f * static_cast<float>(0x3ff)) << 20);
}
TEST_F(GainMapMathTest, ColorToRgbaF16) {
- EXPECT_EQ(colorToRgbaF16(RgbBlack()), ((uint64_t) 0x3C00) << 48);
+ EXPECT_EQ(colorToRgbaF16(RgbBlack()), ((uint64_t)0x3C00) << 48);
EXPECT_EQ(colorToRgbaF16(RgbWhite()), 0x3C003C003C003C00);
- EXPECT_EQ(colorToRgbaF16(RgbRed()), (((uint64_t) 0x3C00) << 48) | ((uint64_t) 0x3C00));
- EXPECT_EQ(colorToRgbaF16(RgbGreen()), (((uint64_t) 0x3C00) << 48) | (((uint64_t) 0x3C00) << 16));
- EXPECT_EQ(colorToRgbaF16(RgbBlue()), (((uint64_t) 0x3C00) << 48) | (((uint64_t) 0x3C00) << 32));
+ EXPECT_EQ(colorToRgbaF16(RgbRed()), (((uint64_t)0x3C00) << 48) | ((uint64_t)0x3C00));
+ EXPECT_EQ(colorToRgbaF16(RgbGreen()), (((uint64_t)0x3C00) << 48) | (((uint64_t)0x3C00) << 16));
+ EXPECT_EQ(colorToRgbaF16(RgbBlue()), (((uint64_t)0x3C00) << 48) | (((uint64_t)0x3C00) << 32));
- Color e_gamma = {{{ 0.1f, 0.2f, 0.3f }}};
+ Color e_gamma = {{{0.1f, 0.2f, 0.3f}}};
EXPECT_EQ(colorToRgbaF16(e_gamma), 0x3C0034CD32662E66);
}
@@ -1190,16 +1256,14 @@ TEST_F(GainMapMathTest, Float32ToFloat16) {
EXPECT_EQ(floatToHalf(0.0f), 0x0);
EXPECT_EQ(floatToHalf(1.0f), 0x3C00);
EXPECT_EQ(floatToHalf(-1.0f), 0xBC00);
- EXPECT_EQ(floatToHalf(0x1.fffffep127f), 0x7FFF); // float max
+ EXPECT_EQ(floatToHalf(0x1.fffffep127f), 0x7FFF); // float max
EXPECT_EQ(floatToHalf(-0x1.fffffep127f), 0xFFFF); // float min
- EXPECT_EQ(floatToHalf(0x1.0p-126f), 0x0); // float zero
+ EXPECT_EQ(floatToHalf(0x1.0p-126f), 0x0); // float zero
}
TEST_F(GainMapMathTest, GenerateMapLuminanceSrgb) {
- EXPECT_FLOAT_EQ(SrgbYuvToLuminance(YuvBlack(), srgbLuminance),
- 0.0f);
- EXPECT_FLOAT_EQ(SrgbYuvToLuminance(YuvWhite(), srgbLuminance),
- kSdrWhiteNits);
+ EXPECT_FLOAT_EQ(SrgbYuvToLuminance(YuvBlack(), srgbLuminance), 0.0f);
+ EXPECT_FLOAT_EQ(SrgbYuvToLuminance(YuvWhite(), srgbLuminance), kSdrWhiteNits);
EXPECT_NEAR(SrgbYuvToLuminance(SrgbYuvRed(), srgbLuminance),
srgbLuminance(RgbRed()) * kSdrWhiteNits, LuminanceEpsilon());
EXPECT_NEAR(SrgbYuvToLuminance(SrgbYuvGreen(), srgbLuminance),
@@ -1209,12 +1273,10 @@ TEST_F(GainMapMathTest, GenerateMapLuminanceSrgb) {
}
TEST_F(GainMapMathTest, GenerateMapLuminanceSrgbP3) {
- EXPECT_FLOAT_EQ(SrgbYuvToLuminance(YuvBlack(), p3Luminance),
- 0.0f);
- EXPECT_FLOAT_EQ(SrgbYuvToLuminance(YuvWhite(), p3Luminance),
- kSdrWhiteNits);
- EXPECT_NEAR(SrgbYuvToLuminance(SrgbYuvRed(), p3Luminance),
- p3Luminance(RgbRed()) * kSdrWhiteNits, LuminanceEpsilon());
+ EXPECT_FLOAT_EQ(SrgbYuvToLuminance(YuvBlack(), p3Luminance), 0.0f);
+ EXPECT_FLOAT_EQ(SrgbYuvToLuminance(YuvWhite(), p3Luminance), kSdrWhiteNits);
+ EXPECT_NEAR(SrgbYuvToLuminance(SrgbYuvRed(), p3Luminance), p3Luminance(RgbRed()) * kSdrWhiteNits,
+ LuminanceEpsilon());
EXPECT_NEAR(SrgbYuvToLuminance(SrgbYuvGreen(), p3Luminance),
p3Luminance(RgbGreen()) * kSdrWhiteNits, LuminanceEpsilon());
EXPECT_NEAR(SrgbYuvToLuminance(SrgbYuvBlue(), p3Luminance),
@@ -1222,10 +1284,8 @@ TEST_F(GainMapMathTest, GenerateMapLuminanceSrgbP3) {
}
TEST_F(GainMapMathTest, GenerateMapLuminanceSrgbBt2100) {
- EXPECT_FLOAT_EQ(SrgbYuvToLuminance(YuvBlack(), bt2100Luminance),
- 0.0f);
- EXPECT_FLOAT_EQ(SrgbYuvToLuminance(YuvWhite(), bt2100Luminance),
- kSdrWhiteNits);
+ EXPECT_FLOAT_EQ(SrgbYuvToLuminance(YuvBlack(), bt2100Luminance), 0.0f);
+ EXPECT_FLOAT_EQ(SrgbYuvToLuminance(YuvWhite(), bt2100Luminance), kSdrWhiteNits);
EXPECT_NEAR(SrgbYuvToLuminance(SrgbYuvRed(), bt2100Luminance),
bt2100Luminance(RgbRed()) * kSdrWhiteNits, LuminanceEpsilon());
EXPECT_NEAR(SrgbYuvToLuminance(SrgbYuvGreen(), bt2100Luminance),
@@ -1235,125 +1295,94 @@ TEST_F(GainMapMathTest, GenerateMapLuminanceSrgbBt2100) {
}
TEST_F(GainMapMathTest, GenerateMapLuminanceHlg) {
- EXPECT_FLOAT_EQ(Bt2100YuvToLuminance(YuvBlack(), hlgInvOetf, identityConversion,
- bt2100Luminance, kHlgMaxNits),
+ EXPECT_FLOAT_EQ(Bt2100YuvToLuminance(YuvBlack(), hlgInvOetf, identityConversion, bt2100Luminance,
+ kHlgMaxNits),
0.0f);
- EXPECT_FLOAT_EQ(Bt2100YuvToLuminance(YuvWhite(), hlgInvOetf, identityConversion,
- bt2100Luminance, kHlgMaxNits),
+ EXPECT_FLOAT_EQ(Bt2100YuvToLuminance(YuvWhite(), hlgInvOetf, identityConversion, bt2100Luminance,
+ kHlgMaxNits),
kHlgMaxNits);
- EXPECT_NEAR(Bt2100YuvToLuminance(Bt2100YuvRed(), hlgInvOetf, identityConversion,
- bt2100Luminance, kHlgMaxNits),
+ EXPECT_NEAR(Bt2100YuvToLuminance(Bt2100YuvRed(), hlgInvOetf, identityConversion, bt2100Luminance,
+ kHlgMaxNits),
bt2100Luminance(RgbRed()) * kHlgMaxNits, LuminanceEpsilon());
EXPECT_NEAR(Bt2100YuvToLuminance(Bt2100YuvGreen(), hlgInvOetf, identityConversion,
bt2100Luminance, kHlgMaxNits),
bt2100Luminance(RgbGreen()) * kHlgMaxNits, LuminanceEpsilon());
- EXPECT_NEAR(Bt2100YuvToLuminance(Bt2100YuvBlue(), hlgInvOetf, identityConversion,
- bt2100Luminance, kHlgMaxNits),
+ EXPECT_NEAR(Bt2100YuvToLuminance(Bt2100YuvBlue(), hlgInvOetf, identityConversion, bt2100Luminance,
+ kHlgMaxNits),
bt2100Luminance(RgbBlue()) * kHlgMaxNits, LuminanceEpsilon());
}
TEST_F(GainMapMathTest, GenerateMapLuminancePq) {
- EXPECT_FLOAT_EQ(Bt2100YuvToLuminance(YuvBlack(), pqInvOetf, identityConversion,
- bt2100Luminance, kPqMaxNits),
- 0.0f);
- EXPECT_FLOAT_EQ(Bt2100YuvToLuminance(YuvWhite(), pqInvOetf, identityConversion,
- bt2100Luminance, kPqMaxNits),
- kPqMaxNits);
- EXPECT_NEAR(Bt2100YuvToLuminance(Bt2100YuvRed(), pqInvOetf, identityConversion,
- bt2100Luminance, kPqMaxNits),
+ EXPECT_FLOAT_EQ(
+ Bt2100YuvToLuminance(YuvBlack(), pqInvOetf, identityConversion, bt2100Luminance, kPqMaxNits),
+ 0.0f);
+ EXPECT_FLOAT_EQ(
+ Bt2100YuvToLuminance(YuvWhite(), pqInvOetf, identityConversion, bt2100Luminance, kPqMaxNits),
+ kPqMaxNits);
+ EXPECT_NEAR(Bt2100YuvToLuminance(Bt2100YuvRed(), pqInvOetf, identityConversion, bt2100Luminance,
+ kPqMaxNits),
bt2100Luminance(RgbRed()) * kPqMaxNits, LuminanceEpsilon());
- EXPECT_NEAR(Bt2100YuvToLuminance(Bt2100YuvGreen(), pqInvOetf, identityConversion,
- bt2100Luminance, kPqMaxNits),
+ EXPECT_NEAR(Bt2100YuvToLuminance(Bt2100YuvGreen(), pqInvOetf, identityConversion, bt2100Luminance,
+ kPqMaxNits),
bt2100Luminance(RgbGreen()) * kPqMaxNits, LuminanceEpsilon());
- EXPECT_NEAR(Bt2100YuvToLuminance(Bt2100YuvBlue(), pqInvOetf, identityConversion,
- bt2100Luminance, kPqMaxNits),
+ EXPECT_NEAR(Bt2100YuvToLuminance(Bt2100YuvBlue(), pqInvOetf, identityConversion, bt2100Luminance,
+ kPqMaxNits),
bt2100Luminance(RgbBlue()) * kPqMaxNits, LuminanceEpsilon());
}
TEST_F(GainMapMathTest, ApplyMap) {
- ultrahdr_metadata_struct metadata = { .maxContentBoost = 8.0f,
- .minContentBoost = 1.0f / 8.0f };
-
- EXPECT_RGB_EQ(Recover(YuvWhite(), 1.0f, &metadata),
- RgbWhite() * 8.0f);
- EXPECT_RGB_EQ(Recover(YuvBlack(), 1.0f, &metadata),
- RgbBlack());
- EXPECT_RGB_CLOSE(Recover(SrgbYuvRed(), 1.0f, &metadata),
- RgbRed() * 8.0f);
- EXPECT_RGB_CLOSE(Recover(SrgbYuvGreen(), 1.0f, &metadata),
- RgbGreen() * 8.0f);
- EXPECT_RGB_CLOSE(Recover(SrgbYuvBlue(), 1.0f, &metadata),
- RgbBlue() * 8.0f);
-
- EXPECT_RGB_EQ(Recover(YuvWhite(), 0.75f, &metadata),
- RgbWhite() * sqrt(8.0f));
- EXPECT_RGB_EQ(Recover(YuvBlack(), 0.75f, &metadata),
- RgbBlack());
- EXPECT_RGB_CLOSE(Recover(SrgbYuvRed(), 0.75f, &metadata),
- RgbRed() * sqrt(8.0f));
- EXPECT_RGB_CLOSE(Recover(SrgbYuvGreen(), 0.75f, &metadata),
- RgbGreen() * sqrt(8.0f));
- EXPECT_RGB_CLOSE(Recover(SrgbYuvBlue(), 0.75f, &metadata),
- RgbBlue() * sqrt(8.0f));
-
- EXPECT_RGB_EQ(Recover(YuvWhite(), 0.5f, &metadata),
- RgbWhite());
- EXPECT_RGB_EQ(Recover(YuvBlack(), 0.5f, &metadata),
- RgbBlack());
- EXPECT_RGB_CLOSE(Recover(SrgbYuvRed(), 0.5f, &metadata),
- RgbRed());
- EXPECT_RGB_CLOSE(Recover(SrgbYuvGreen(), 0.5f, &metadata),
- RgbGreen());
- EXPECT_RGB_CLOSE(Recover(SrgbYuvBlue(), 0.5f, &metadata),
- RgbBlue());
-
- EXPECT_RGB_EQ(Recover(YuvWhite(), 0.25f, &metadata),
- RgbWhite() / sqrt(8.0f));
- EXPECT_RGB_EQ(Recover(YuvBlack(), 0.25f, &metadata),
- RgbBlack());
- EXPECT_RGB_CLOSE(Recover(SrgbYuvRed(), 0.25f, &metadata),
- RgbRed() / sqrt(8.0f));
- EXPECT_RGB_CLOSE(Recover(SrgbYuvGreen(), 0.25f, &metadata),
- RgbGreen() / sqrt(8.0f));
- EXPECT_RGB_CLOSE(Recover(SrgbYuvBlue(), 0.25f, &metadata),
- RgbBlue() / sqrt(8.0f));
-
- EXPECT_RGB_EQ(Recover(YuvWhite(), 0.0f, &metadata),
- RgbWhite() / 8.0f);
- EXPECT_RGB_EQ(Recover(YuvBlack(), 0.0f, &metadata),
- RgbBlack());
- EXPECT_RGB_CLOSE(Recover(SrgbYuvRed(), 0.0f, &metadata),
- RgbRed() / 8.0f);
- EXPECT_RGB_CLOSE(Recover(SrgbYuvGreen(), 0.0f, &metadata),
- RgbGreen() / 8.0f);
- EXPECT_RGB_CLOSE(Recover(SrgbYuvBlue(), 0.0f, &metadata),
- RgbBlue() / 8.0f);
+ ultrahdr_metadata_struct metadata;
+
+ metadata.minContentBoost = 1.0f / 8.0f;
+ metadata.maxContentBoost = 8.0f;
+
+ EXPECT_RGB_EQ(Recover(YuvWhite(), 1.0f, &metadata), RgbWhite() * 8.0f);
+ EXPECT_RGB_EQ(Recover(YuvBlack(), 1.0f, &metadata), RgbBlack());
+ EXPECT_RGB_CLOSE(Recover(SrgbYuvRed(), 1.0f, &metadata), RgbRed() * 8.0f);
+ EXPECT_RGB_CLOSE(Recover(SrgbYuvGreen(), 1.0f, &metadata), RgbGreen() * 8.0f);
+ EXPECT_RGB_CLOSE(Recover(SrgbYuvBlue(), 1.0f, &metadata), RgbBlue() * 8.0f);
+
+ EXPECT_RGB_EQ(Recover(YuvWhite(), 0.75f, &metadata), RgbWhite() * sqrt(8.0f));
+ EXPECT_RGB_EQ(Recover(YuvBlack(), 0.75f, &metadata), RgbBlack());
+ EXPECT_RGB_CLOSE(Recover(SrgbYuvRed(), 0.75f, &metadata), RgbRed() * sqrt(8.0f));
+ EXPECT_RGB_CLOSE(Recover(SrgbYuvGreen(), 0.75f, &metadata), RgbGreen() * sqrt(8.0f));
+ EXPECT_RGB_CLOSE(Recover(SrgbYuvBlue(), 0.75f, &metadata), RgbBlue() * sqrt(8.0f));
+
+ EXPECT_RGB_EQ(Recover(YuvWhite(), 0.5f, &metadata), RgbWhite());
+ EXPECT_RGB_EQ(Recover(YuvBlack(), 0.5f, &metadata), RgbBlack());
+ EXPECT_RGB_CLOSE(Recover(SrgbYuvRed(), 0.5f, &metadata), RgbRed());
+ EXPECT_RGB_CLOSE(Recover(SrgbYuvGreen(), 0.5f, &metadata), RgbGreen());
+ EXPECT_RGB_CLOSE(Recover(SrgbYuvBlue(), 0.5f, &metadata), RgbBlue());
+
+ EXPECT_RGB_EQ(Recover(YuvWhite(), 0.25f, &metadata), RgbWhite() / sqrt(8.0f));
+ EXPECT_RGB_EQ(Recover(YuvBlack(), 0.25f, &metadata), RgbBlack());
+ EXPECT_RGB_CLOSE(Recover(SrgbYuvRed(), 0.25f, &metadata), RgbRed() / sqrt(8.0f));
+ EXPECT_RGB_CLOSE(Recover(SrgbYuvGreen(), 0.25f, &metadata), RgbGreen() / sqrt(8.0f));
+ EXPECT_RGB_CLOSE(Recover(SrgbYuvBlue(), 0.25f, &metadata), RgbBlue() / sqrt(8.0f));
+
+ EXPECT_RGB_EQ(Recover(YuvWhite(), 0.0f, &metadata), RgbWhite() / 8.0f);
+ EXPECT_RGB_EQ(Recover(YuvBlack(), 0.0f, &metadata), RgbBlack());
+ EXPECT_RGB_CLOSE(Recover(SrgbYuvRed(), 0.0f, &metadata), RgbRed() / 8.0f);
+ EXPECT_RGB_CLOSE(Recover(SrgbYuvGreen(), 0.0f, &metadata), RgbGreen() / 8.0f);
+ EXPECT_RGB_CLOSE(Recover(SrgbYuvBlue(), 0.0f, &metadata), RgbBlue() / 8.0f);
metadata.maxContentBoost = 8.0f;
metadata.minContentBoost = 1.0f;
- EXPECT_RGB_EQ(Recover(YuvWhite(), 1.0f, &metadata),
- RgbWhite() * 8.0f);
- EXPECT_RGB_EQ(Recover(YuvWhite(), 2.0f / 3.0f, &metadata),
- RgbWhite() * 4.0f);
- EXPECT_RGB_EQ(Recover(YuvWhite(), 1.0f / 3.0f, &metadata),
- RgbWhite() * 2.0f);
- EXPECT_RGB_EQ(Recover(YuvWhite(), 0.0f, &metadata),
- RgbWhite());
+ EXPECT_RGB_EQ(Recover(YuvWhite(), 1.0f, &metadata), RgbWhite() * 8.0f);
+ EXPECT_RGB_EQ(Recover(YuvWhite(), 2.0f / 3.0f, &metadata), RgbWhite() * 4.0f);
+ EXPECT_RGB_EQ(Recover(YuvWhite(), 1.0f / 3.0f, &metadata), RgbWhite() * 2.0f);
+ EXPECT_RGB_EQ(Recover(YuvWhite(), 0.0f, &metadata), RgbWhite());
metadata.maxContentBoost = 8.0f;
- metadata.minContentBoost = 0.5f;;
-
- EXPECT_RGB_EQ(Recover(YuvWhite(), 1.0f, &metadata),
- RgbWhite() * 8.0f);
- EXPECT_RGB_EQ(Recover(YuvWhite(), 0.75, &metadata),
- RgbWhite() * 4.0f);
- EXPECT_RGB_EQ(Recover(YuvWhite(), 0.5f, &metadata),
- RgbWhite() * 2.0f);
- EXPECT_RGB_EQ(Recover(YuvWhite(), 0.25f, &metadata),
- RgbWhite());
- EXPECT_RGB_EQ(Recover(YuvWhite(), 0.0f, &metadata),
- RgbWhite() / 2.0f);
+ metadata.minContentBoost = 0.5f;
+ ;
+
+ EXPECT_RGB_EQ(Recover(YuvWhite(), 1.0f, &metadata), RgbWhite() * 8.0f);
+ EXPECT_RGB_EQ(Recover(YuvWhite(), 0.75, &metadata), RgbWhite() * 4.0f);
+ EXPECT_RGB_EQ(Recover(YuvWhite(), 0.5f, &metadata), RgbWhite() * 2.0f);
+ EXPECT_RGB_EQ(Recover(YuvWhite(), 0.25f, &metadata), RgbWhite());
+ EXPECT_RGB_EQ(Recover(YuvWhite(), 0.0f, &metadata), RgbWhite() / 2.0f);
}
-} // namespace ultrahdr
+} // namespace ultrahdr
diff --git a/tests/icchelper_test.cpp b/tests/icchelper_test.cpp
index 081ef0e..02ad27b 100644
--- a/tests/icchelper_test.cpp
+++ b/tests/icchelper_test.cpp
@@ -16,17 +16,18 @@
#include <gtest/gtest.h>
-#include "ultrahdr/icc.h"
+#include "icc.h"
namespace ultrahdr {
class IccHelperTest : public testing::Test {
-public:
- IccHelperTest();
- ~IccHelperTest();
-protected:
- virtual void SetUp();
- virtual void TearDown();
+ public:
+ IccHelperTest();
+ ~IccHelperTest();
+
+ protected:
+ virtual void SetUp();
+ virtual void TearDown();
};
IccHelperTest::IccHelperTest() {}
@@ -38,41 +39,39 @@ void IccHelperTest::SetUp() {}
void IccHelperTest::TearDown() {}
TEST_F(IccHelperTest, iccWriteThenRead) {
- std::shared_ptr<DataStruct> iccBt709 =
- IccHelper::writeIccProfile(ULTRAHDR_TF_SRGB, ULTRAHDR_COLORGAMUT_BT709);
- ASSERT_NE(iccBt709->getLength(), 0);
- ASSERT_NE(iccBt709->getData(), nullptr);
- EXPECT_EQ(IccHelper::readIccColorGamut(iccBt709->getData(), iccBt709->getLength()),
- ULTRAHDR_COLORGAMUT_BT709);
-
- std::shared_ptr<DataStruct> iccP3 =
- IccHelper::writeIccProfile(ULTRAHDR_TF_SRGB, ULTRAHDR_COLORGAMUT_P3);
- ASSERT_NE(iccP3->getLength(), 0);
- ASSERT_NE(iccP3->getData(), nullptr);
- EXPECT_EQ(IccHelper::readIccColorGamut(iccP3->getData(), iccP3->getLength()),
- ULTRAHDR_COLORGAMUT_P3);
-
- std::shared_ptr<DataStruct> iccBt2100 =
- IccHelper::writeIccProfile(ULTRAHDR_TF_SRGB, ULTRAHDR_COLORGAMUT_BT2100);
- ASSERT_NE(iccBt2100->getLength(), 0);
- ASSERT_NE(iccBt2100->getData(), nullptr);
- EXPECT_EQ(IccHelper::readIccColorGamut(iccBt2100->getData(), iccBt2100->getLength()),
- ULTRAHDR_COLORGAMUT_BT2100);
+ std::shared_ptr<DataStruct> iccBt709 =
+ IccHelper::writeIccProfile(ULTRAHDR_TF_SRGB, ULTRAHDR_COLORGAMUT_BT709);
+ ASSERT_NE(iccBt709->getLength(), 0);
+ ASSERT_NE(iccBt709->getData(), nullptr);
+ EXPECT_EQ(IccHelper::readIccColorGamut(iccBt709->getData(), iccBt709->getLength()),
+ ULTRAHDR_COLORGAMUT_BT709);
+
+ std::shared_ptr<DataStruct> iccP3 =
+ IccHelper::writeIccProfile(ULTRAHDR_TF_SRGB, ULTRAHDR_COLORGAMUT_P3);
+ ASSERT_NE(iccP3->getLength(), 0);
+ ASSERT_NE(iccP3->getData(), nullptr);
+ EXPECT_EQ(IccHelper::readIccColorGamut(iccP3->getData(), iccP3->getLength()),
+ ULTRAHDR_COLORGAMUT_P3);
+
+ std::shared_ptr<DataStruct> iccBt2100 =
+ IccHelper::writeIccProfile(ULTRAHDR_TF_SRGB, ULTRAHDR_COLORGAMUT_BT2100);
+ ASSERT_NE(iccBt2100->getLength(), 0);
+ ASSERT_NE(iccBt2100->getData(), nullptr);
+ EXPECT_EQ(IccHelper::readIccColorGamut(iccBt2100->getData(), iccBt2100->getLength()),
+ ULTRAHDR_COLORGAMUT_BT2100);
}
TEST_F(IccHelperTest, iccEndianness) {
- std::shared_ptr<DataStruct> icc =
- IccHelper::writeIccProfile(ULTRAHDR_TF_SRGB, ULTRAHDR_COLORGAMUT_BT709);
- size_t profile_size = icc->getLength() - kICCIdentifierSize;
+ std::shared_ptr<DataStruct> icc =
+ IccHelper::writeIccProfile(ULTRAHDR_TF_SRGB, ULTRAHDR_COLORGAMUT_BT709);
+ size_t profile_size = icc->getLength() - kICCIdentifierSize;
- uint8_t* icc_bytes = reinterpret_cast<uint8_t*>(icc->getData()) + kICCIdentifierSize;
- uint32_t encoded_size = static_cast<uint32_t>(icc_bytes[0]) << 24 |
- static_cast<uint32_t>(icc_bytes[1]) << 16 |
- static_cast<uint32_t>(icc_bytes[2]) << 8 |
- static_cast<uint32_t>(icc_bytes[3]);
+ uint8_t* icc_bytes = reinterpret_cast<uint8_t*>(icc->getData()) + kICCIdentifierSize;
+ uint32_t encoded_size =
+ static_cast<uint32_t>(icc_bytes[0]) << 24 | static_cast<uint32_t>(icc_bytes[1]) << 16 |
+ static_cast<uint32_t>(icc_bytes[2]) << 8 | static_cast<uint32_t>(icc_bytes[3]);
- EXPECT_EQ(static_cast<size_t>(encoded_size), profile_size);
+ EXPECT_EQ(static_cast<size_t>(encoded_size), profile_size);
}
} // namespace ultrahdr
-
diff --git a/tests/jpegdecoderhelper_test.cpp b/tests/jpegdecoderhelper_test.cpp
index 838c911..3b8e88e 100644
--- a/tests/jpegdecoderhelper_test.cpp
+++ b/tests/jpegdecoderhelper_test.cpp
@@ -14,12 +14,14 @@
* limitations under the License.
*/
-#include <fcntl.h>
#include <gtest/gtest.h>
-#include "ultrahdr/ultrahdrcommon.h"
-#include "ultrahdr/jpegdecoderhelper.h"
-#include "ultrahdr/icc.h"
+#include <fstream>
+#include <iostream>
+
+#include "ultrahdrcommon.h"
+#include "jpegdecoderhelper.h"
+#include "icc.h"
namespace ultrahdr {
@@ -41,120 +43,105 @@ namespace ultrahdr {
#define IMAGE_HEIGHT 240
class JpegDecoderHelperTest : public testing::Test {
-public:
- struct Image {
- std::unique_ptr<uint8_t[]> buffer;
- size_t size;
- };
- JpegDecoderHelperTest();
- ~JpegDecoderHelperTest();
-
-protected:
- virtual void SetUp();
- virtual void TearDown();
-
- Image mYuvImage, mYuvIccImage, mGreyImage;
+ public:
+ struct Image {
+ std::unique_ptr<uint8_t[]> buffer;
+ size_t size;
+ };
+ JpegDecoderHelperTest();
+ ~JpegDecoderHelperTest();
+
+ protected:
+ virtual void SetUp();
+ virtual void TearDown();
+
+ Image mYuvImage, mYuvIccImage, mGreyImage;
};
JpegDecoderHelperTest::JpegDecoderHelperTest() {}
JpegDecoderHelperTest::~JpegDecoderHelperTest() {}
-static size_t getFileSize(int fd) {
- struct stat st;
- if (fstat(fd, &st) < 0) {
- ALOGW("%s : fstat failed", __func__);
- return 0;
- }
- return st.st_size; // bytes
-}
-
static bool loadFile(const char filename[], JpegDecoderHelperTest::Image* result) {
- int fd = open(filename, O_CLOEXEC);
- if (fd < 0) {
- return false;
- }
- int length = getFileSize(fd);
- if (length == 0) {
- close(fd);
- return false;
- }
- result->buffer.reset(new uint8_t[length]);
- if (read(fd, result->buffer.get(), length) != static_cast<ssize_t>(length)) {
- close(fd);
- return false;
- }
- close(fd);
+ std::ifstream ifd(filename, std::ios::binary | std::ios::ate);
+ if (ifd.good()) {
+ int size = ifd.tellg();
+ ifd.seekg(0, std::ios::beg);
+ result->buffer.reset(new uint8_t[size]);
+ ifd.read(reinterpret_cast<char*>(result->buffer.get()), size);
+ ifd.close();
return true;
+ }
+ return false;
}
void JpegDecoderHelperTest::SetUp() {
- if (!loadFile(YUV_IMAGE, &mYuvImage)) {
- FAIL() << "Load file " << YUV_IMAGE << " failed";
- }
- mYuvImage.size = YUV_IMAGE_SIZE;
- if (!loadFile(YUV_ICC_IMAGE, &mYuvIccImage)) {
- FAIL() << "Load file " << YUV_ICC_IMAGE << " failed";
- }
- mYuvIccImage.size = YUV_ICC_IMAGE_SIZE;
- if (!loadFile(GREY_IMAGE, &mGreyImage)) {
- FAIL() << "Load file " << GREY_IMAGE << " failed";
- }
- mGreyImage.size = GREY_IMAGE_SIZE;
+ if (!loadFile(YUV_IMAGE, &mYuvImage)) {
+ FAIL() << "Load file " << YUV_IMAGE << " failed";
+ }
+ mYuvImage.size = YUV_IMAGE_SIZE;
+ if (!loadFile(YUV_ICC_IMAGE, &mYuvIccImage)) {
+ FAIL() << "Load file " << YUV_ICC_IMAGE << " failed";
+ }
+ mYuvIccImage.size = YUV_ICC_IMAGE_SIZE;
+ if (!loadFile(GREY_IMAGE, &mGreyImage)) {
+ FAIL() << "Load file " << GREY_IMAGE << " failed";
+ }
+ mGreyImage.size = GREY_IMAGE_SIZE;
}
void JpegDecoderHelperTest::TearDown() {}
TEST_F(JpegDecoderHelperTest, decodeYuvImage) {
- JpegDecoderHelper decoder;
- EXPECT_TRUE(decoder.decompressImage(mYuvImage.buffer.get(), mYuvImage.size));
- ASSERT_GT(decoder.getDecompressedImageSize(), static_cast<uint32_t>(0));
- EXPECT_EQ(IccHelper::readIccColorGamut(decoder.getICCPtr(), decoder.getICCSize()),
- ULTRAHDR_COLORGAMUT_UNSPECIFIED);
+ JpegDecoderHelper decoder;
+ EXPECT_TRUE(decoder.decompressImage(mYuvImage.buffer.get(), mYuvImage.size));
+ ASSERT_GT(decoder.getDecompressedImageSize(), static_cast<uint32_t>(0));
+ EXPECT_EQ(IccHelper::readIccColorGamut(decoder.getICCPtr(), decoder.getICCSize()),
+ ULTRAHDR_COLORGAMUT_UNSPECIFIED);
}
TEST_F(JpegDecoderHelperTest, decodeYuvIccImage) {
- JpegDecoderHelper decoder;
- EXPECT_TRUE(decoder.decompressImage(mYuvIccImage.buffer.get(), mYuvIccImage.size));
- ASSERT_GT(decoder.getDecompressedImageSize(), static_cast<uint32_t>(0));
- EXPECT_EQ(IccHelper::readIccColorGamut(decoder.getICCPtr(), decoder.getICCSize()),
- ULTRAHDR_COLORGAMUT_BT709);
+ JpegDecoderHelper decoder;
+ EXPECT_TRUE(decoder.decompressImage(mYuvIccImage.buffer.get(), mYuvIccImage.size));
+ ASSERT_GT(decoder.getDecompressedImageSize(), static_cast<uint32_t>(0));
+ EXPECT_EQ(IccHelper::readIccColorGamut(decoder.getICCPtr(), decoder.getICCSize()),
+ ULTRAHDR_COLORGAMUT_BT709);
}
TEST_F(JpegDecoderHelperTest, decodeGreyImage) {
- JpegDecoderHelper decoder;
- EXPECT_TRUE(decoder.decompressImage(mGreyImage.buffer.get(), mGreyImage.size));
- ASSERT_GT(decoder.getDecompressedImageSize(), static_cast<uint32_t>(0));
+ JpegDecoderHelper decoder;
+ EXPECT_TRUE(decoder.decompressImage(mGreyImage.buffer.get(), mGreyImage.size));
+ ASSERT_GT(decoder.getDecompressedImageSize(), static_cast<uint32_t>(0));
}
TEST_F(JpegDecoderHelperTest, getCompressedImageParameters) {
- size_t width = 0, height = 0;
- std::vector<uint8_t> icc, exif;
+ size_t width = 0, height = 0;
+ std::vector<uint8_t> icc, exif;
- JpegDecoderHelper decoder;
- EXPECT_TRUE(decoder.getCompressedImageParameters(mYuvImage.buffer.get(), mYuvImage.size, &width,
- &height, &icc, &exif));
+ JpegDecoderHelper decoder;
+ EXPECT_TRUE(decoder.getCompressedImageParameters(mYuvImage.buffer.get(), mYuvImage.size, &width,
+ &height, &icc, &exif));
- EXPECT_EQ(width, IMAGE_WIDTH);
- EXPECT_EQ(height, IMAGE_HEIGHT);
- EXPECT_EQ(icc.size(), 0);
- EXPECT_EQ(exif.size(), 0);
+ EXPECT_EQ(width, IMAGE_WIDTH);
+ EXPECT_EQ(height, IMAGE_HEIGHT);
+ EXPECT_EQ(icc.size(), 0);
+ EXPECT_EQ(exif.size(), 0);
}
TEST_F(JpegDecoderHelperTest, getCompressedImageParametersIcc) {
- size_t width = 0, height = 0;
- std::vector<uint8_t> icc, exif;
+ size_t width = 0, height = 0;
+ std::vector<uint8_t> icc, exif;
- JpegDecoderHelper decoder;
- EXPECT_TRUE(decoder.getCompressedImageParameters(mYuvIccImage.buffer.get(), mYuvIccImage.size,
- &width, &height, &icc, &exif));
+ JpegDecoderHelper decoder;
+ EXPECT_TRUE(decoder.getCompressedImageParameters(mYuvIccImage.buffer.get(), mYuvIccImage.size,
+ &width, &height, &icc, &exif));
- EXPECT_EQ(width, IMAGE_WIDTH);
- EXPECT_EQ(height, IMAGE_HEIGHT);
- EXPECT_GT(icc.size(), 0);
- EXPECT_GT(exif.size(), 0);
+ EXPECT_EQ(width, IMAGE_WIDTH);
+ EXPECT_EQ(height, IMAGE_HEIGHT);
+ EXPECT_GT(icc.size(), 0);
+ EXPECT_GT(exif.size(), 0);
- EXPECT_EQ(IccHelper::readIccColorGamut(icc.data(), icc.size()), ULTRAHDR_COLORGAMUT_BT709);
+ EXPECT_EQ(IccHelper::readIccColorGamut(icc.data(), icc.size()), ULTRAHDR_COLORGAMUT_BT709);
}
-} // namespace ultrahdr
+} // namespace ultrahdr
diff --git a/tests/jpegencoderhelper_test.cpp b/tests/jpegencoderhelper_test.cpp
index 37aa995..3463c26 100644
--- a/tests/jpegencoderhelper_test.cpp
+++ b/tests/jpegencoderhelper_test.cpp
@@ -14,12 +14,14 @@
* limitations under the License.
*/
-#include <fcntl.h>
#include <gtest/gtest.h>
-#include "ultrahdr/ultrahdrcommon.h"
-#include "ultrahdr/ultrahdr.h"
-#include "ultrahdr/jpegencoderhelper.h"
+#include <fstream>
+#include <iostream>
+
+#include "ultrahdrcommon.h"
+#include "ultrahdr.h"
+#include "jpegencoderhelper.h"
namespace ultrahdr {
@@ -41,102 +43,84 @@ namespace ultrahdr {
#define JPEG_QUALITY 90
class JpegEncoderHelperTest : public testing::Test {
-public:
- struct Image {
- std::unique_ptr<uint8_t[]> buffer;
- size_t width;
- size_t height;
- };
- JpegEncoderHelperTest();
- ~JpegEncoderHelperTest();
-
-protected:
- virtual void SetUp();
- virtual void TearDown();
-
- Image mAlignedImage, mUnalignedImage, mSingleChannelImage;
+ public:
+ struct Image {
+ std::unique_ptr<uint8_t[]> buffer;
+ size_t width;
+ size_t height;
+ };
+ JpegEncoderHelperTest();
+ ~JpegEncoderHelperTest();
+
+ protected:
+ virtual void SetUp();
+ virtual void TearDown();
+
+ Image mAlignedImage, mUnalignedImage, mSingleChannelImage;
};
JpegEncoderHelperTest::JpegEncoderHelperTest() {}
JpegEncoderHelperTest::~JpegEncoderHelperTest() {}
-static size_t getFileSize(int fd) {
- struct stat st;
- if (fstat(fd, &st) < 0) {
- ALOGW("%s : fstat failed", __func__);
- return 0;
- }
- return st.st_size; // bytes
-}
-
static bool loadFile(const char filename[], JpegEncoderHelperTest::Image* result) {
- int fd = open(filename, O_CLOEXEC);
- if (fd < 0) {
- return false;
- }
- int length = getFileSize(fd);
- if (length == 0) {
- close(fd);
- return false;
- }
- result->buffer.reset(new uint8_t[length]);
- if (read(fd, result->buffer.get(), length) != static_cast<ssize_t>(length)) {
- close(fd);
- return false;
- }
- close(fd);
+ std::ifstream ifd(filename, std::ios::binary | std::ios::ate);
+ if (ifd.good()) {
+ int size = ifd.tellg();
+ ifd.seekg(0, std::ios::beg);
+ result->buffer.reset(new uint8_t[size]);
+ ifd.read(reinterpret_cast<char*>(result->buffer.get()), size);
+ ifd.close();
return true;
+ }
+ return false;
}
void JpegEncoderHelperTest::SetUp() {
- if (!loadFile(ALIGNED_IMAGE, &mAlignedImage)) {
- FAIL() << "Load file " << ALIGNED_IMAGE << " failed";
- }
- mAlignedImage.width = ALIGNED_IMAGE_WIDTH;
- mAlignedImage.height = ALIGNED_IMAGE_HEIGHT;
- if (!loadFile(UNALIGNED_IMAGE, &mUnalignedImage)) {
- FAIL() << "Load file " << UNALIGNED_IMAGE << " failed";
- }
- mUnalignedImage.width = UNALIGNED_IMAGE_WIDTH;
- mUnalignedImage.height = UNALIGNED_IMAGE_HEIGHT;
- if (!loadFile(SINGLE_CHANNEL_IMAGE, &mSingleChannelImage)) {
- FAIL() << "Load file " << SINGLE_CHANNEL_IMAGE << " failed";
- }
- mSingleChannelImage.width = SINGLE_CHANNEL_IMAGE_WIDTH;
- mSingleChannelImage.height = SINGLE_CHANNEL_IMAGE_HEIGHT;
+ if (!loadFile(ALIGNED_IMAGE, &mAlignedImage)) {
+ FAIL() << "Load file " << ALIGNED_IMAGE << " failed";
+ }
+ mAlignedImage.width = ALIGNED_IMAGE_WIDTH;
+ mAlignedImage.height = ALIGNED_IMAGE_HEIGHT;
+ if (!loadFile(UNALIGNED_IMAGE, &mUnalignedImage)) {
+ FAIL() << "Load file " << UNALIGNED_IMAGE << " failed";
+ }
+ mUnalignedImage.width = UNALIGNED_IMAGE_WIDTH;
+ mUnalignedImage.height = UNALIGNED_IMAGE_HEIGHT;
+ if (!loadFile(SINGLE_CHANNEL_IMAGE, &mSingleChannelImage)) {
+ FAIL() << "Load file " << SINGLE_CHANNEL_IMAGE << " failed";
+ }
+ mSingleChannelImage.width = SINGLE_CHANNEL_IMAGE_WIDTH;
+ mSingleChannelImage.height = SINGLE_CHANNEL_IMAGE_HEIGHT;
}
void JpegEncoderHelperTest::TearDown() {}
TEST_F(JpegEncoderHelperTest, encodeAlignedImage) {
- JpegEncoderHelper encoder;
- EXPECT_TRUE(encoder.compressImage(mAlignedImage.buffer.get(),
- mAlignedImage.buffer.get() +
- mAlignedImage.width * mAlignedImage.height,
- mAlignedImage.width, mAlignedImage.height,
- mAlignedImage.width, mAlignedImage.width / 2, JPEG_QUALITY,
- NULL, 0));
- ASSERT_GT(encoder.getCompressedImageSize(), static_cast<uint32_t>(0));
+ JpegEncoderHelper encoder;
+ EXPECT_TRUE(encoder.compressImage(
+ mAlignedImage.buffer.get(),
+ mAlignedImage.buffer.get() + mAlignedImage.width * mAlignedImage.height, mAlignedImage.width,
+ mAlignedImage.height, mAlignedImage.width, mAlignedImage.width / 2, JPEG_QUALITY, NULL, 0));
+ ASSERT_GT(encoder.getCompressedImageSize(), static_cast<uint32_t>(0));
}
TEST_F(JpegEncoderHelperTest, encodeUnalignedImage) {
- JpegEncoderHelper encoder;
- EXPECT_TRUE(encoder.compressImage(mUnalignedImage.buffer.get(),
- mUnalignedImage.buffer.get() +
- mUnalignedImage.width * mUnalignedImage.height,
- mUnalignedImage.width, mUnalignedImage.height,
- mUnalignedImage.width, mUnalignedImage.width / 2,
- JPEG_QUALITY, NULL, 0));
- ASSERT_GT(encoder.getCompressedImageSize(), static_cast<uint32_t>(0));
+ JpegEncoderHelper encoder;
+ EXPECT_TRUE(encoder.compressImage(
+ mUnalignedImage.buffer.get(),
+ mUnalignedImage.buffer.get() + mUnalignedImage.width * mUnalignedImage.height,
+ mUnalignedImage.width, mUnalignedImage.height, mUnalignedImage.width,
+ mUnalignedImage.width / 2, JPEG_QUALITY, NULL, 0));
+ ASSERT_GT(encoder.getCompressedImageSize(), static_cast<uint32_t>(0));
}
TEST_F(JpegEncoderHelperTest, encodeSingleChannelImage) {
- JpegEncoderHelper encoder;
- EXPECT_TRUE(encoder.compressImage(mSingleChannelImage.buffer.get(), nullptr,
- mSingleChannelImage.width, mSingleChannelImage.height,
- mSingleChannelImage.width, 0, JPEG_QUALITY, NULL, 0));
- ASSERT_GT(encoder.getCompressedImageSize(), static_cast<uint32_t>(0));
+ JpegEncoderHelper encoder;
+ EXPECT_TRUE(encoder.compressImage(mSingleChannelImage.buffer.get(), nullptr,
+ mSingleChannelImage.width, mSingleChannelImage.height,
+ mSingleChannelImage.width, 0, JPEG_QUALITY, NULL, 0));
+ ASSERT_GT(encoder.getCompressedImageSize(), static_cast<uint32_t>(0));
}
-} // namespace ultrahdr
+} // namespace ultrahdr
diff --git a/tests/jpegr_test.cpp b/tests/jpegr_test.cpp
index d3832e8..bfbfcde 100644
--- a/tests/jpegr_test.cpp
+++ b/tests/jpegr_test.cpp
@@ -14,15 +14,19 @@
* limitations under the License.
*/
+#ifdef _WIN32
+#include <windows.h>
+#else
#include <sys/time.h>
+#endif
#include <gtest/gtest.h>
#include <fstream>
#include <iostream>
-#include "ultrahdr/ultrahdrcommon.h"
-#include "ultrahdr/jpegr.h"
-#include "ultrahdr/jpegrutils.h"
+#include "ultrahdrcommon.h"
+#include "jpegr.h"
+#include "jpegrutils.h"
//#define DUMP_OUTPUT
@@ -59,7 +63,7 @@ typedef enum {
* rawImg.loadRawResource(kYCbCrP010FileName);
*/
class UhdrUnCompressedStructWrapper {
-public:
+ public:
UhdrUnCompressedStructWrapper(size_t width, size_t height, UhdrInputFormat format);
~UhdrUnCompressedStructWrapper() = default;
@@ -70,7 +74,7 @@ public:
bool loadRawResource(const char* fileName);
jr_uncompressed_ptr getImageHandle();
-private:
+ private:
std::unique_ptr<uint8_t[]> mLumaData;
std::unique_ptr<uint8_t[]> mChromaData;
jpegr_uncompressed_struct mImg;
@@ -85,14 +89,14 @@ private:
* rawImg.allocateMemory();
*/
class UhdrCompressedStructWrapper {
-public:
+ public:
UhdrCompressedStructWrapper(size_t width, size_t height);
~UhdrCompressedStructWrapper() = default;
bool allocateMemory();
jr_compressed_ptr getImageHandle();
-private:
+ private:
std::unique_ptr<uint8_t[]> mData;
jpegr_compressed_struct mImg{};
size_t mWidth;
@@ -200,7 +204,7 @@ bool UhdrUnCompressedStructWrapper::loadRawResource(const char* fileName) {
if (ifd.good()) {
int bpp = mFormat == YCbCr_p010 ? 2 : 1;
int size = ifd.tellg();
- int length = mImg.width * mImg.height * bpp * 3 / 2; // 2x2 subsampling
+ int length = mImg.width * mImg.height * bpp * 3 / 2; // 2x2 subsampling
if (size < length) {
std::cerr << "requested to read " << length << " bytes from file : " << fileName
<< ", file contains only " << length << " bytes" << std::endl;
@@ -247,9 +251,7 @@ bool UhdrUnCompressedStructWrapper::loadRawResource(const char* fileName) {
return false;
}
-jr_uncompressed_ptr UhdrUnCompressedStructWrapper::getImageHandle() {
- return &mImg;
-}
+jr_uncompressed_ptr UhdrUnCompressedStructWrapper::getImageHandle() { return &mImg; }
UhdrCompressedStructWrapper::UhdrCompressedStructWrapper(size_t width, size_t height) {
mWidth = width;
@@ -261,7 +263,7 @@ bool UhdrCompressedStructWrapper::allocateMemory() {
std::cerr << "Object in bad state, mem alloc failed" << std::endl;
return false;
}
- int maxLength = std::max(8 * 1024 /* min size 8kb */, (int)(mWidth * mHeight * 3 * 2));
+ int maxLength = (std::max)(8 * 1024 /* min size 8kb */, (int)(mWidth * mHeight * 3 * 2));
mData = std::make_unique<uint8_t[]>(maxLength);
mImg.data = mData.get();
mImg.length = 0;
@@ -269,9 +271,7 @@ bool UhdrCompressedStructWrapper::allocateMemory() {
return true;
}
-jr_compressed_ptr UhdrCompressedStructWrapper::getImageHandle() {
- return &mImg;
-}
+jr_compressed_ptr UhdrCompressedStructWrapper::getImageHandle() { return &mImg; }
#ifdef DUMP_OUTPUT
static bool writeFile(const char* filename, void*& result, int length) {
@@ -306,14 +306,14 @@ void decodeJpegRImg(jr_compressed_ptr img, [[maybe_unused]] const char* outFileN
std::vector<uint8_t> exifData(0);
jpegr_info_struct info{0, 0, &iccData, &exifData};
JpegR jpegHdr;
- ASSERT_EQ(OK, jpegHdr.getJPEGRInfo(img, &info));
+ ASSERT_EQ(JPEGR_NO_ERROR, jpegHdr.getJPEGRInfo(img, &info));
ASSERT_EQ(kImageWidth, info.width);
ASSERT_EQ(kImageHeight, info.height);
size_t outSize = info.width * info.height * 8;
std::unique_ptr<uint8_t[]> data = std::make_unique<uint8_t[]>(outSize);
jpegr_uncompressed_struct destImage{};
destImage.data = data.get();
- ASSERT_EQ(OK, jpegHdr.decodeJPEGR(img, &destImage));
+ ASSERT_EQ(JPEGR_NO_ERROR, jpegHdr.decodeJPEGR(img, &destImage));
ASSERT_EQ(kImageWidth, destImage.width);
ASSERT_EQ(kImageHeight, destImage.height);
#ifdef DUMP_OUTPUT
@@ -340,33 +340,33 @@ TEST(JpegRTest, EncodeAPI0WithInvalidArgs) {
ASSERT_TRUE(rawImg.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
ASSERT_TRUE(rawImg.allocateMemory());
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(),
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg.getImageHandle(), -1, nullptr),
- OK)
- << "fail, API allows bad jpeg quality factor";
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(),
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg.getImageHandle(), 101, nullptr),
- OK)
- << "fail, API allows bad jpeg quality factor";
+ ASSERT_NE(
+ uHdrLib.encodeJPEGR(rawImg.getImageHandle(), ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle(), -1, nullptr),
+ JPEGR_NO_ERROR)
+ << "fail, API allows bad jpeg quality factor";
+ ASSERT_NE(
+ uHdrLib.encodeJPEGR(rawImg.getImageHandle(), ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle(), 101, nullptr),
+ JPEGR_NO_ERROR)
+ << "fail, API allows bad jpeg quality factor";
ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(),
ultrahdr_transfer_function::ULTRAHDR_TF_UNSPECIFIED,
jpgImg.getImageHandle(), kQuality, nullptr),
- OK)
- << "fail, API allows bad hdr transfer function";
+ JPEGR_NO_ERROR)
+ << "fail, API allows bad hdr transfer function";
ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(),
static_cast<ultrahdr_transfer_function>(
- ultrahdr_transfer_function::ULTRAHDR_TF_MAX + 1),
+ ultrahdr_transfer_function::ULTRAHDR_TF_MAX + 1),
jpgImg.getImageHandle(), kQuality, nullptr),
- OK)
- << "fail, API allows bad hdr transfer function";
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(),
- static_cast<ultrahdr_transfer_function>(-10),
- jpgImg.getImageHandle(), kQuality, nullptr),
- OK)
- << "fail, API allows bad hdr transfer function";
+ JPEGR_NO_ERROR)
+ << "fail, API allows bad hdr transfer function";
+ ASSERT_NE(
+ uHdrLib.encodeJPEGR(rawImg.getImageHandle(), static_cast<ultrahdr_transfer_function>(-10),
+ jpgImg.getImageHandle(), kQuality, nullptr),
+ JPEGR_NO_ERROR)
+ << "fail, API allows bad hdr transfer function";
}
// test dest
@@ -375,54 +375,54 @@ TEST(JpegRTest, EncodeAPI0WithInvalidArgs) {
ASSERT_TRUE(rawImg.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
ASSERT_TRUE(rawImg.allocateMemory());
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(),
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG, nullptr, kQuality,
- nullptr),
- OK)
- << "fail, API allows nullptr dest";
+ ASSERT_NE(
+ uHdrLib.encodeJPEGR(rawImg.getImageHandle(), ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ nullptr, kQuality, nullptr),
+ JPEGR_NO_ERROR)
+ << "fail, API allows nullptr dest";
UhdrCompressedStructWrapper jpgImg2(16, 16);
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(),
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg2.getImageHandle(), kQuality, nullptr),
- OK)
- << "fail, API allows nullptr dest";
+ ASSERT_NE(
+ uHdrLib.encodeJPEGR(rawImg.getImageHandle(), ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg2.getImageHandle(), kQuality, nullptr),
+ JPEGR_NO_ERROR)
+ << "fail, API allows nullptr dest";
}
// test p010 input
{
ASSERT_NE(uHdrLib.encodeJPEGR(nullptr, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
jpgImg.getImageHandle(), kQuality, nullptr),
- OK)
- << "fail, API allows nullptr p010 image";
+ JPEGR_NO_ERROR)
+ << "fail, API allows nullptr p010 image";
UhdrUnCompressedStructWrapper rawImg(16, 16, YCbCr_p010);
ASSERT_TRUE(rawImg.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(),
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg.getImageHandle(), kQuality, nullptr),
- OK)
- << "fail, API allows nullptr p010 image";
+ ASSERT_NE(
+ uHdrLib.encodeJPEGR(rawImg.getImageHandle(), ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle(), kQuality, nullptr),
+ JPEGR_NO_ERROR)
+ << "fail, API allows nullptr p010 image";
}
{
UhdrUnCompressedStructWrapper rawImg(16, 16, YCbCr_p010);
ASSERT_TRUE(rawImg.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_UNSPECIFIED));
ASSERT_TRUE(rawImg.allocateMemory());
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(),
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg.getImageHandle(), kQuality, nullptr),
- OK)
- << "fail, API allows bad p010 color gamut";
+ ASSERT_NE(
+ uHdrLib.encodeJPEGR(rawImg.getImageHandle(), ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle(), kQuality, nullptr),
+ JPEGR_NO_ERROR)
+ << "fail, API allows bad p010 color gamut";
UhdrUnCompressedStructWrapper rawImg2(16, 16, YCbCr_p010);
ASSERT_TRUE(rawImg2.setImageColorGamut(
- static_cast<ultrahdr_color_gamut>(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_MAX + 1)));
+ static_cast<ultrahdr_color_gamut>(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_MAX + 1)));
ASSERT_TRUE(rawImg2.allocateMemory());
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImg2.getImageHandle(),
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg.getImageHandle(), kQuality, nullptr),
- OK)
- << "fail, API allows bad p010 color gamut";
+ ASSERT_NE(
+ uHdrLib.encodeJPEGR(rawImg2.getImageHandle(), ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle(), kQuality, nullptr),
+ JPEGR_NO_ERROR)
+ << "fail, API allows bad p010 color gamut";
}
{
@@ -436,37 +436,37 @@ TEST(JpegRTest, EncodeAPI0WithInvalidArgs) {
rawImgP010->height = kHeight;
ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
jpgImg.getImageHandle(), kQuality, nullptr),
- OK)
- << "fail, API allows bad image width";
+ JPEGR_NO_ERROR)
+ << "fail, API allows bad image width";
rawImgP010->width = kWidth;
rawImgP010->height = kHeight - 1;
ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
jpgImg.getImageHandle(), kQuality, nullptr),
- OK)
- << "fail, API allows bad image height";
+ JPEGR_NO_ERROR)
+ << "fail, API allows bad image height";
rawImgP010->width = 0;
rawImgP010->height = kHeight;
ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
jpgImg.getImageHandle(), kQuality, nullptr),
- OK)
- << "fail, API allows bad image width";
+ JPEGR_NO_ERROR)
+ << "fail, API allows bad image width";
rawImgP010->width = kWidth;
rawImgP010->height = 0;
ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
jpgImg.getImageHandle(), kQuality, nullptr),
- OK)
- << "fail, API allows bad image height";
+ JPEGR_NO_ERROR)
+ << "fail, API allows bad image height";
rawImgP010->width = kWidth;
rawImgP010->height = kHeight;
rawImgP010->luma_stride = kWidth - 2;
ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
jpgImg.getImageHandle(), kQuality, nullptr),
- OK)
- << "fail, API allows bad luma stride";
+ JPEGR_NO_ERROR)
+ << "fail, API allows bad luma stride";
rawImgP010->width = kWidth;
rawImgP010->height = kHeight;
@@ -475,8 +475,8 @@ TEST(JpegRTest, EncodeAPI0WithInvalidArgs) {
rawImgP010->chroma_stride = kWidth - 2;
ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
jpgImg.getImageHandle(), kQuality, nullptr),
- OK)
- << "fail, API allows bad chroma stride";
+ JPEGR_NO_ERROR)
+ << "fail, API allows bad chroma stride";
}
}
@@ -499,30 +499,30 @@ TEST(JpegRTest, EncodeAPI1WithInvalidArgs) {
ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(), rawImg2.getImageHandle(),
ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
jpgImg.getImageHandle(), -1, nullptr),
- OK)
- << "fail, API allows bad jpeg quality factor";
+ JPEGR_NO_ERROR)
+ << "fail, API allows bad jpeg quality factor";
ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(), rawImg2.getImageHandle(),
ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
jpgImg.getImageHandle(), 101, nullptr),
- OK)
- << "fail, API allows bad jpeg quality factor";
+ JPEGR_NO_ERROR)
+ << "fail, API allows bad jpeg quality factor";
ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(), rawImg2.getImageHandle(),
ultrahdr_transfer_function::ULTRAHDR_TF_UNSPECIFIED,
jpgImg.getImageHandle(), kQuality, nullptr),
- OK)
- << "fail, API allows bad hdr transfer function";
+ JPEGR_NO_ERROR)
+ << "fail, API allows bad hdr transfer function";
ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(), rawImg2.getImageHandle(),
static_cast<ultrahdr_transfer_function>(
- ultrahdr_transfer_function::ULTRAHDR_TF_MAX + 1),
+ ultrahdr_transfer_function::ULTRAHDR_TF_MAX + 1),
jpgImg.getImageHandle(), kQuality, nullptr),
- OK)
- << "fail, API allows bad hdr transfer function";
+ JPEGR_NO_ERROR)
+ << "fail, API allows bad hdr transfer function";
ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(), rawImg2.getImageHandle(),
static_cast<ultrahdr_transfer_function>(-10),
jpgImg.getImageHandle(), kQuality, nullptr),
- OK)
- << "fail, API allows bad hdr transfer function";
+ JPEGR_NO_ERROR)
+ << "fail, API allows bad hdr transfer function";
}
// test dest
@@ -537,14 +537,14 @@ TEST(JpegRTest, EncodeAPI1WithInvalidArgs) {
ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(), rawImg2.getImageHandle(),
ultrahdr_transfer_function::ULTRAHDR_TF_HLG, nullptr, kQuality,
nullptr),
- OK)
- << "fail, API allows nullptr dest";
+ JPEGR_NO_ERROR)
+ << "fail, API allows nullptr dest";
UhdrCompressedStructWrapper jpgImg2(16, 16);
ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(), rawImg2.getImageHandle(),
ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
jpgImg2.getImageHandle(), kQuality, nullptr),
- OK)
- << "fail, API allows nullptr dest";
+ JPEGR_NO_ERROR)
+ << "fail, API allows nullptr dest";
}
// test p010 input
@@ -555,16 +555,16 @@ TEST(JpegRTest, EncodeAPI1WithInvalidArgs) {
ASSERT_NE(uHdrLib.encodeJPEGR(nullptr, rawImg2.getImageHandle(),
ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
jpgImg.getImageHandle(), kQuality, nullptr),
- OK)
- << "fail, API allows nullptr p010 image";
+ JPEGR_NO_ERROR)
+ << "fail, API allows nullptr p010 image";
UhdrUnCompressedStructWrapper rawImg(16, 16, YCbCr_p010);
ASSERT_TRUE(rawImg.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(), rawImg2.getImageHandle(),
ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
jpgImg.getImageHandle(), kQuality, nullptr),
- OK)
- << "fail, API allows nullptr p010 image";
+ JPEGR_NO_ERROR)
+ << "fail, API allows nullptr p010 image";
}
{
@@ -581,74 +581,74 @@ TEST(JpegRTest, EncodeAPI1WithInvalidArgs) {
rawImgP010->width = kWidth;
rawImgP010->height = kHeight;
rawImgP010->colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_UNSPECIFIED;
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420,
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg.getImageHandle(), kQuality, nullptr),
- OK)
- << "fail, API allows bad p010 color gamut";
+ ASSERT_NE(
+ uHdrLib.encodeJPEGR(rawImgP010, rawImg420, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle(), kQuality, nullptr),
+ JPEGR_NO_ERROR)
+ << "fail, API allows bad p010 color gamut";
rawImgP010->width = kWidth;
rawImgP010->height = kHeight;
rawImgP010->colorGamut =
- static_cast<ultrahdr_color_gamut>(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_MAX + 1);
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420,
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg.getImageHandle(), kQuality, nullptr),
- OK)
- << "fail, API allows bad p010 color gamut";
+ static_cast<ultrahdr_color_gamut>(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_MAX + 1);
+ ASSERT_NE(
+ uHdrLib.encodeJPEGR(rawImgP010, rawImg420, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle(), kQuality, nullptr),
+ JPEGR_NO_ERROR)
+ << "fail, API allows bad p010 color gamut";
rawImgP010->width = kWidth - 1;
rawImgP010->height = kHeight;
rawImgP010->colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100;
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420,
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg.getImageHandle(), kQuality, nullptr),
- OK)
- << "fail, API allows bad image width";
+ ASSERT_NE(
+ uHdrLib.encodeJPEGR(rawImgP010, rawImg420, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle(), kQuality, nullptr),
+ JPEGR_NO_ERROR)
+ << "fail, API allows bad image width";
rawImgP010->width = kWidth;
rawImgP010->height = kHeight - 1;
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420,
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg.getImageHandle(), kQuality, nullptr),
- OK)
- << "fail, API allows bad image height";
+ ASSERT_NE(
+ uHdrLib.encodeJPEGR(rawImgP010, rawImg420, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle(), kQuality, nullptr),
+ JPEGR_NO_ERROR)
+ << "fail, API allows bad image height";
rawImgP010->width = 0;
rawImgP010->height = kHeight;
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420,
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg.getImageHandle(), kQuality, nullptr),
- OK)
- << "fail, API allows bad image width";
+ ASSERT_NE(
+ uHdrLib.encodeJPEGR(rawImgP010, rawImg420, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle(), kQuality, nullptr),
+ JPEGR_NO_ERROR)
+ << "fail, API allows bad image width";
rawImgP010->width = kWidth;
rawImgP010->height = 0;
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420,
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg.getImageHandle(), kQuality, nullptr),
- OK)
- << "fail, API allows bad image height";
+ ASSERT_NE(
+ uHdrLib.encodeJPEGR(rawImgP010, rawImg420, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle(), kQuality, nullptr),
+ JPEGR_NO_ERROR)
+ << "fail, API allows bad image height";
rawImgP010->width = kWidth;
rawImgP010->height = kHeight;
rawImgP010->luma_stride = kWidth - 2;
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420,
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg.getImageHandle(), kQuality, nullptr),
- OK)
- << "fail, API allows bad luma stride";
+ ASSERT_NE(
+ uHdrLib.encodeJPEGR(rawImgP010, rawImg420, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle(), kQuality, nullptr),
+ JPEGR_NO_ERROR)
+ << "fail, API allows bad luma stride";
rawImgP010->width = kWidth;
rawImgP010->height = kHeight;
rawImgP010->luma_stride = kWidth + 64;
rawImgP010->chroma_data = rawImgP010->data;
rawImgP010->chroma_stride = kWidth - 2;
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420,
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg.getImageHandle(), kQuality, nullptr),
- OK)
- << "fail, API allows bad chroma stride";
+ ASSERT_NE(
+ uHdrLib.encodeJPEGR(rawImgP010, rawImg420, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle(), kQuality, nullptr),
+ JPEGR_NO_ERROR)
+ << "fail, API allows bad chroma stride";
}
// test 420 input
@@ -659,16 +659,16 @@ TEST(JpegRTest, EncodeAPI1WithInvalidArgs) {
ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(), nullptr,
ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
jpgImg.getImageHandle(), kQuality, nullptr),
- OK)
- << "fail, API allows nullptr 420 image";
+ JPEGR_NO_ERROR)
+ << "fail, API allows nullptr 420 image";
UhdrUnCompressedStructWrapper rawImg2(16, 16, YCbCr_420);
ASSERT_TRUE(rawImg2.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(), rawImg2.getImageHandle(),
ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
jpgImg.getImageHandle(), kQuality, nullptr),
- OK)
- << "fail, API allows nullptr 420 image";
+ JPEGR_NO_ERROR)
+ << "fail, API allows nullptr 420 image";
}
{
const int kWidth = 32, kHeight = 32;
@@ -684,74 +684,74 @@ TEST(JpegRTest, EncodeAPI1WithInvalidArgs) {
rawImg420->width = kWidth;
rawImg420->height = kHeight;
rawImg420->colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_UNSPECIFIED;
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420,
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg.getImageHandle(), kQuality, nullptr),
- OK)
- << "fail, API allows bad 420 color gamut";
+ ASSERT_NE(
+ uHdrLib.encodeJPEGR(rawImgP010, rawImg420, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle(), kQuality, nullptr),
+ JPEGR_NO_ERROR)
+ << "fail, API allows bad 420 color gamut";
rawImg420->width = kWidth;
rawImg420->height = kHeight;
rawImg420->colorGamut =
- static_cast<ultrahdr_color_gamut>(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_MAX + 1);
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420,
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg.getImageHandle(), kQuality, nullptr),
- OK)
- << "fail, API allows bad 420 color gamut";
+ static_cast<ultrahdr_color_gamut>(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_MAX + 1);
+ ASSERT_NE(
+ uHdrLib.encodeJPEGR(rawImgP010, rawImg420, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle(), kQuality, nullptr),
+ JPEGR_NO_ERROR)
+ << "fail, API allows bad 420 color gamut";
rawImg420->width = kWidth - 1;
rawImg420->height = kHeight;
rawImg420->colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT709;
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420,
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg.getImageHandle(), kQuality, nullptr),
- OK)
- << "fail, API allows bad image width for 420";
+ ASSERT_NE(
+ uHdrLib.encodeJPEGR(rawImgP010, rawImg420, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle(), kQuality, nullptr),
+ JPEGR_NO_ERROR)
+ << "fail, API allows bad image width for 420";
rawImg420->width = kWidth;
rawImg420->height = kHeight - 1;
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420,
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg.getImageHandle(), kQuality, nullptr),
- OK)
- << "fail, API allows bad image height for 420";
+ ASSERT_NE(
+ uHdrLib.encodeJPEGR(rawImgP010, rawImg420, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle(), kQuality, nullptr),
+ JPEGR_NO_ERROR)
+ << "fail, API allows bad image height for 420";
rawImg420->width = 0;
rawImg420->height = kHeight;
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420,
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg.getImageHandle(), kQuality, nullptr),
- OK)
- << "fail, API allows bad image width for 420";
+ ASSERT_NE(
+ uHdrLib.encodeJPEGR(rawImgP010, rawImg420, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle(), kQuality, nullptr),
+ JPEGR_NO_ERROR)
+ << "fail, API allows bad image width for 420";
rawImg420->width = kWidth;
rawImg420->height = 0;
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420,
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg.getImageHandle(), kQuality, nullptr),
- OK)
- << "fail, API allows bad image height for 420";
+ ASSERT_NE(
+ uHdrLib.encodeJPEGR(rawImgP010, rawImg420, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle(), kQuality, nullptr),
+ JPEGR_NO_ERROR)
+ << "fail, API allows bad image height for 420";
rawImg420->width = kWidth;
rawImg420->height = kHeight;
rawImg420->luma_stride = kWidth - 2;
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420,
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg.getImageHandle(), kQuality, nullptr),
- OK)
- << "fail, API allows bad luma stride for 420";
+ ASSERT_NE(
+ uHdrLib.encodeJPEGR(rawImgP010, rawImg420, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle(), kQuality, nullptr),
+ JPEGR_NO_ERROR)
+ << "fail, API allows bad luma stride for 420";
rawImg420->width = kWidth;
rawImg420->height = kHeight;
rawImg420->luma_stride = 0;
rawImg420->chroma_data = rawImgP010->data;
rawImg420->chroma_stride = kWidth / 2 - 2;
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420,
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg.getImageHandle(), kQuality, nullptr),
- OK)
- << "fail, API allows bad chroma stride for 420";
+ ASSERT_NE(
+ uHdrLib.encodeJPEGR(rawImgP010, rawImg420, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle(), kQuality, nullptr),
+ JPEGR_NO_ERROR)
+ << "fail, API allows bad chroma stride for 420";
}
}
@@ -771,25 +771,23 @@ TEST(JpegRTest, EncodeAPI2WithInvalidArgs) {
ASSERT_TRUE(rawImg2.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT709));
ASSERT_TRUE(rawImg2.allocateMemory());
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(), rawImg2.getImageHandle(),
- jpgImg.getImageHandle(),
- ultrahdr_transfer_function::ULTRAHDR_TF_UNSPECIFIED,
- jpgImg.getImageHandle()),
- OK)
- << "fail, API allows bad hdr transfer function";
+ ASSERT_NE(uHdrLib.encodeJPEGR(
+ rawImg.getImageHandle(), rawImg2.getImageHandle(), jpgImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_UNSPECIFIED, jpgImg.getImageHandle()),
+ JPEGR_NO_ERROR)
+ << "fail, API allows bad hdr transfer function";
ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(), rawImg2.getImageHandle(),
jpgImg.getImageHandle(),
static_cast<ultrahdr_transfer_function>(
- ultrahdr_transfer_function::ULTRAHDR_TF_MAX + 1),
+ ultrahdr_transfer_function::ULTRAHDR_TF_MAX + 1),
jpgImg.getImageHandle()),
- OK)
- << "fail, API allows bad hdr transfer function";
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(), rawImg2.getImageHandle(),
- jpgImg.getImageHandle(),
- static_cast<ultrahdr_transfer_function>(-10),
- jpgImg.getImageHandle()),
- OK)
- << "fail, API allows bad hdr transfer function";
+ JPEGR_NO_ERROR)
+ << "fail, API allows bad hdr transfer function";
+ ASSERT_NE(uHdrLib.encodeJPEGR(
+ rawImg.getImageHandle(), rawImg2.getImageHandle(), jpgImg.getImageHandle(),
+ static_cast<ultrahdr_transfer_function>(-10), jpgImg.getImageHandle()),
+ JPEGR_NO_ERROR)
+ << "fail, API allows bad hdr transfer function";
}
// test dest
@@ -804,15 +802,14 @@ TEST(JpegRTest, EncodeAPI2WithInvalidArgs) {
ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(), rawImg2.getImageHandle(),
jpgImg.getImageHandle(),
ultrahdr_transfer_function::ULTRAHDR_TF_HLG, nullptr),
- OK)
- << "fail, API allows nullptr dest";
+ JPEGR_NO_ERROR)
+ << "fail, API allows nullptr dest";
UhdrCompressedStructWrapper jpgImg2(16, 16);
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(), rawImg2.getImageHandle(),
- jpgImg.getImageHandle(),
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg2.getImageHandle()),
- OK)
- << "fail, API allows nullptr dest";
+ ASSERT_NE(uHdrLib.encodeJPEGR(
+ rawImg.getImageHandle(), rawImg2.getImageHandle(), jpgImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG, jpgImg2.getImageHandle()),
+ JPEGR_NO_ERROR)
+ << "fail, API allows nullptr dest";
}
// test compressed image
@@ -824,18 +821,17 @@ TEST(JpegRTest, EncodeAPI2WithInvalidArgs) {
ASSERT_TRUE(rawImg2.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT709));
ASSERT_TRUE(rawImg2.allocateMemory());
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(), rawImg2.getImageHandle(), nullptr,
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg.getImageHandle()),
- OK)
- << "fail, API allows nullptr for compressed image";
+ ASSERT_NE(
+ uHdrLib.encodeJPEGR(rawImg.getImageHandle(), rawImg2.getImageHandle(), nullptr,
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG, jpgImg.getImageHandle()),
+ JPEGR_NO_ERROR)
+ << "fail, API allows nullptr for compressed image";
UhdrCompressedStructWrapper jpgImg2(16, 16);
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(), rawImg2.getImageHandle(),
- jpgImg2.getImageHandle(),
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg.getImageHandle()),
- OK)
- << "fail, API allows nullptr for compressed image";
+ ASSERT_NE(uHdrLib.encodeJPEGR(
+ rawImg.getImageHandle(), rawImg2.getImageHandle(), jpgImg2.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG, jpgImg.getImageHandle()),
+ JPEGR_NO_ERROR)
+ << "fail, API allows nullptr for compressed image";
}
// test p010 input
@@ -843,20 +839,19 @@ TEST(JpegRTest, EncodeAPI2WithInvalidArgs) {
UhdrUnCompressedStructWrapper rawImg2(16, 16, YCbCr_420);
ASSERT_TRUE(rawImg2.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT709));
ASSERT_TRUE(rawImg2.allocateMemory());
- ASSERT_NE(uHdrLib.encodeJPEGR(nullptr, rawImg2.getImageHandle(), jpgImg.getImageHandle(),
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg.getImageHandle()),
- OK)
- << "fail, API allows nullptr p010 image";
+ ASSERT_NE(
+ uHdrLib.encodeJPEGR(nullptr, rawImg2.getImageHandle(), jpgImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG, jpgImg.getImageHandle()),
+ JPEGR_NO_ERROR)
+ << "fail, API allows nullptr p010 image";
UhdrUnCompressedStructWrapper rawImg(16, 16, YCbCr_p010);
ASSERT_TRUE(rawImg.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(), rawImg2.getImageHandle(),
- jpgImg.getImageHandle(),
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg.getImageHandle()),
- OK)
- << "fail, API allows nullptr p010 image";
+ ASSERT_NE(uHdrLib.encodeJPEGR(
+ rawImg.getImageHandle(), rawImg2.getImageHandle(), jpgImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG, jpgImg.getImageHandle()),
+ JPEGR_NO_ERROR)
+ << "fail, API allows nullptr p010 image";
}
{
@@ -873,74 +868,74 @@ TEST(JpegRTest, EncodeAPI2WithInvalidArgs) {
rawImgP010->width = kWidth;
rawImgP010->height = kHeight;
rawImgP010->colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_UNSPECIFIED;
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420, jpgImg.getImageHandle(),
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg.getImageHandle()),
- OK)
- << "fail, API allows bad p010 color gamut";
+ ASSERT_NE(
+ uHdrLib.encodeJPEGR(rawImgP010, rawImg420, jpgImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG, jpgImg.getImageHandle()),
+ JPEGR_NO_ERROR)
+ << "fail, API allows bad p010 color gamut";
rawImgP010->width = kWidth;
rawImgP010->height = kHeight;
rawImgP010->colorGamut =
- static_cast<ultrahdr_color_gamut>(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_MAX + 1);
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420, jpgImg.getImageHandle(),
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg.getImageHandle()),
- OK)
- << "fail, API allows bad p010 color gamut";
+ static_cast<ultrahdr_color_gamut>(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_MAX + 1);
+ ASSERT_NE(
+ uHdrLib.encodeJPEGR(rawImgP010, rawImg420, jpgImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG, jpgImg.getImageHandle()),
+ JPEGR_NO_ERROR)
+ << "fail, API allows bad p010 color gamut";
rawImgP010->width = kWidth - 1;
rawImgP010->height = kHeight;
rawImgP010->colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100;
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420, jpgImg.getImageHandle(),
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg.getImageHandle()),
- OK)
- << "fail, API allows bad image width";
+ ASSERT_NE(
+ uHdrLib.encodeJPEGR(rawImgP010, rawImg420, jpgImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG, jpgImg.getImageHandle()),
+ JPEGR_NO_ERROR)
+ << "fail, API allows bad image width";
rawImgP010->width = kWidth;
rawImgP010->height = kHeight - 1;
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420, jpgImg.getImageHandle(),
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg.getImageHandle()),
- OK)
- << "fail, API allows bad image height";
+ ASSERT_NE(
+ uHdrLib.encodeJPEGR(rawImgP010, rawImg420, jpgImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG, jpgImg.getImageHandle()),
+ JPEGR_NO_ERROR)
+ << "fail, API allows bad image height";
rawImgP010->width = 0;
rawImgP010->height = kHeight;
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420, jpgImg.getImageHandle(),
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg.getImageHandle()),
- OK)
- << "fail, API allows bad image width";
+ ASSERT_NE(
+ uHdrLib.encodeJPEGR(rawImgP010, rawImg420, jpgImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG, jpgImg.getImageHandle()),
+ JPEGR_NO_ERROR)
+ << "fail, API allows bad image width";
rawImgP010->width = kWidth;
rawImgP010->height = 0;
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420, jpgImg.getImageHandle(),
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg.getImageHandle()),
- OK)
- << "fail, API allows bad image height";
+ ASSERT_NE(
+ uHdrLib.encodeJPEGR(rawImgP010, rawImg420, jpgImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG, jpgImg.getImageHandle()),
+ JPEGR_NO_ERROR)
+ << "fail, API allows bad image height";
rawImgP010->width = kWidth;
rawImgP010->height = kHeight;
rawImgP010->luma_stride = kWidth - 2;
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420, jpgImg.getImageHandle(),
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg.getImageHandle()),
- OK)
- << "fail, API allows bad luma stride";
+ ASSERT_NE(
+ uHdrLib.encodeJPEGR(rawImgP010, rawImg420, jpgImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG, jpgImg.getImageHandle()),
+ JPEGR_NO_ERROR)
+ << "fail, API allows bad luma stride";
rawImgP010->width = kWidth;
rawImgP010->height = kHeight;
rawImgP010->luma_stride = kWidth + 64;
rawImgP010->chroma_data = rawImgP010->data;
rawImgP010->chroma_stride = kWidth - 2;
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420, jpgImg.getImageHandle(),
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg.getImageHandle()),
- OK)
- << "fail, API allows bad chroma stride";
+ ASSERT_NE(
+ uHdrLib.encodeJPEGR(rawImgP010, rawImg420, jpgImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG, jpgImg.getImageHandle()),
+ JPEGR_NO_ERROR)
+ << "fail, API allows bad chroma stride";
}
// test 420 input
@@ -948,20 +943,19 @@ TEST(JpegRTest, EncodeAPI2WithInvalidArgs) {
UhdrUnCompressedStructWrapper rawImg(16, 16, YCbCr_p010);
ASSERT_TRUE(rawImg.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
ASSERT_TRUE(rawImg.allocateMemory());
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(), nullptr, jpgImg.getImageHandle(),
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg.getImageHandle()),
- OK)
- << "fail, API allows nullptr 420 image";
+ ASSERT_NE(
+ uHdrLib.encodeJPEGR(rawImg.getImageHandle(), nullptr, jpgImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG, jpgImg.getImageHandle()),
+ JPEGR_NO_ERROR)
+ << "fail, API allows nullptr 420 image";
UhdrUnCompressedStructWrapper rawImg2(16, 16, YCbCr_420);
ASSERT_TRUE(rawImg2.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(), rawImg2.getImageHandle(),
- jpgImg.getImageHandle(),
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg.getImageHandle()),
- OK)
- << "fail, API allows nullptr 420 image";
+ ASSERT_NE(uHdrLib.encodeJPEGR(
+ rawImg.getImageHandle(), rawImg2.getImageHandle(), jpgImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG, jpgImg.getImageHandle()),
+ JPEGR_NO_ERROR)
+ << "fail, API allows nullptr 420 image";
}
{
const int kWidth = 32, kHeight = 32;
@@ -977,74 +971,74 @@ TEST(JpegRTest, EncodeAPI2WithInvalidArgs) {
rawImg420->width = kWidth;
rawImg420->height = kHeight;
rawImg420->colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_UNSPECIFIED;
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420, jpgImg.getImageHandle(),
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg.getImageHandle()),
- OK)
- << "fail, API allows bad 420 color gamut";
+ ASSERT_NE(
+ uHdrLib.encodeJPEGR(rawImgP010, rawImg420, jpgImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG, jpgImg.getImageHandle()),
+ JPEGR_NO_ERROR)
+ << "fail, API allows bad 420 color gamut";
rawImg420->width = kWidth;
rawImg420->height = kHeight;
rawImg420->colorGamut =
- static_cast<ultrahdr_color_gamut>(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_MAX + 1);
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420, jpgImg.getImageHandle(),
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg.getImageHandle()),
- OK)
- << "fail, API allows bad 420 color gamut";
+ static_cast<ultrahdr_color_gamut>(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_MAX + 1);
+ ASSERT_NE(
+ uHdrLib.encodeJPEGR(rawImgP010, rawImg420, jpgImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG, jpgImg.getImageHandle()),
+ JPEGR_NO_ERROR)
+ << "fail, API allows bad 420 color gamut";
rawImg420->width = kWidth - 1;
rawImg420->height = kHeight;
rawImg420->colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT709;
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420, jpgImg.getImageHandle(),
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg.getImageHandle()),
- OK)
- << "fail, API allows bad image width for 420";
+ ASSERT_NE(
+ uHdrLib.encodeJPEGR(rawImgP010, rawImg420, jpgImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG, jpgImg.getImageHandle()),
+ JPEGR_NO_ERROR)
+ << "fail, API allows bad image width for 420";
rawImg420->width = kWidth;
rawImg420->height = kHeight - 1;
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420, jpgImg.getImageHandle(),
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg.getImageHandle()),
- OK)
- << "fail, API allows bad image height for 420";
+ ASSERT_NE(
+ uHdrLib.encodeJPEGR(rawImgP010, rawImg420, jpgImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG, jpgImg.getImageHandle()),
+ JPEGR_NO_ERROR)
+ << "fail, API allows bad image height for 420";
rawImg420->width = 0;
rawImg420->height = kHeight;
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420, jpgImg.getImageHandle(),
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg.getImageHandle()),
- OK)
- << "fail, API allows bad image width for 420";
+ ASSERT_NE(
+ uHdrLib.encodeJPEGR(rawImgP010, rawImg420, jpgImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG, jpgImg.getImageHandle()),
+ JPEGR_NO_ERROR)
+ << "fail, API allows bad image width for 420";
rawImg420->width = kWidth;
rawImg420->height = 0;
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420, jpgImg.getImageHandle(),
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg.getImageHandle()),
- OK)
- << "fail, API allows bad image height for 420";
+ ASSERT_NE(
+ uHdrLib.encodeJPEGR(rawImgP010, rawImg420, jpgImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG, jpgImg.getImageHandle()),
+ JPEGR_NO_ERROR)
+ << "fail, API allows bad image height for 420";
rawImg420->width = kWidth;
rawImg420->height = kHeight;
rawImg420->luma_stride = kWidth - 2;
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420, jpgImg.getImageHandle(),
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg.getImageHandle()),
- OK)
- << "fail, API allows bad luma stride for 420";
+ ASSERT_NE(
+ uHdrLib.encodeJPEGR(rawImgP010, rawImg420, jpgImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG, jpgImg.getImageHandle()),
+ JPEGR_NO_ERROR)
+ << "fail, API allows bad luma stride for 420";
rawImg420->width = kWidth;
rawImg420->height = kHeight;
rawImg420->luma_stride = 0;
rawImg420->chroma_data = rawImgP010->data;
rawImg420->chroma_stride = kWidth / 2 - 2;
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420, jpgImg.getImageHandle(),
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg.getImageHandle()),
- OK)
- << "fail, API allows bad chroma stride for 420";
+ ASSERT_NE(
+ uHdrLib.encodeJPEGR(rawImgP010, rawImg420, jpgImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG, jpgImg.getImageHandle()),
+ JPEGR_NO_ERROR)
+ << "fail, API allows bad chroma stride for 420";
}
}
@@ -1064,19 +1058,19 @@ TEST(JpegRTest, EncodeAPI3WithInvalidArgs) {
ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(), jpgImg.getImageHandle(),
ultrahdr_transfer_function::ULTRAHDR_TF_UNSPECIFIED,
jpgImg.getImageHandle()),
- OK)
- << "fail, API allows bad hdr transfer function";
+ JPEGR_NO_ERROR)
+ << "fail, API allows bad hdr transfer function";
ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(), jpgImg.getImageHandle(),
static_cast<ultrahdr_transfer_function>(
- ultrahdr_transfer_function::ULTRAHDR_TF_MAX + 1),
- jpgImg.getImageHandle()),
- OK)
- << "fail, API allows bad hdr transfer function";
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(), jpgImg.getImageHandle(),
- static_cast<ultrahdr_transfer_function>(-10),
+ ultrahdr_transfer_function::ULTRAHDR_TF_MAX + 1),
jpgImg.getImageHandle()),
- OK)
- << "fail, API allows bad hdr transfer function";
+ JPEGR_NO_ERROR)
+ << "fail, API allows bad hdr transfer function";
+ ASSERT_NE(
+ uHdrLib.encodeJPEGR(rawImg.getImageHandle(), jpgImg.getImageHandle(),
+ static_cast<ultrahdr_transfer_function>(-10), jpgImg.getImageHandle()),
+ JPEGR_NO_ERROR)
+ << "fail, API allows bad hdr transfer function";
}
// test dest
@@ -1087,14 +1081,14 @@ TEST(JpegRTest, EncodeAPI3WithInvalidArgs) {
ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(), jpgImg.getImageHandle(),
ultrahdr_transfer_function::ULTRAHDR_TF_HLG, nullptr),
- OK)
- << "fail, API allows nullptr dest";
+ JPEGR_NO_ERROR)
+ << "fail, API allows nullptr dest";
UhdrCompressedStructWrapper jpgImg2(16, 16);
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(), jpgImg.getImageHandle(),
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg2.getImageHandle()),
- OK)
- << "fail, API allows nullptr dest";
+ ASSERT_NE(
+ uHdrLib.encodeJPEGR(rawImg.getImageHandle(), jpgImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG, jpgImg2.getImageHandle()),
+ JPEGR_NO_ERROR)
+ << "fail, API allows nullptr dest";
}
// test compressed image
@@ -1103,34 +1097,34 @@ TEST(JpegRTest, EncodeAPI3WithInvalidArgs) {
ASSERT_TRUE(rawImg.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
ASSERT_TRUE(rawImg.allocateMemory());
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(), nullptr,
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg.getImageHandle()),
- OK)
- << "fail, API allows nullptr for compressed image";
+ ASSERT_NE(
+ uHdrLib.encodeJPEGR(rawImg.getImageHandle(), nullptr,
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG, jpgImg.getImageHandle()),
+ JPEGR_NO_ERROR)
+ << "fail, API allows nullptr for compressed image";
UhdrCompressedStructWrapper jpgImg2(16, 16);
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(), jpgImg2.getImageHandle(),
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg.getImageHandle()),
- OK)
- << "fail, API allows nullptr for compressed image";
+ ASSERT_NE(
+ uHdrLib.encodeJPEGR(rawImg.getImageHandle(), jpgImg2.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG, jpgImg.getImageHandle()),
+ JPEGR_NO_ERROR)
+ << "fail, API allows nullptr for compressed image";
}
// test p010 input
{
- ASSERT_NE(uHdrLib.encodeJPEGR(nullptr, jpgImg.getImageHandle(),
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg.getImageHandle()),
- OK)
- << "fail, API allows nullptr p010 image";
+ ASSERT_NE(
+ uHdrLib.encodeJPEGR(nullptr, jpgImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG, jpgImg.getImageHandle()),
+ JPEGR_NO_ERROR)
+ << "fail, API allows nullptr p010 image";
UhdrUnCompressedStructWrapper rawImg(16, 16, YCbCr_p010);
ASSERT_TRUE(rawImg.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(), jpgImg.getImageHandle(),
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg.getImageHandle()),
- OK)
- << "fail, API allows nullptr p010 image";
+ ASSERT_NE(
+ uHdrLib.encodeJPEGR(rawImg.getImageHandle(), jpgImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG, jpgImg.getImageHandle()),
+ JPEGR_NO_ERROR)
+ << "fail, API allows nullptr p010 image";
}
{
@@ -1143,74 +1137,74 @@ TEST(JpegRTest, EncodeAPI3WithInvalidArgs) {
rawImgP010->width = kWidth;
rawImgP010->height = kHeight;
rawImgP010->colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_UNSPECIFIED;
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, jpgImg.getImageHandle(),
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg.getImageHandle()),
- OK)
- << "fail, API allows bad p010 color gamut";
+ ASSERT_NE(
+ uHdrLib.encodeJPEGR(rawImgP010, jpgImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG, jpgImg.getImageHandle()),
+ JPEGR_NO_ERROR)
+ << "fail, API allows bad p010 color gamut";
rawImgP010->width = kWidth;
rawImgP010->height = kHeight;
rawImgP010->colorGamut =
- static_cast<ultrahdr_color_gamut>(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_MAX + 1);
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, jpgImg.getImageHandle(),
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg.getImageHandle()),
- OK)
- << "fail, API allows bad p010 color gamut";
+ static_cast<ultrahdr_color_gamut>(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_MAX + 1);
+ ASSERT_NE(
+ uHdrLib.encodeJPEGR(rawImgP010, jpgImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG, jpgImg.getImageHandle()),
+ JPEGR_NO_ERROR)
+ << "fail, API allows bad p010 color gamut";
rawImgP010->width = kWidth - 1;
rawImgP010->height = kHeight;
rawImgP010->colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100;
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, jpgImg.getImageHandle(),
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg.getImageHandle()),
- OK)
- << "fail, API allows bad image width";
+ ASSERT_NE(
+ uHdrLib.encodeJPEGR(rawImgP010, jpgImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG, jpgImg.getImageHandle()),
+ JPEGR_NO_ERROR)
+ << "fail, API allows bad image width";
rawImgP010->width = kWidth;
rawImgP010->height = kHeight - 1;
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, jpgImg.getImageHandle(),
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg.getImageHandle()),
- OK)
- << "fail, API allows bad image height";
+ ASSERT_NE(
+ uHdrLib.encodeJPEGR(rawImgP010, jpgImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG, jpgImg.getImageHandle()),
+ JPEGR_NO_ERROR)
+ << "fail, API allows bad image height";
rawImgP010->width = 0;
rawImgP010->height = kHeight;
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, jpgImg.getImageHandle(),
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg.getImageHandle()),
- OK)
- << "fail, API allows bad image width";
+ ASSERT_NE(
+ uHdrLib.encodeJPEGR(rawImgP010, jpgImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG, jpgImg.getImageHandle()),
+ JPEGR_NO_ERROR)
+ << "fail, API allows bad image width";
rawImgP010->width = kWidth;
rawImgP010->height = 0;
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, jpgImg.getImageHandle(),
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg.getImageHandle()),
- OK)
- << "fail, API allows bad image height";
+ ASSERT_NE(
+ uHdrLib.encodeJPEGR(rawImgP010, jpgImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG, jpgImg.getImageHandle()),
+ JPEGR_NO_ERROR)
+ << "fail, API allows bad image height";
rawImgP010->width = kWidth;
rawImgP010->height = kHeight;
rawImgP010->luma_stride = kWidth - 2;
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, jpgImg.getImageHandle(),
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg.getImageHandle()),
- OK)
- << "fail, API allows bad luma stride";
+ ASSERT_NE(
+ uHdrLib.encodeJPEGR(rawImgP010, jpgImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG, jpgImg.getImageHandle()),
+ JPEGR_NO_ERROR)
+ << "fail, API allows bad luma stride";
rawImgP010->width = kWidth;
rawImgP010->height = kHeight;
rawImgP010->luma_stride = kWidth + 64;
rawImgP010->chroma_data = rawImgP010->data;
rawImgP010->chroma_stride = kWidth - 2;
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, jpgImg.getImageHandle(),
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg.getImageHandle()),
- OK)
- << "fail, API allows bad chroma stride";
+ ASSERT_NE(
+ uHdrLib.encodeJPEGR(rawImgP010, jpgImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG, jpgImg.getImageHandle()),
+ JPEGR_NO_ERROR)
+ << "fail, API allows bad chroma stride";
}
}
@@ -1223,30 +1217,30 @@ TEST(JpegRTest, EncodeAPI4WithInvalidArgs) {
// test dest
ASSERT_NE(uHdrLib.encodeJPEGR(jpgImg.getImageHandle(), jpgImg.getImageHandle(), nullptr, nullptr),
- OK)
- << "fail, API allows nullptr dest";
+ JPEGR_NO_ERROR)
+ << "fail, API allows nullptr dest";
ASSERT_NE(uHdrLib.encodeJPEGR(jpgImg.getImageHandle(), jpgImg.getImageHandle(), nullptr,
jpgImg2.getImageHandle()),
- OK)
- << "fail, API allows nullptr dest";
+ JPEGR_NO_ERROR)
+ << "fail, API allows nullptr dest";
// test primary image
ASSERT_NE(uHdrLib.encodeJPEGR(nullptr, jpgImg.getImageHandle(), nullptr, jpgImg.getImageHandle()),
- OK)
- << "fail, API allows nullptr primary image";
+ JPEGR_NO_ERROR)
+ << "fail, API allows nullptr primary image";
ASSERT_NE(uHdrLib.encodeJPEGR(jpgImg2.getImageHandle(), jpgImg.getImageHandle(), nullptr,
jpgImg.getImageHandle()),
- OK)
- << "fail, API allows nullptr primary image";
+ JPEGR_NO_ERROR)
+ << "fail, API allows nullptr primary image";
// test gain map
ASSERT_NE(uHdrLib.encodeJPEGR(jpgImg.getImageHandle(), nullptr, nullptr, jpgImg.getImageHandle()),
- OK)
- << "fail, API allows nullptr gain map image";
+ JPEGR_NO_ERROR)
+ << "fail, API allows nullptr gain map image";
ASSERT_NE(uHdrLib.encodeJPEGR(jpgImg.getImageHandle(), jpgImg2.getImageHandle(), nullptr,
jpgImg.getImageHandle()),
- OK)
- << "fail, API allows nullptr gain map image";
+ JPEGR_NO_ERROR)
+ << "fail, API allows nullptr gain map image";
// test metadata
ultrahdr_metadata_struct good_metadata;
@@ -1263,50 +1257,50 @@ TEST(JpegRTest, EncodeAPI4WithInvalidArgs) {
metadata.version = "1.1";
ASSERT_NE(uHdrLib.encodeJPEGR(jpgImg.getImageHandle(), jpgImg.getImageHandle(), &metadata,
jpgImg.getImageHandle()),
- OK)
- << "fail, API allows bad metadata version";
+ JPEGR_NO_ERROR)
+ << "fail, API allows bad metadata version";
metadata = good_metadata;
metadata.minContentBoost = 3.0f;
ASSERT_NE(uHdrLib.encodeJPEGR(jpgImg.getImageHandle(), jpgImg.getImageHandle(), &metadata,
jpgImg.getImageHandle()),
- OK)
- << "fail, API allows bad metadata content boost";
+ JPEGR_NO_ERROR)
+ << "fail, API allows bad metadata content boost";
metadata = good_metadata;
metadata.gamma = -0.1f;
ASSERT_NE(uHdrLib.encodeJPEGR(jpgImg.getImageHandle(), jpgImg.getImageHandle(), &metadata,
jpgImg.getImageHandle()),
- OK)
- << "fail, API allows bad metadata gamma";
+ JPEGR_NO_ERROR)
+ << "fail, API allows bad metadata gamma";
metadata = good_metadata;
metadata.offsetSdr = -0.1f;
ASSERT_NE(uHdrLib.encodeJPEGR(jpgImg.getImageHandle(), jpgImg.getImageHandle(), &metadata,
jpgImg.getImageHandle()),
- OK)
- << "fail, API allows bad metadata offset sdr";
+ JPEGR_NO_ERROR)
+ << "fail, API allows bad metadata offset sdr";
metadata = good_metadata;
metadata.offsetHdr = -0.1f;
ASSERT_NE(uHdrLib.encodeJPEGR(jpgImg.getImageHandle(), jpgImg.getImageHandle(), &metadata,
jpgImg.getImageHandle()),
- OK)
- << "fail, API allows bad metadata offset hdr";
+ JPEGR_NO_ERROR)
+ << "fail, API allows bad metadata offset hdr";
metadata = good_metadata;
metadata.hdrCapacityMax = 0.5f;
ASSERT_NE(uHdrLib.encodeJPEGR(jpgImg.getImageHandle(), jpgImg.getImageHandle(), &metadata,
jpgImg.getImageHandle()),
- OK)
- << "fail, API allows bad metadata hdr capacity max";
+ JPEGR_NO_ERROR)
+ << "fail, API allows bad metadata hdr capacity max";
metadata = good_metadata;
metadata.hdrCapacityMin = 0.5f;
ASSERT_NE(uHdrLib.encodeJPEGR(jpgImg.getImageHandle(), jpgImg.getImageHandle(), &metadata,
jpgImg.getImageHandle()),
- OK)
- << "fail, API allows bad metadata hdr capacity min";
+ JPEGR_NO_ERROR)
+ << "fail, API allows bad metadata hdr capacity min";
}
/* Test Decode API invalid arguments */
@@ -1320,33 +1314,33 @@ TEST(JpegRTest, DecodeAPIWithInvalidArgs) {
destImage.data = data.get();
// test jpegr image
- ASSERT_NE(uHdrLib.decodeJPEGR(nullptr, &destImage), OK)
- << "fail, API allows nullptr for jpegr img";
- ASSERT_NE(uHdrLib.decodeJPEGR(jpgImg.getImageHandle(), &destImage), OK)
- << "fail, API allows nullptr for jpegr img";
+ ASSERT_NE(uHdrLib.decodeJPEGR(nullptr, &destImage), JPEGR_NO_ERROR)
+ << "fail, API allows nullptr for jpegr img";
+ ASSERT_NE(uHdrLib.decodeJPEGR(jpgImg.getImageHandle(), &destImage), JPEGR_NO_ERROR)
+ << "fail, API allows nullptr for jpegr img";
ASSERT_TRUE(jpgImg.allocateMemory());
// test dest image
- ASSERT_NE(uHdrLib.decodeJPEGR(jpgImg.getImageHandle(), nullptr), OK)
- << "fail, API allows nullptr for dest";
+ ASSERT_NE(uHdrLib.decodeJPEGR(jpgImg.getImageHandle(), nullptr), JPEGR_NO_ERROR)
+ << "fail, API allows nullptr for dest";
destImage.data = nullptr;
- ASSERT_NE(uHdrLib.decodeJPEGR(jpgImg.getImageHandle(), &destImage), OK)
- << "fail, API allows nullptr for dest";
+ ASSERT_NE(uHdrLib.decodeJPEGR(jpgImg.getImageHandle(), &destImage), JPEGR_NO_ERROR)
+ << "fail, API allows nullptr for dest";
destImage.data = data.get();
// test max display boost
- ASSERT_NE(uHdrLib.decodeJPEGR(jpgImg.getImageHandle(), &destImage, 0.5), OK)
- << "fail, API allows invalid max display boost";
+ ASSERT_NE(uHdrLib.decodeJPEGR(jpgImg.getImageHandle(), &destImage, 0.5), JPEGR_NO_ERROR)
+ << "fail, API allows invalid max display boost";
// test output format
ASSERT_NE(uHdrLib.decodeJPEGR(jpgImg.getImageHandle(), &destImage, FLT_MAX, nullptr,
static_cast<ultrahdr_output_format>(-1)),
- OK)
- << "fail, API allows invalid output format";
+ JPEGR_NO_ERROR)
+ << "fail, API allows invalid output format";
ASSERT_NE(uHdrLib.decodeJPEGR(jpgImg.getImageHandle(), &destImage, FLT_MAX, nullptr,
static_cast<ultrahdr_output_format>(ULTRAHDR_OUTPUT_MAX + 1)),
- OK)
- << "fail, API allows invalid output format";
+ JPEGR_NO_ERROR)
+ << "fail, API allows invalid output format";
}
TEST(JpegRTest, writeXmpThenRead) {
@@ -1360,7 +1354,7 @@ TEST(JpegRTest, writeXmpThenRead) {
metadata_expected.hdrCapacityMin = 1.0f;
metadata_expected.hdrCapacityMax = metadata_expected.maxContentBoost;
const std::string nameSpace = "http://ns.adobe.com/xap/1.0/\0";
- const int nameSpaceLength = nameSpace.size() + 1; // need to count the null terminator
+ const int nameSpaceLength = nameSpace.size() + 1; // need to count the null terminator
std::string xmp = generateXmpForSecondaryImage(metadata_expected);
@@ -1383,10 +1377,10 @@ TEST(JpegRTest, writeXmpThenRead) {
}
class JpegRAPIEncodeAndDecodeTest
- : public ::testing::TestWithParam<std::tuple<ultrahdr_color_gamut, ultrahdr_color_gamut>> {
-public:
+ : public ::testing::TestWithParam<std::tuple<ultrahdr_color_gamut, ultrahdr_color_gamut>> {
+ public:
JpegRAPIEncodeAndDecodeTest()
- : mP010ColorGamut(std::get<0>(GetParam())), mYuv420ColorGamut(std::get<1>(GetParam())){};
+ : mP010ColorGamut(std::get<0>(GetParam())), mYuv420ColorGamut(std::get<1>(GetParam())){};
const ultrahdr_color_gamut mP010ColorGamut;
const ultrahdr_color_gamut mYuv420ColorGamut;
@@ -1402,10 +1396,10 @@ TEST_P(JpegRAPIEncodeAndDecodeTest, EncodeAPI0AndDecodeTest) {
UhdrCompressedStructWrapper jpgImg(kImageWidth, kImageHeight);
ASSERT_TRUE(jpgImg.allocateMemory());
JpegR uHdrLib;
- ASSERT_EQ(uHdrLib.encodeJPEGR(rawImg.getImageHandle(),
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg.getImageHandle(), kQuality, nullptr),
- OK);
+ ASSERT_EQ(
+ uHdrLib.encodeJPEGR(rawImg.getImageHandle(), ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle(), kQuality, nullptr),
+ JPEGR_NO_ERROR);
// encode with luma stride set
{
UhdrUnCompressedStructWrapper rawImg2(kImageWidth, kImageHeight, YCbCr_p010);
@@ -1415,10 +1409,10 @@ TEST_P(JpegRAPIEncodeAndDecodeTest, EncodeAPI0AndDecodeTest) {
ASSERT_TRUE(rawImg2.loadRawResource(kYCbCrP010FileName));
UhdrCompressedStructWrapper jpgImg2(kImageWidth, kImageHeight);
ASSERT_TRUE(jpgImg2.allocateMemory());
- ASSERT_EQ(uHdrLib.encodeJPEGR(rawImg2.getImageHandle(),
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg2.getImageHandle(), kQuality, nullptr),
- OK);
+ ASSERT_EQ(
+ uHdrLib.encodeJPEGR(rawImg2.getImageHandle(), ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg2.getImageHandle(), kQuality, nullptr),
+ JPEGR_NO_ERROR);
auto jpg1 = jpgImg.getImageHandle();
auto jpg2 = jpgImg2.getImageHandle();
ASSERT_EQ(jpg1->length, jpg2->length);
@@ -1434,10 +1428,10 @@ TEST_P(JpegRAPIEncodeAndDecodeTest, EncodeAPI0AndDecodeTest) {
ASSERT_TRUE(rawImg2.loadRawResource(kYCbCrP010FileName));
UhdrCompressedStructWrapper jpgImg2(kImageWidth, kImageHeight);
ASSERT_TRUE(jpgImg2.allocateMemory());
- ASSERT_EQ(uHdrLib.encodeJPEGR(rawImg2.getImageHandle(),
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg2.getImageHandle(), kQuality, nullptr),
- OK);
+ ASSERT_EQ(
+ uHdrLib.encodeJPEGR(rawImg2.getImageHandle(), ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg2.getImageHandle(), kQuality, nullptr),
+ JPEGR_NO_ERROR);
auto jpg1 = jpgImg.getImageHandle();
auto jpg2 = jpgImg2.getImageHandle();
ASSERT_EQ(jpg1->length, jpg2->length);
@@ -1453,10 +1447,10 @@ TEST_P(JpegRAPIEncodeAndDecodeTest, EncodeAPI0AndDecodeTest) {
ASSERT_TRUE(rawImg2.loadRawResource(kYCbCrP010FileName));
UhdrCompressedStructWrapper jpgImg2(kImageWidth, kImageHeight);
ASSERT_TRUE(jpgImg2.allocateMemory());
- ASSERT_EQ(uHdrLib.encodeJPEGR(rawImg2.getImageHandle(),
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg2.getImageHandle(), kQuality, nullptr),
- OK);
+ ASSERT_EQ(
+ uHdrLib.encodeJPEGR(rawImg2.getImageHandle(), ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg2.getImageHandle(), kQuality, nullptr),
+ JPEGR_NO_ERROR);
auto jpg1 = jpgImg.getImageHandle();
auto jpg2 = jpgImg2.getImageHandle();
ASSERT_EQ(jpg1->length, jpg2->length);
@@ -1471,10 +1465,10 @@ TEST_P(JpegRAPIEncodeAndDecodeTest, EncodeAPI0AndDecodeTest) {
ASSERT_TRUE(rawImg2.loadRawResource(kYCbCrP010FileName));
UhdrCompressedStructWrapper jpgImg2(kImageWidth, kImageHeight);
ASSERT_TRUE(jpgImg2.allocateMemory());
- ASSERT_EQ(uHdrLib.encodeJPEGR(rawImg2.getImageHandle(),
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg2.getImageHandle(), kQuality, nullptr),
- OK);
+ ASSERT_EQ(
+ uHdrLib.encodeJPEGR(rawImg2.getImageHandle(), ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg2.getImageHandle(), kQuality, nullptr),
+ JPEGR_NO_ERROR);
auto jpg1 = jpgImg.getImageHandle();
auto jpg2 = jpgImg2.getImageHandle();
ASSERT_EQ(jpg1->length, jpg2->length);
@@ -1507,7 +1501,7 @@ TEST_P(JpegRAPIEncodeAndDecodeTest, EncodeAPI1AndDecodeTest) {
ASSERT_EQ(uHdrLib.encodeJPEGR(rawImgP010.getImageHandle(), rawImg420.getImageHandle(),
ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
jpgImg.getImageHandle(), kQuality, nullptr),
- OK);
+ JPEGR_NO_ERROR);
// encode with luma stride set p010
{
UhdrUnCompressedStructWrapper rawImg2P010(kImageWidth, kImageHeight, YCbCr_p010);
@@ -1520,7 +1514,7 @@ TEST_P(JpegRAPIEncodeAndDecodeTest, EncodeAPI1AndDecodeTest) {
ASSERT_EQ(uHdrLib.encodeJPEGR(rawImg2P010.getImageHandle(), rawImg420.getImageHandle(),
ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
jpgImg2.getImageHandle(), kQuality, nullptr),
- OK);
+ JPEGR_NO_ERROR);
auto jpg1 = jpgImg.getImageHandle();
auto jpg2 = jpgImg2.getImageHandle();
ASSERT_EQ(jpg1->length, jpg2->length);
@@ -1539,7 +1533,7 @@ TEST_P(JpegRAPIEncodeAndDecodeTest, EncodeAPI1AndDecodeTest) {
ASSERT_EQ(uHdrLib.encodeJPEGR(rawImg2P010.getImageHandle(), rawImg420.getImageHandle(),
ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
jpgImg2.getImageHandle(), kQuality, nullptr),
- OK);
+ JPEGR_NO_ERROR);
auto jpg1 = jpgImg.getImageHandle();
auto jpg2 = jpgImg2.getImageHandle();
ASSERT_EQ(jpg1->length, jpg2->length);
@@ -1558,7 +1552,7 @@ TEST_P(JpegRAPIEncodeAndDecodeTest, EncodeAPI1AndDecodeTest) {
ASSERT_EQ(uHdrLib.encodeJPEGR(rawImg2P010.getImageHandle(), rawImg420.getImageHandle(),
ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
jpgImg2.getImageHandle(), kQuality, nullptr),
- OK);
+ JPEGR_NO_ERROR);
auto jpg1 = jpgImg.getImageHandle();
auto jpg2 = jpgImg2.getImageHandle();
ASSERT_EQ(jpg1->length, jpg2->length);
@@ -1576,7 +1570,7 @@ TEST_P(JpegRAPIEncodeAndDecodeTest, EncodeAPI1AndDecodeTest) {
ASSERT_EQ(uHdrLib.encodeJPEGR(rawImg2P010.getImageHandle(), rawImg420.getImageHandle(),
ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
jpgImg2.getImageHandle(), kQuality, nullptr),
- OK);
+ JPEGR_NO_ERROR);
auto jpg1 = jpgImg.getImageHandle();
auto jpg2 = jpgImg2.getImageHandle();
ASSERT_EQ(jpg1->length, jpg2->length);
@@ -1594,7 +1588,7 @@ TEST_P(JpegRAPIEncodeAndDecodeTest, EncodeAPI1AndDecodeTest) {
ASSERT_EQ(uHdrLib.encodeJPEGR(rawImgP010.getImageHandle(), rawImg2420.getImageHandle(),
ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
jpgImg2.getImageHandle(), kQuality, nullptr),
- OK);
+ JPEGR_NO_ERROR);
auto jpg1 = jpgImg.getImageHandle();
auto jpg2 = jpgImg2.getImageHandle();
ASSERT_EQ(jpg1->length, jpg2->length);
@@ -1613,7 +1607,7 @@ TEST_P(JpegRAPIEncodeAndDecodeTest, EncodeAPI1AndDecodeTest) {
ASSERT_EQ(uHdrLib.encodeJPEGR(rawImgP010.getImageHandle(), rawImg2420.getImageHandle(),
ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
jpgImg2.getImageHandle(), kQuality, nullptr),
- OK);
+ JPEGR_NO_ERROR);
auto jpg1 = jpgImg.getImageHandle();
auto jpg2 = jpgImg2.getImageHandle();
ASSERT_EQ(jpg1->length, jpg2->length);
@@ -1632,7 +1626,7 @@ TEST_P(JpegRAPIEncodeAndDecodeTest, EncodeAPI1AndDecodeTest) {
ASSERT_EQ(uHdrLib.encodeJPEGR(rawImgP010.getImageHandle(), rawImg2420.getImageHandle(),
ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
jpgImg2.getImageHandle(), kQuality, nullptr),
- OK);
+ JPEGR_NO_ERROR);
auto jpg1 = jpgImg.getImageHandle();
auto jpg2 = jpgImg2.getImageHandle();
ASSERT_EQ(jpg1->length, jpg2->length);
@@ -1650,7 +1644,7 @@ TEST_P(JpegRAPIEncodeAndDecodeTest, EncodeAPI1AndDecodeTest) {
ASSERT_EQ(uHdrLib.encodeJPEGR(rawImgP010.getImageHandle(), rawImg2420.getImageHandle(),
ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
jpgImg2.getImageHandle(), kQuality, nullptr),
- OK);
+ JPEGR_NO_ERROR);
auto jpg1 = jpgImg.getImageHandle();
auto jpg2 = jpgImg2.getImageHandle();
ASSERT_EQ(jpg1->length, jpg2->length);
@@ -1685,10 +1679,10 @@ TEST_P(JpegRAPIEncodeAndDecodeTest, EncodeAPI2AndDecodeTest) {
auto sdr = jpgSdr.getImageHandle();
ASSERT_TRUE(readFile(kSdrJpgFileName, sdr->data, sdr->maxLength, sdr->length));
JpegR uHdrLib;
- ASSERT_EQ(uHdrLib.encodeJPEGR(rawImgP010.getImageHandle(), rawImg420.getImageHandle(), sdr,
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg.getImageHandle()),
- OK);
+ ASSERT_EQ(
+ uHdrLib.encodeJPEGR(rawImgP010.getImageHandle(), rawImg420.getImageHandle(), sdr,
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG, jpgImg.getImageHandle()),
+ JPEGR_NO_ERROR);
// encode with luma stride set
{
UhdrUnCompressedStructWrapper rawImg2P010(kImageWidth, kImageHeight, YCbCr_p010);
@@ -1698,10 +1692,10 @@ TEST_P(JpegRAPIEncodeAndDecodeTest, EncodeAPI2AndDecodeTest) {
ASSERT_TRUE(rawImg2P010.loadRawResource(kYCbCrP010FileName));
UhdrCompressedStructWrapper jpgImg2(kImageWidth, kImageHeight);
ASSERT_TRUE(jpgImg2.allocateMemory());
- ASSERT_EQ(uHdrLib.encodeJPEGR(rawImg2P010.getImageHandle(), rawImg420.getImageHandle(), sdr,
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg2.getImageHandle()),
- OK);
+ ASSERT_EQ(
+ uHdrLib.encodeJPEGR(rawImg2P010.getImageHandle(), rawImg420.getImageHandle(), sdr,
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG, jpgImg2.getImageHandle()),
+ JPEGR_NO_ERROR);
auto jpg1 = jpgImg.getImageHandle();
auto jpg2 = jpgImg2.getImageHandle();
ASSERT_EQ(jpg1->length, jpg2->length);
@@ -1717,10 +1711,10 @@ TEST_P(JpegRAPIEncodeAndDecodeTest, EncodeAPI2AndDecodeTest) {
ASSERT_TRUE(rawImg2P010.loadRawResource(kYCbCrP010FileName));
UhdrCompressedStructWrapper jpgImg2(kImageWidth, kImageHeight);
ASSERT_TRUE(jpgImg2.allocateMemory());
- ASSERT_EQ(uHdrLib.encodeJPEGR(rawImg2P010.getImageHandle(), rawImg420.getImageHandle(), sdr,
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg2.getImageHandle()),
- OK);
+ ASSERT_EQ(
+ uHdrLib.encodeJPEGR(rawImg2P010.getImageHandle(), rawImg420.getImageHandle(), sdr,
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG, jpgImg2.getImageHandle()),
+ JPEGR_NO_ERROR);
auto jpg1 = jpgImg.getImageHandle();
auto jpg2 = jpgImg2.getImageHandle();
ASSERT_EQ(jpg1->length, jpg2->length);
@@ -1736,10 +1730,10 @@ TEST_P(JpegRAPIEncodeAndDecodeTest, EncodeAPI2AndDecodeTest) {
ASSERT_TRUE(rawImg2P010.loadRawResource(kYCbCrP010FileName));
UhdrCompressedStructWrapper jpgImg2(kImageWidth, kImageHeight);
ASSERT_TRUE(jpgImg2.allocateMemory());
- ASSERT_EQ(uHdrLib.encodeJPEGR(rawImg2P010.getImageHandle(), rawImg420.getImageHandle(), sdr,
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg2.getImageHandle()),
- OK);
+ ASSERT_EQ(
+ uHdrLib.encodeJPEGR(rawImg2P010.getImageHandle(), rawImg420.getImageHandle(), sdr,
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG, jpgImg2.getImageHandle()),
+ JPEGR_NO_ERROR);
auto jpg1 = jpgImg.getImageHandle();
auto jpg2 = jpgImg2.getImageHandle();
ASSERT_EQ(jpg1->length, jpg2->length);
@@ -1754,10 +1748,10 @@ TEST_P(JpegRAPIEncodeAndDecodeTest, EncodeAPI2AndDecodeTest) {
ASSERT_TRUE(rawImg2420.loadRawResource(kYCbCr420FileName));
UhdrCompressedStructWrapper jpgImg2(kImageWidth, kImageHeight);
ASSERT_TRUE(jpgImg2.allocateMemory());
- ASSERT_EQ(uHdrLib.encodeJPEGR(rawImgP010.getImageHandle(), rawImg2420.getImageHandle(), sdr,
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg2.getImageHandle()),
- OK);
+ ASSERT_EQ(
+ uHdrLib.encodeJPEGR(rawImgP010.getImageHandle(), rawImg2420.getImageHandle(), sdr,
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG, jpgImg2.getImageHandle()),
+ JPEGR_NO_ERROR);
auto jpg1 = jpgImg.getImageHandle();
auto jpg2 = jpgImg2.getImageHandle();
ASSERT_EQ(jpg1->length, jpg2->length);
@@ -1773,10 +1767,10 @@ TEST_P(JpegRAPIEncodeAndDecodeTest, EncodeAPI2AndDecodeTest) {
ASSERT_TRUE(rawImg2420.loadRawResource(kYCbCr420FileName));
UhdrCompressedStructWrapper jpgImg2(kImageWidth, kImageHeight);
ASSERT_TRUE(jpgImg2.allocateMemory());
- ASSERT_EQ(uHdrLib.encodeJPEGR(rawImgP010.getImageHandle(), rawImg2420.getImageHandle(), sdr,
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg2.getImageHandle()),
- OK);
+ ASSERT_EQ(
+ uHdrLib.encodeJPEGR(rawImgP010.getImageHandle(), rawImg2420.getImageHandle(), sdr,
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG, jpgImg2.getImageHandle()),
+ JPEGR_NO_ERROR);
auto jpg1 = jpgImg.getImageHandle();
auto jpg2 = jpgImg2.getImageHandle();
ASSERT_EQ(jpg1->length, jpg2->length);
@@ -1792,10 +1786,10 @@ TEST_P(JpegRAPIEncodeAndDecodeTest, EncodeAPI2AndDecodeTest) {
ASSERT_TRUE(rawImg2420.loadRawResource(kYCbCr420FileName));
UhdrCompressedStructWrapper jpgImg2(kImageWidth, kImageHeight);
ASSERT_TRUE(jpgImg2.allocateMemory());
- ASSERT_EQ(uHdrLib.encodeJPEGR(rawImgP010.getImageHandle(), rawImg2420.getImageHandle(), sdr,
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg2.getImageHandle()),
- OK);
+ ASSERT_EQ(
+ uHdrLib.encodeJPEGR(rawImgP010.getImageHandle(), rawImg2420.getImageHandle(), sdr,
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG, jpgImg2.getImageHandle()),
+ JPEGR_NO_ERROR);
auto jpg1 = jpgImg.getImageHandle();
auto jpg2 = jpgImg2.getImageHandle();
ASSERT_EQ(jpg1->length, jpg2->length);
@@ -1826,10 +1820,10 @@ TEST_P(JpegRAPIEncodeAndDecodeTest, EncodeAPI3AndDecodeTest) {
auto sdr = jpgSdr.getImageHandle();
ASSERT_TRUE(readFile(kSdrJpgFileName, sdr->data, sdr->maxLength, sdr->length));
JpegR uHdrLib;
- ASSERT_EQ(uHdrLib.encodeJPEGR(rawImgP010.getImageHandle(), sdr,
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg.getImageHandle()),
- OK);
+ ASSERT_EQ(
+ uHdrLib.encodeJPEGR(rawImgP010.getImageHandle(), sdr,
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG, jpgImg.getImageHandle()),
+ JPEGR_NO_ERROR);
// encode with luma stride set
{
UhdrUnCompressedStructWrapper rawImg2P010(kImageWidth, kImageHeight, YCbCr_p010);
@@ -1839,10 +1833,10 @@ TEST_P(JpegRAPIEncodeAndDecodeTest, EncodeAPI3AndDecodeTest) {
ASSERT_TRUE(rawImg2P010.loadRawResource(kYCbCrP010FileName));
UhdrCompressedStructWrapper jpgImg2(kImageWidth, kImageHeight);
ASSERT_TRUE(jpgImg2.allocateMemory());
- ASSERT_EQ(uHdrLib.encodeJPEGR(rawImg2P010.getImageHandle(), sdr,
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg2.getImageHandle()),
- OK);
+ ASSERT_EQ(
+ uHdrLib.encodeJPEGR(rawImg2P010.getImageHandle(), sdr,
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG, jpgImg2.getImageHandle()),
+ JPEGR_NO_ERROR);
auto jpg1 = jpgImg.getImageHandle();
auto jpg2 = jpgImg2.getImageHandle();
ASSERT_EQ(jpg1->length, jpg2->length);
@@ -1858,10 +1852,10 @@ TEST_P(JpegRAPIEncodeAndDecodeTest, EncodeAPI3AndDecodeTest) {
ASSERT_TRUE(rawImg2P010.loadRawResource(kYCbCrP010FileName));
UhdrCompressedStructWrapper jpgImg2(kImageWidth, kImageHeight);
ASSERT_TRUE(jpgImg2.allocateMemory());
- ASSERT_EQ(uHdrLib.encodeJPEGR(rawImg2P010.getImageHandle(), sdr,
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg2.getImageHandle()),
- OK);
+ ASSERT_EQ(
+ uHdrLib.encodeJPEGR(rawImg2P010.getImageHandle(), sdr,
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG, jpgImg2.getImageHandle()),
+ JPEGR_NO_ERROR);
auto jpg1 = jpgImg.getImageHandle();
auto jpg2 = jpgImg2.getImageHandle();
ASSERT_EQ(jpg1->length, jpg2->length);
@@ -1877,10 +1871,10 @@ TEST_P(JpegRAPIEncodeAndDecodeTest, EncodeAPI3AndDecodeTest) {
ASSERT_TRUE(rawImg2P010.loadRawResource(kYCbCrP010FileName));
UhdrCompressedStructWrapper jpgImg2(kImageWidth, kImageHeight);
ASSERT_TRUE(jpgImg2.allocateMemory());
- ASSERT_EQ(uHdrLib.encodeJPEGR(rawImg2P010.getImageHandle(), sdr,
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg2.getImageHandle()),
- OK);
+ ASSERT_EQ(
+ uHdrLib.encodeJPEGR(rawImg2P010.getImageHandle(), sdr,
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG, jpgImg2.getImageHandle()),
+ JPEGR_NO_ERROR);
auto jpg1 = jpgImg.getImageHandle();
auto jpg2 = jpgImg2.getImageHandle();
ASSERT_EQ(jpg1->length, jpg2->length);
@@ -1895,10 +1889,10 @@ TEST_P(JpegRAPIEncodeAndDecodeTest, EncodeAPI3AndDecodeTest) {
ASSERT_TRUE(rawImg2P010.loadRawResource(kYCbCrP010FileName));
UhdrCompressedStructWrapper jpgImg2(kImageWidth, kImageHeight);
ASSERT_TRUE(jpgImg2.allocateMemory());
- ASSERT_EQ(uHdrLib.encodeJPEGR(rawImg2P010.getImageHandle(), sdr,
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg2.getImageHandle()),
- OK);
+ ASSERT_EQ(
+ uHdrLib.encodeJPEGR(rawImg2P010.getImageHandle(), sdr,
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG, jpgImg2.getImageHandle()),
+ JPEGR_NO_ERROR);
auto jpg1 = jpgImg.getImageHandle();
auto jpg2 = jpgImg2.getImageHandle();
ASSERT_EQ(jpg1->length, jpg2->length);
@@ -1917,18 +1911,37 @@ TEST_P(JpegRAPIEncodeAndDecodeTest, EncodeAPI3AndDecodeTest) {
}
INSTANTIATE_TEST_SUITE_P(
- JpegRAPIParameterizedTests, JpegRAPIEncodeAndDecodeTest,
- ::testing::Combine(::testing::Values(ULTRAHDR_COLORGAMUT_BT709, ULTRAHDR_COLORGAMUT_P3,
- ULTRAHDR_COLORGAMUT_BT2100),
- ::testing::Values(ULTRAHDR_COLORGAMUT_BT709, ULTRAHDR_COLORGAMUT_P3,
- ULTRAHDR_COLORGAMUT_BT2100)));
+ JpegRAPIParameterizedTests, JpegRAPIEncodeAndDecodeTest,
+ ::testing::Combine(::testing::Values(ULTRAHDR_COLORGAMUT_BT709, ULTRAHDR_COLORGAMUT_P3,
+ ULTRAHDR_COLORGAMUT_BT2100),
+ ::testing::Values(ULTRAHDR_COLORGAMUT_BT709, ULTRAHDR_COLORGAMUT_P3,
+ ULTRAHDR_COLORGAMUT_BT2100)));
// ============================================================================
// Profiling
// ============================================================================
+#ifdef _WIN32
+class Profiler {
+ public:
+ void timerStart() { QueryPerformanceCounter(&mStartingTime); }
+
+ void timerStop() { QueryPerformanceCounter(&mEndingTime); }
+ int64_t elapsedTime() {
+ LARGE_INTEGER frequency;
+ LARGE_INTEGER elapsedMicroseconds;
+ QueryPerformanceFrequency(&frequency);
+ elapsedMicroseconds.QuadPart = mEndingTime.QuadPart - mStartingTime.QuadPart;
+ return (double)elapsedMicroseconds.QuadPart / (double)frequency.QuadPart * 1000000;
+ }
+
+ private:
+ LARGE_INTEGER mStartingTime;
+ LARGE_INTEGER mEndingTime;
+};
+#else
class Profiler {
-public:
+ public:
void timerStart() { gettimeofday(&mStartingTime, nullptr); }
void timerStop() { gettimeofday(&mEndingTime, nullptr); }
@@ -1940,19 +1953,20 @@ public:
return elapsedMicroseconds.tv_sec * 1000000 + elapsedMicroseconds.tv_usec;
}
-private:
+ private:
struct timeval mStartingTime;
struct timeval mEndingTime;
};
+#endif
class JpegRBenchmark : public JpegR {
-public:
+ public:
void BenchmarkGenerateGainMap(jr_uncompressed_ptr yuv420Image, jr_uncompressed_ptr p010Image,
ultrahdr_metadata_ptr metadata, jr_uncompressed_ptr map);
void BenchmarkApplyGainMap(jr_uncompressed_ptr yuv420Image, jr_uncompressed_ptr map,
ultrahdr_metadata_ptr metadata, jr_uncompressed_ptr dest);
-private:
+ private:
const int kProfileCount = 10;
};
@@ -1965,7 +1979,7 @@ void JpegRBenchmark::BenchmarkGenerateGainMap(jr_uncompressed_ptr yuv420Image,
Profiler profileGenerateMap;
profileGenerateMap.timerStart();
for (auto i = 0; i < kProfileCount; i++) {
- ASSERT_EQ(OK,
+ ASSERT_EQ(JPEGR_NO_ERROR,
generateGainMap(yuv420Image, p010Image, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
metadata, map));
if (i != kProfileCount - 1) {
@@ -1975,7 +1989,7 @@ void JpegRBenchmark::BenchmarkGenerateGainMap(jr_uncompressed_ptr yuv420Image,
}
profileGenerateMap.timerStop();
ALOGE("Generate Gain Map:- Res = %zu x %zu, time = %f ms", yuv420Image->width,
- yuv420Image->height, profileGenerateMap.elapsedTime() / (kProfileCount * 1000.f));
+ yuv420Image->height, profileGenerateMap.elapsedTime() / (kProfileCount * 1000.f));
}
void JpegRBenchmark::BenchmarkApplyGainMap(jr_uncompressed_ptr yuv420Image, jr_uncompressed_ptr map,
@@ -1984,9 +1998,8 @@ void JpegRBenchmark::BenchmarkApplyGainMap(jr_uncompressed_ptr yuv420Image, jr_u
Profiler profileRecMap;
profileRecMap.timerStart();
for (auto i = 0; i < kProfileCount; i++) {
- ASSERT_EQ(OK,
- applyGainMap(yuv420Image, map, metadata, ULTRAHDR_OUTPUT_HDR_HLG,
- metadata->maxContentBoost /* displayBoost */, dest));
+ ASSERT_EQ(JPEGR_NO_ERROR, applyGainMap(yuv420Image, map, metadata, ULTRAHDR_OUTPUT_HDR_HLG,
+ metadata->maxContentBoost /* displayBoost */, dest));
}
profileRecMap.timerStop();
ALOGE("Apply Gain Map:- Res = %zu x %zu, time = %f ms", yuv420Image->width, yuv420Image->height,
@@ -2002,11 +2015,13 @@ TEST(JpegRTest, ProfileGainMapFuncs) {
ASSERT_TRUE(rawImg420.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT709));
ASSERT_TRUE(rawImg420.allocateMemory());
ASSERT_TRUE(rawImg420.loadRawResource(kYCbCr420FileName));
- ultrahdr_metadata_struct metadata = {.version = "1.0"};
- jpegr_uncompressed_struct map = {.data = NULL,
- .width = 0,
- .height = 0,
- .colorGamut = ULTRAHDR_COLORGAMUT_UNSPECIFIED};
+ ultrahdr_metadata_struct metadata;
+ metadata.version = kJpegrVersion;
+ jpegr_uncompressed_struct map;
+ map.data = NULL;
+ map.width = 0;
+ map.height = 0;
+ map.colorGamut = ULTRAHDR_COLORGAMUT_UNSPECIFIED;
{
auto rawImg = rawImgP010.getImageHandle();
if (rawImg->luma_stride == 0) rawImg->luma_stride = rawImg->width;
@@ -2027,19 +2042,19 @@ TEST(JpegRTest, ProfileGainMapFuncs) {
}
JpegRBenchmark benchmark;
- ASSERT_NO_FATAL_FAILURE(benchmark.BenchmarkGenerateGainMap(rawImg420.getImageHandle(),
- rawImgP010.getImageHandle(), &metadata,
- &map));
+ ASSERT_NO_FATAL_FAILURE(benchmark.BenchmarkGenerateGainMap(
+ rawImg420.getImageHandle(), rawImgP010.getImageHandle(), &metadata, &map));
const int dstSize = kImageWidth * kImageWidth * 4;
auto bufferDst = std::make_unique<uint8_t[]>(dstSize);
- jpegr_uncompressed_struct dest = {.data = bufferDst.get(),
- .width = 0,
- .height = 0,
- .colorGamut = ULTRAHDR_COLORGAMUT_UNSPECIFIED};
+ jpegr_uncompressed_struct dest;
+ dest.data = bufferDst.get();
+ dest.width = 0;
+ dest.height = 0;
+ dest.colorGamut = ULTRAHDR_COLORGAMUT_UNSPECIFIED;
ASSERT_NO_FATAL_FAILURE(
- benchmark.BenchmarkApplyGainMap(rawImg420.getImageHandle(), &map, &metadata, &dest));
+ benchmark.BenchmarkApplyGainMap(rawImg420.getImageHandle(), &map, &metadata, &dest));
if (map.data) {
delete[] static_cast<uint8_t*>(map.data);
@@ -2047,4 +2062,4 @@ TEST(JpegRTest, ProfileGainMapFuncs) {
}
}
-} // namespace ultrahdr
+} // namespace ultrahdr
diff --git a/tests/ultrahdr_app.cpp b/tests/ultrahdr_app.cpp
deleted file mode 100644
index ca237c7..0000000
--- a/tests/ultrahdr_app.cpp
+++ /dev/null
@@ -1,921 +0,0 @@
-/*
- * Copyright 2023 The Android Open Source Project
- *
- * 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.
- */
-
-#include <sys/time.h>
-#include <unistd.h>
-
-#include <algorithm>
-#include <cmath>
-#include <fstream>
-#include <iostream>
-
-#include "ultrahdr/ultrahdrcommon.h"
-#include "ultrahdr/gainmapmath.h"
-#include "ultrahdr/jpegr.h"
-
-using namespace ultrahdr;
-
-const float BT601YUVtoRGBMatrix[9] =
- {1, 0, 1.402, 1, (-0.202008 / 0.587), (-0.419198 / 0.587), 1.0, 1.772, 0.0};
-const float BT709YUVtoRGBMatrix[9] =
- {1, 0, 1.5748, 1, (-0.13397432 / 0.7152), (-0.33480248 / 0.7152), 1.0, 1.8556, 0.0};
-const float BT2020YUVtoRGBMatrix[9] =
- {1, 0, 1.4746, 1, (-0.11156702 / 0.6780), (-0.38737742 / 0.6780), 1, 1.8814, 0};
-
-const float BT601RGBtoYUVMatrix[9] = {0.299,
- 0.587,
- 0.114,
- (-0.299 / 1.772),
- (-0.587 / 1.772),
- 0.5,
- 0.5,
- (-0.587 / 1.402),
- (-0.114 / 1.402)};
-const float BT709RGBtoYUVMatrix[9] = {0.2126,
- 0.7152,
- 0.0722,
- (-0.2126 / 1.8556),
- (-0.7152 / 1.8556),
- 0.5,
- 0.5,
- (-0.7152 / 1.5748),
- (-0.0722 / 1.5748)};
-const float BT2020RGBtoYUVMatrix[9] = {0.2627,
- 0.6780,
- 0.0593,
- (-0.2627 / 1.8814),
- (-0.6780 / 1.8814),
- 0.5,
- 0.5,
- (-0.6780 / 1.4746),
- (-0.0593 / 1.4746)};
-
-//#define PROFILE_ENABLE 1
-class Profiler {
-public:
- void timerStart() { gettimeofday(&mStartingTime, nullptr); }
-
- void timerStop() { gettimeofday(&mEndingTime, nullptr); }
-
- int64_t elapsedTime() {
- struct timeval elapsedMicroseconds;
- elapsedMicroseconds.tv_sec = mEndingTime.tv_sec - mStartingTime.tv_sec;
- elapsedMicroseconds.tv_usec = mEndingTime.tv_usec - mStartingTime.tv_usec;
- return elapsedMicroseconds.tv_sec * 1000000 + elapsedMicroseconds.tv_usec;
- }
-
-private:
- struct timeval mStartingTime;
- struct timeval mEndingTime;
-};
-
-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) {
- std::cerr << "requested to read " << length << " bytes from file : " << filename
- << ", file contains only " << size << " bytes" << std::endl;
- return false;
- }
- ifd.seekg(0, std::ios::beg);
- result = malloc(length);
- if (result == nullptr) {
- std::cerr << "failed to allocate memory to store contents of file : " << filename
- << std::endl;
- return false;
- }
- ifd.read(static_cast<char*>(result), length);
- return true;
- }
- std::cerr << "unable to open file : " << filename << std::endl;
- return false;
-}
-
-static bool writeFile(const char* filename, void*& result, int length) {
- std::ofstream ofd(filename, std::ios::binary);
- if (ofd.is_open()) {
- ofd.write(static_cast<char*>(result), length);
- return true;
- }
- std::cerr << "unable to write to file : " << filename << std::endl;
- return false;
-}
-
-class UltraHdrAppInput {
-public:
- UltraHdrAppInput(const char* p010File, const char* yuv420File, const char* yuv420JpegFile,
- size_t width, size_t height,
- ultrahdr_color_gamut p010Cg = ULTRAHDR_COLORGAMUT_BT709,
- ultrahdr_color_gamut yuv420Cg = ULTRAHDR_COLORGAMUT_BT709,
- ultrahdr_transfer_function tf = ULTRAHDR_TF_HLG, int quality = 100,
- ultrahdr_output_format of = ULTRAHDR_OUTPUT_HDR_HLG)
- : mP010File(p010File),
- mYuv420File(yuv420File),
- mYuv420JpegFile(yuv420JpegFile),
- mJpegRFile(nullptr),
- mWidth(width),
- mHeight(height),
- mP010Cg(p010Cg),
- mYuv420Cg(yuv420Cg),
- mTf(tf),
- mQuality(quality),
- mOf(of),
- mMode(0){};
-
- UltraHdrAppInput(const char* jpegRFile, ultrahdr_output_format of = ULTRAHDR_OUTPUT_HDR_HLG)
- : mP010File(nullptr),
- mYuv420File(nullptr),
- mJpegRFile(jpegRFile),
- mWidth(0),
- mHeight(0),
- mP010Cg(ULTRAHDR_COLORGAMUT_UNSPECIFIED),
- mYuv420Cg(ULTRAHDR_COLORGAMUT_UNSPECIFIED),
- mTf(ULTRAHDR_TF_UNSPECIFIED),
- mQuality(100),
- mOf(of),
- mMode(1){};
-
- ~UltraHdrAppInput() {
- if (mRawP010Image.data) free(mRawP010Image.data);
- if (mRawP010Image.chroma_data) free(mRawP010Image.chroma_data);
- if (mRawRgba1010102Image.data) free(mRawRgba1010102Image.data);
- if (mRawRgba1010102Image.chroma_data) free(mRawRgba1010102Image.chroma_data);
- if (mRawYuv420Image.data) free(mRawYuv420Image.data);
- if (mRawYuv420Image.chroma_data) free(mRawYuv420Image.chroma_data);
- if (mRawRgba8888Image.data) free(mRawRgba8888Image.data);
- if (mRawRgba8888Image.chroma_data) free(mRawRgba8888Image.chroma_data);
- if (mJpegImgR.data) free(mJpegImgR.data);
- if (mDestImage.data) free(mDestImage.data);
- if (mDestImage.chroma_data) free(mDestImage.chroma_data);
- if (mDestYUV444Image.data) free(mDestYUV444Image.data);
- if (mDestYUV444Image.chroma_data) free(mDestYUV444Image.chroma_data);
- }
-
- bool fillJpegRImageHandle();
- bool fillP010ImageHandle();
- bool convertP010ToRGBImage();
- bool fillYuv420ImageHandle();
- bool fillYuv420JpegImageHandle();
- bool convertYuv420ToRGBImage();
- bool convertRgba8888ToYUV444Image();
- bool convertRgba1010102ToYUV444Image();
- bool encode();
- bool decode();
- void computeRGBHdrPSNR();
- void computeRGBSdrPSNR();
- void computeYUVHdrPSNR();
- void computeYUVSdrPSNR();
-
- const char* mP010File;
- const char* mYuv420File;
- const char* mYuv420JpegFile;
- const char* mJpegRFile;
- const int mWidth;
- const int mHeight;
- const ultrahdr_color_gamut mP010Cg;
- const ultrahdr_color_gamut mYuv420Cg;
- const ultrahdr_transfer_function mTf;
- const int mQuality;
- const ultrahdr_output_format mOf;
- const int mMode;
- jpegr_uncompressed_struct mRawP010Image{};
- jpegr_uncompressed_struct mRawRgba1010102Image{};
- jpegr_uncompressed_struct mRawYuv420Image{};
- jpegr_compressed_struct mYuv420JpegImage{};
- jpegr_uncompressed_struct mRawRgba8888Image{};
- jpegr_compressed_struct mJpegImgR{};
- jpegr_uncompressed_struct mDestImage{};
- jpegr_uncompressed_struct mDestYUV444Image{};
- double mPsnr[3]{};
-};
-
-bool UltraHdrAppInput::fillP010ImageHandle() {
- const int bpp = 2;
- int p010Size = mWidth * mHeight * bpp * 1.5;
- mRawP010Image.width = mWidth;
- mRawP010Image.height = mHeight;
- mRawP010Image.colorGamut = mP010Cg;
- return loadFile(mP010File, mRawP010Image.data, p010Size);
-}
-
-bool UltraHdrAppInput::fillYuv420ImageHandle() {
- int yuv420Size = mWidth * mHeight * 1.5;
- mRawYuv420Image.width = mWidth;
- mRawYuv420Image.height = mHeight;
- mRawYuv420Image.colorGamut = mYuv420Cg;
- return loadFile(mYuv420File, mRawYuv420Image.data, yuv420Size);
-}
-
-bool UltraHdrAppInput::fillYuv420JpegImageHandle() {
- std::ifstream ifd(mYuv420JpegFile, std::ios::binary | std::ios::ate);
- if (ifd.good()) {
- int size = ifd.tellg();
- mYuv420JpegImage.length = size;
- mYuv420JpegImage.maxLength = size;
- mYuv420JpegImage.data = nullptr;
- mYuv420JpegImage.colorGamut = mYuv420Cg;
- ifd.close();
- return loadFile(mYuv420JpegFile, mYuv420JpegImage.data, size);
- }
- return false;
-}
-
-bool UltraHdrAppInput::fillJpegRImageHandle() {
- std::ifstream ifd(mJpegRFile, std::ios::binary | std::ios::ate);
- if (ifd.good()) {
- int size = ifd.tellg();
- mJpegImgR.length = size;
- mJpegImgR.maxLength = size;
- mJpegImgR.data = nullptr;
- mJpegImgR.colorGamut = mYuv420Cg;
- ifd.close();
- return loadFile(mJpegRFile, mJpegImgR.data, size);
- }
- return false;
-}
-
-bool UltraHdrAppInput::encode() {
- if (!fillP010ImageHandle()) return false;
- if (mYuv420File != nullptr && !fillYuv420ImageHandle()) return false;
- if (mYuv420JpegFile != nullptr && !fillYuv420JpegImageHandle()) return false;
-
- mJpegImgR.maxLength = std::max(static_cast<size_t>(8 * 1024) /* min size 8kb */,
- mRawP010Image.width * mRawP010Image.height * 3 * 2);
- mJpegImgR.data = malloc(mJpegImgR.maxLength);
- if (mJpegImgR.data == nullptr) {
- std::cerr << "unable to allocate memory to store compressed image" << std::endl;
- return false;
- }
-
- JpegR jpegHdr;
- status_t status = UNKNOWN_ERROR;
-#ifdef PROFILE_ENABLE
- const int profileCount = 10;
- Profiler profileEncode;
- profileEncode.timerStart();
- for (auto i = 0; i < profileCount; i++) {
-#endif
- if (mYuv420File == nullptr && mYuv420JpegFile == nullptr) { // api-0
- status = jpegHdr.encodeJPEGR(&mRawP010Image, mTf, &mJpegImgR, mQuality, nullptr);
- if (OK != status) {
- std::cerr << "Encountered error during encodeJPEGR call, error code " << status
- << std::endl;
- return false;
- }
- } else if (mYuv420File != nullptr && mYuv420JpegFile == nullptr) { // api-1
- status = jpegHdr.encodeJPEGR(&mRawP010Image, &mRawYuv420Image, mTf, &mJpegImgR, mQuality,
- nullptr);
- if (OK != status) {
- std::cerr << "Encountered error during encodeJPEGR call, error code " << status
- << std::endl;
- return false;
- }
- } else if (mYuv420File != nullptr && mYuv420JpegFile != nullptr) { // api-2
- status = jpegHdr.encodeJPEGR(&mRawP010Image, &mRawYuv420Image, &mYuv420JpegImage, mTf,
- &mJpegImgR);
- if (OK != status) {
- std::cerr << "Encountered error during encodeJPEGR call, error code " << status
- << std::endl;
- return false;
- }
- } else if (mYuv420File == nullptr && mYuv420JpegFile != nullptr) { // api-3
- status = jpegHdr.encodeJPEGR(&mRawP010Image, &mYuv420JpegImage, mTf, &mJpegImgR);
- if (OK != status) {
- std::cerr << "Encountered error during encodeJPEGR call, error code " << status
- << std::endl;
- return false;
- }
- }
-#ifdef PROFILE_ENABLE
- }
- profileEncode.timerStop();
- auto avgEncTime = profileEncode.elapsedTime() / (profileCount * 1000.f);
- printf("Average encode time for res %d x %d is %f ms \n", mWidth, mHeight, avgEncTime);
-#endif
- writeFile("out.jpeg", mJpegImgR.data, mJpegImgR.length);
- return true;
-}
-
-bool UltraHdrAppInput::decode() {
- if (mMode == 1 && !fillJpegRImageHandle()) return false;
- std::vector<uint8_t> iccData(0);
- std::vector<uint8_t> exifData(0);
- jpegr_info_struct info{0, 0, &iccData, &exifData};
- JpegR jpegHdr;
- status_t status = jpegHdr.getJPEGRInfo(&mJpegImgR, &info);
- if (OK == status) {
- size_t outSize = info.width * info.height * ((mOf == ULTRAHDR_OUTPUT_HDR_LINEAR) ? 8 : 4);
- mDestImage.data = malloc(outSize);
- if (mDestImage.data == nullptr) {
- std::cerr << "failed to allocate memory to store decoded output" << std::endl;
- return false;
- }
-#ifdef PROFILE_ENABLE
- const int profileCount = 10;
- Profiler profileDecode;
- profileDecode.timerStart();
- for (auto i = 0; i < profileCount; i++) {
-#endif
- status = jpegHdr.decodeJPEGR(&mJpegImgR, &mDestImage, FLT_MAX, nullptr, mOf, nullptr,
- nullptr);
- if (OK != status) {
- std::cerr << "Encountered error during decodeJPEGR call, error code " << status
- << std::endl;
- return false;
- }
-#ifdef PROFILE_ENABLE
- }
- profileDecode.timerStop();
- auto avgDecTime = profileDecode.elapsedTime() / (profileCount * 1000.f);
- printf("Average decode time for res %ld x %ld is %f ms \n", info.width, info.height,
- avgDecTime);
-#endif
- writeFile("outrgb.raw", mDestImage.data, outSize);
- } else {
- std::cerr << "Encountered error during getJPEGRInfo call, error code " << status
- << std::endl;
- return false;
- }
- return true;
-}
-
-bool UltraHdrAppInput::convertP010ToRGBImage() {
- const float* coeffs = BT2020YUVtoRGBMatrix;
- if (mP010Cg == ULTRAHDR_COLORGAMUT_BT709) {
- coeffs = BT709YUVtoRGBMatrix;
- } else if (mP010Cg == ULTRAHDR_COLORGAMUT_BT2100) {
- coeffs = BT2020YUVtoRGBMatrix;
- } else if (mP010Cg == ULTRAHDR_COLORGAMUT_P3) {
- coeffs = BT601YUVtoRGBMatrix;
- } else {
- std::cerr << "color matrix not present for gamut " << mP010Cg << " using BT2020Matrix"
- << std::endl;
- }
-
- mRawRgba1010102Image.data = malloc(mRawP010Image.width * mRawP010Image.height * 4);
- if (mRawRgba1010102Image.data == nullptr) {
- std::cerr << "failed to allocate memory to store Rgba1010102" << std::endl;
- return false;
- }
- mRawRgba1010102Image.width = mRawP010Image.width;
- mRawRgba1010102Image.height = mRawP010Image.height;
- mRawRgba1010102Image.colorGamut = mRawP010Image.colorGamut;
- uint32_t* rgbData = static_cast<uint32_t*>(mRawRgba1010102Image.data);
- uint16_t* y = static_cast<uint16_t*>(mRawP010Image.data);
- uint16_t* u = y + mRawP010Image.width * mRawP010Image.height;
- uint16_t* v = u + 1;
-
- for (size_t i = 0; i < mRawP010Image.height; i++) {
- for (size_t j = 0; j < mRawP010Image.width; j++) {
- float y0 = float(y[mRawP010Image.width * i + j] >> 6);
- float u0 = float(u[mRawP010Image.width * (i / 2) + (j / 2) * 2] >> 6);
- float v0 = float(v[mRawP010Image.width * (i / 2) + (j / 2) * 2] >> 6);
-
- y0 = CLIP3(y0, 64.0f, 940.0f);
- u0 = CLIP3(u0, 64.0f, 960.0f);
- v0 = CLIP3(v0, 64.0f, 960.0f);
-
- y0 = (y0 - 64.0f) / 876.0f;
- u0 = (u0 - 64.0f) / 896.0f - 0.5f;
- v0 = (v0 - 64.0f) / 896.0f - 0.5f;
-
- float r = coeffs[0] * y0 + coeffs[1] * u0 + coeffs[2] * v0;
- float g = coeffs[3] * y0 + coeffs[4] * u0 + coeffs[5] * v0;
- float b = coeffs[6] * y0 + coeffs[7] * u0 + coeffs[8] * v0;
-
- r = CLIP3(r * 1023.0f + 0.5f, 0.0f, 1023.0f);
- g = CLIP3(g * 1023.0f + 0.5f, 0.0f, 1023.0f);
- b = CLIP3(b * 1023.0f + 0.5f, 0.0f, 1023.0f);
-
- int32_t r0 = int32_t(r);
- int32_t g0 = int32_t(g);
- int32_t b0 = int32_t(b);
- *rgbData = (0x3ff & r0) | ((0x3ff & g0) << 10) | ((0x3ff & b0) << 20) |
- (0x3 << 30); // Set alpha to 1.0
-
- rgbData++;
- }
- }
- writeFile("inRgba1010102.raw", mRawRgba1010102Image.data,
- mRawP010Image.width * mRawP010Image.height * 4);
- return true;
-}
-
-bool UltraHdrAppInput::convertYuv420ToRGBImage() {
- mRawRgba8888Image.data = malloc(mRawYuv420Image.width * mRawYuv420Image.height * 4);
- if (mRawRgba8888Image.data == nullptr) {
- std::cerr << "failed to allocate memory to store rgba888" << std::endl;
- return false;
- }
- mRawRgba8888Image.width = mRawYuv420Image.width;
- mRawRgba8888Image.height = mRawYuv420Image.height;
- mRawRgba8888Image.colorGamut = mRawYuv420Image.colorGamut;
- uint32_t* rgbData = static_cast<uint32_t*>(mRawRgba8888Image.data);
- uint8_t* y = static_cast<uint8_t*>(mRawYuv420Image.data);
- uint8_t* u = y + (mRawYuv420Image.width * mRawYuv420Image.height);
- uint8_t* v = u + (mRawYuv420Image.width * mRawYuv420Image.height / 4);
-
- const float* coeffs = BT601YUVtoRGBMatrix;
- for (size_t i = 0; i < mRawYuv420Image.height; i++) {
- for (size_t j = 0; j < mRawYuv420Image.width; j++) {
- float y0 = float(y[mRawYuv420Image.width * i + j]);
- float u0 = float(u[mRawYuv420Image.width / 2 * (i / 2) + (j / 2)] - 128);
- float v0 = float(v[mRawYuv420Image.width / 2 * (i / 2) + (j / 2)] - 128);
-
- y0 /= 255.0f;
- u0 /= 255.0f;
- v0 /= 255.0f;
-
- float r = coeffs[0] * y0 + coeffs[1] * u0 + coeffs[2] * v0;
- float g = coeffs[3] * y0 + coeffs[4] * u0 + coeffs[5] * v0;
- float b = coeffs[6] * y0 + coeffs[7] * u0 + coeffs[8] * v0;
-
- r = r * 255.0f + 0.5f;
- g = g * 255.0f + 0.5f;
- b = b * 255.0f + 0.5f;
-
- r = CLIP3(r, 0.0f, 255.0f);
- g = CLIP3(g, 0.0f, 255.0f);
- b = CLIP3(b, 0.0f, 255.0f);
-
- int32_t r0 = int32_t(r);
- int32_t g0 = int32_t(g);
- int32_t b0 = int32_t(b);
- *rgbData = r0 | (g0 << 8) | (b0 << 16) | (255 << 24); // Set alpha to 1.0
-
- rgbData++;
- }
- }
- writeFile("inRgba8888.raw", mRawRgba8888Image.data,
- mRawYuv420Image.width * mRawYuv420Image.height * 4);
- return true;
-}
-
-bool UltraHdrAppInput::convertRgba8888ToYUV444Image() {
- mDestYUV444Image.data = malloc(mDestImage.width * mDestImage.height * 3);
- if (mDestYUV444Image.data == nullptr) {
- std::cerr << "failed to allocate memory to store yuv444" << std::endl;
- return false;
- }
- mDestYUV444Image.width = mDestImage.width;
- mDestYUV444Image.height = mDestImage.height;
- mDestYUV444Image.colorGamut = mDestImage.colorGamut;
-
- uint32_t* rgbData = static_cast<uint32_t*>(mDestImage.data);
-
- uint8_t* yData = static_cast<uint8_t*>(mDestYUV444Image.data);
- uint8_t* uData = yData + (mDestYUV444Image.width * mDestYUV444Image.height);
- uint8_t* vData = uData + (mDestYUV444Image.width * mDestYUV444Image.height);
-
- const float* coeffs = BT601RGBtoYUVMatrix;
- for (size_t i = 0; i < mDestImage.height; i++) {
- for (size_t j = 0; j < mDestImage.width; j++) {
- float r0 = float(rgbData[mDestImage.width * i + j] & 0xff);
- float g0 = float((rgbData[mDestImage.width * i + j] >> 8) & 0xff);
- float b0 = float((rgbData[mDestImage.width * i + j] >> 16) & 0xff);
-
- r0 /= 255.0f;
- g0 /= 255.0f;
- b0 /= 255.0f;
-
- float y = coeffs[0] * r0 + coeffs[1] * g0 + coeffs[2] * b0;
- float u = coeffs[3] * r0 + coeffs[4] * g0 + coeffs[5] * b0;
- float v = coeffs[6] * r0 + coeffs[7] * g0 + coeffs[8] * b0;
-
- y = y * 255.0f + 0.5f;
- u = u * 255.0f + 0.5f + 128.0f;
- v = v * 255.0f + 0.5f + 128.0f;
-
- y = CLIP3(y, 0.0f, 255.0f);
- u = CLIP3(u, 0.0f, 255.0f);
- v = CLIP3(v, 0.0f, 255.0f);
-
- yData[mDestYUV444Image.width * i + j] = uint8_t(y);
- uData[mDestYUV444Image.width * i + j] = uint8_t(u);
- vData[mDestYUV444Image.width * i + j] = uint8_t(v);
- }
- }
- writeFile("outyuv444.yuv", mDestYUV444Image.data,
- mDestYUV444Image.width * mDestYUV444Image.height * 3);
- return true;
-}
-
-bool UltraHdrAppInput::convertRgba1010102ToYUV444Image() {
- const float* coeffs = BT2020RGBtoYUVMatrix;
- if (mP010Cg == ULTRAHDR_COLORGAMUT_BT709) {
- coeffs = BT709RGBtoYUVMatrix;
- } else if (mP010Cg == ULTRAHDR_COLORGAMUT_BT2100) {
- coeffs = BT2020RGBtoYUVMatrix;
- } else if (mP010Cg == ULTRAHDR_COLORGAMUT_P3) {
- coeffs = BT601RGBtoYUVMatrix;
- } else {
- std::cerr << "color matrix not present for gamut " << mP010Cg << " using BT2020Matrix"
- << std::endl;
- }
-
- mDestYUV444Image.data = malloc(mDestImage.width * mDestImage.height * 3 * 2);
- if (mDestYUV444Image.data == nullptr) {
- std::cerr << "failed to allocate memory to store yuv444" << std::endl;
- return false;
- }
- mDestYUV444Image.width = mDestImage.width;
- mDestYUV444Image.height = mDestImage.height;
- mDestYUV444Image.colorGamut = mDestImage.colorGamut;
-
- uint32_t* rgbData = static_cast<uint32_t*>(mDestImage.data);
-
- uint16_t* yData = static_cast<uint16_t*>(mDestYUV444Image.data);
- uint16_t* uData = yData + (mDestYUV444Image.width * mDestYUV444Image.height);
- uint16_t* vData = uData + (mDestYUV444Image.width * mDestYUV444Image.height);
-
- for (size_t i = 0; i < mDestImage.height; i++) {
- for (size_t j = 0; j < mDestImage.width; j++) {
- float r0 = float(rgbData[mDestImage.width * i + j] & 0x3ff);
- float g0 = float((rgbData[mDestImage.width * i + j] >> 10) & 0x3ff);
- float b0 = float((rgbData[mDestImage.width * i + j] >> 20) & 0x3ff);
-
- r0 /= 1023.0f;
- g0 /= 1023.0f;
- b0 /= 1023.0f;
-
- float y = coeffs[0] * r0 + coeffs[1] * g0 + coeffs[2] * b0;
- float u = coeffs[3] * r0 + coeffs[4] * g0 + coeffs[5] * b0;
- float v = coeffs[6] * r0 + coeffs[7] * g0 + coeffs[8] * b0;
-
- y = (y * 876.0f) + 64.0f + 0.5f;
- u = (u * 896.0f) + 64.0f + 512.0f + 0.5f;
- v = (v * 896.0f) + 64.0f + 512.0f + 0.5f;
-
- y = CLIP3(y, 64.0f, 940.0f);
- u = CLIP3(u, 64.0f, 960.0f);
- v = CLIP3(v, 64.0f, 960.0f);
-
- yData[mDestYUV444Image.width * i + j] = uint16_t(y);
- uData[mDestYUV444Image.width * i + j] = uint16_t(u);
- vData[mDestYUV444Image.width * i + j] = uint16_t(v);
- }
- }
- writeFile("outyuv444.yuv", mDestYUV444Image.data,
- mDestYUV444Image.width * mDestYUV444Image.height * 3 * 2);
- return true;
-}
-
-void UltraHdrAppInput::computeRGBHdrPSNR() {
- if (mOf == ULTRAHDR_OUTPUT_SDR || mOf == ULTRAHDR_OUTPUT_HDR_LINEAR) {
- std::cout << "psnr not supported for output format " << mOf << std::endl;
- return;
- }
- uint32_t* rgbDataSrc = static_cast<uint32_t*>(mRawRgba1010102Image.data);
- uint32_t* rgbDataDst = static_cast<uint32_t*>(mDestImage.data);
- if (rgbDataSrc == nullptr || rgbDataDst == nullptr) {
- std::cerr << "invalid src or dst pointer for psnr computation " << std::endl;
- return;
- }
- if ((mOf == ULTRAHDR_OUTPUT_HDR_PQ && mTf != ULTRAHDR_TF_PQ) ||
- (mOf == ULTRAHDR_OUTPUT_HDR_HLG && mTf != ULTRAHDR_TF_HLG)) {
- std::cout << "input transfer function and output format are not compatible, psnr results "
- "may be unreliable"
- << std::endl;
- }
- uint64_t rSqError = 0, gSqError = 0, bSqError = 0;
- for (size_t i = 0; i < mRawP010Image.width * mRawP010Image.height; i++) {
- int rSrc = *rgbDataSrc & 0x3ff;
- int rDst = *rgbDataDst & 0x3ff;
- rSqError += (rSrc - rDst) * (rSrc - rDst);
-
- int gSrc = (*rgbDataSrc >> 10) & 0x3ff;
- int gDst = (*rgbDataDst >> 10) & 0x3ff;
- gSqError += (gSrc - gDst) * (gSrc - gDst);
-
- int bSrc = (*rgbDataSrc >> 20) & 0x3ff;
- int bDst = (*rgbDataDst >> 20) & 0x3ff;
- bSqError += (bSrc - bDst) * (bSrc - bDst);
-
- rgbDataSrc++;
- rgbDataDst++;
- }
- double meanSquareError = (double)rSqError / (mRawP010Image.width * mRawP010Image.height);
- mPsnr[0] = meanSquareError ? 10 * log10((double)1023 * 1023 / meanSquareError) : 100;
-
- meanSquareError = (double)gSqError / (mRawP010Image.width * mRawP010Image.height);
- mPsnr[1] = meanSquareError ? 10 * log10((double)1023 * 1023 / meanSquareError) : 100;
-
- meanSquareError = (double)bSqError / (mRawP010Image.width * mRawP010Image.height);
- mPsnr[2] = meanSquareError ? 10 * log10((double)1023 * 1023 / meanSquareError) : 100;
-
- std::cout << "psnr r :: " << mPsnr[0] << " psnr g :: " << mPsnr[1] << " psnr b :: " << mPsnr[2]
- << std::endl;
-}
-
-void UltraHdrAppInput::computeRGBSdrPSNR() {
- if (mOf != ULTRAHDR_OUTPUT_SDR) {
- std::cout << "psnr not supported for output format " << mOf << std::endl;
- return;
- }
- uint32_t* rgbDataSrc = static_cast<uint32_t*>(mRawRgba8888Image.data);
- uint32_t* rgbDataDst = static_cast<uint32_t*>(mDestImage.data);
- if (rgbDataSrc == nullptr || rgbDataDst == nullptr) {
- std::cerr << "invalid src or dst pointer for psnr computation " << std::endl;
- return;
- }
-
- uint64_t rSqError = 0, gSqError = 0, bSqError = 0;
- for (size_t i = 0; i < mRawYuv420Image.width * mRawYuv420Image.height; i++) {
- int rSrc = *rgbDataSrc & 0xff;
- int rDst = *rgbDataDst & 0xff;
- rSqError += (rSrc - rDst) * (rSrc - rDst);
-
- int gSrc = (*rgbDataSrc >> 8) & 0xff;
- int gDst = (*rgbDataDst >> 8) & 0xff;
- gSqError += (gSrc - gDst) * (gSrc - gDst);
-
- int bSrc = (*rgbDataSrc >> 16) & 0xff;
- int bDst = (*rgbDataDst >> 16) & 0xff;
- bSqError += (bSrc - bDst) * (bSrc - bDst);
-
- rgbDataSrc++;
- rgbDataDst++;
- }
- double meanSquareError = (double)rSqError / (mRawYuv420Image.width * mRawYuv420Image.height);
- mPsnr[0] = meanSquareError ? 10 * log10((double)255 * 255 / meanSquareError) : 100;
-
- meanSquareError = (double)gSqError / (mRawYuv420Image.width * mRawYuv420Image.height);
- mPsnr[1] = meanSquareError ? 10 * log10((double)255 * 255 / meanSquareError) : 100;
-
- meanSquareError = (double)bSqError / (mRawYuv420Image.width * mRawYuv420Image.height);
- mPsnr[2] = meanSquareError ? 10 * log10((double)255 * 255 / meanSquareError) : 100;
-
- std::cout << "psnr r :: " << mPsnr[0] << " psnr g :: " << mPsnr[1] << " psnr b :: " << mPsnr[2]
- << std::endl;
-}
-
-void UltraHdrAppInput::computeYUVHdrPSNR() {
- if (mOf == ULTRAHDR_OUTPUT_SDR || mOf == ULTRAHDR_OUTPUT_HDR_LINEAR) {
- std::cout << "psnr not supported for output format " << mOf << std::endl;
- return;
- }
- uint16_t* yuvDataSrc = static_cast<uint16_t*>(mRawP010Image.data);
- uint16_t* yuvDataDst = static_cast<uint16_t*>(mDestYUV444Image.data);
- if (yuvDataSrc == nullptr || yuvDataDst == nullptr) {
- std::cerr << "invalid src or dst pointer for psnr computation " << std::endl;
- return;
- }
- if ((mOf == ULTRAHDR_OUTPUT_HDR_PQ && mTf != ULTRAHDR_TF_PQ) ||
- (mOf == ULTRAHDR_OUTPUT_HDR_HLG && mTf != ULTRAHDR_TF_HLG)) {
- std::cout << "input transfer function and output format are not compatible, psnr results "
- "may be unreliable"
- << std::endl;
- }
-
- uint16_t* yDataSrc = static_cast<uint16_t*>(mRawP010Image.data);
- uint16_t* uDataSrc = yDataSrc + (mRawP010Image.width * mRawP010Image.height);
- uint16_t* vDataSrc = uDataSrc + 1;
-
- uint16_t* yDataDst = static_cast<uint16_t*>(mDestYUV444Image.data);
- uint16_t* uDataDst = yDataDst + (mDestYUV444Image.width * mDestYUV444Image.height);
- uint16_t* vDataDst = uDataDst + (mDestYUV444Image.width * mDestYUV444Image.height);
-
- uint64_t ySqError = 0, uSqError = 0, vSqError = 0;
- for (size_t i = 0; i < mDestYUV444Image.height; i++) {
- for (size_t j = 0; j < mDestYUV444Image.width; j++) {
- int ySrc = (yDataSrc[mRawP010Image.width * i + j] >> 6) & 0x3ff;
- ySrc = CLIP3(ySrc, 64, 940);
- int yDst = yDataDst[mDestYUV444Image.width * i + j] & 0x3ff;
- ySqError += (ySrc - yDst) * (ySrc - yDst);
-
- if (i % 2 == 0 && j % 2 == 0) {
- int uSrc = (uDataSrc[mRawP010Image.width * (i / 2) + (j / 2) * 2] >> 6) & 0x3ff;
- uSrc = CLIP3(uSrc, 64, 960);
- int uDst = uDataDst[mDestYUV444Image.width * i + j] & 0x3ff;
- uDst += uDataDst[mDestYUV444Image.width * i + j + 1] & 0x3ff;
- uDst += uDataDst[mDestYUV444Image.width * (i + 1) + j + 1] & 0x3ff;
- uDst += uDataDst[mDestYUV444Image.width * (i + 1) + j + 1] & 0x3ff;
- uDst = (uDst + 2) >> 2;
- uSqError += (uSrc - uDst) * (uSrc - uDst);
-
- int vSrc = (vDataSrc[mRawP010Image.width * (i / 2) + (j / 2) * 2] >> 6) & 0x3ff;
- vSrc = CLIP3(vSrc, 64, 960);
- int vDst = vDataDst[mDestYUV444Image.width * i + j] & 0x3ff;
- vDst += vDataDst[mDestYUV444Image.width * i + j + 1] & 0x3ff;
- vDst += vDataDst[mDestYUV444Image.width * (i + 1) + j + 1] & 0x3ff;
- vDst += vDataDst[mDestYUV444Image.width * (i + 1) + j + 1] & 0x3ff;
- vDst = (vDst + 2) >> 2;
- vSqError += (vSrc - vDst) * (vSrc - vDst);
- }
- }
- }
-
- double meanSquareError = (double)ySqError / (mDestYUV444Image.width * mDestYUV444Image.height);
- mPsnr[0] = meanSquareError ? 10 * log10((double)1023 * 1023 / meanSquareError) : 100;
-
- meanSquareError = (double)uSqError / (mDestYUV444Image.width * mDestYUV444Image.height / 4);
- mPsnr[1] = meanSquareError ? 10 * log10((double)1023 * 1023 / meanSquareError) : 100;
-
- meanSquareError = (double)vSqError / (mDestYUV444Image.width * mDestYUV444Image.height / 4);
- mPsnr[2] = meanSquareError ? 10 * log10((double)1023 * 1023 / meanSquareError) : 100;
-
- std::cout << "psnr y :: " << mPsnr[0] << " psnr u :: " << mPsnr[1] << " psnr v :: " << mPsnr[2]
- << std::endl;
-}
-
-void UltraHdrAppInput::computeYUVSdrPSNR() {
- if (mOf != ULTRAHDR_OUTPUT_SDR) {
- std::cout << "psnr not supported for output format " << mOf << std::endl;
- return;
- }
-
- uint8_t* yDataSrc = static_cast<uint8_t*>(mRawYuv420Image.data);
- uint8_t* uDataSrc = yDataSrc + (mRawYuv420Image.width * mRawYuv420Image.height);
- uint8_t* vDataSrc = uDataSrc + (mRawYuv420Image.width * mRawYuv420Image.height / 4);
-
- uint8_t* yDataDst = static_cast<uint8_t*>(mDestYUV444Image.data);
- uint8_t* uDataDst = yDataDst + (mDestYUV444Image.width * mDestYUV444Image.height);
- uint8_t* vDataDst = uDataDst + (mDestYUV444Image.width * mDestYUV444Image.height);
-
- uint64_t ySqError = 0, uSqError = 0, vSqError = 0;
- for (size_t i = 0; i < mDestYUV444Image.height; i++) {
- for (size_t j = 0; j < mDestYUV444Image.width; j++) {
- int ySrc = yDataSrc[mRawYuv420Image.width * i + j];
- int yDst = yDataDst[mDestYUV444Image.width * i + j];
- ySqError += (ySrc - yDst) * (ySrc - yDst);
-
- if (i % 2 == 0 && j % 2 == 0) {
- int uSrc = uDataSrc[mRawYuv420Image.width / 2 * (i / 2) + j / 2];
- int uDst = uDataDst[mDestYUV444Image.width * i + j];
- uDst += uDataDst[mDestYUV444Image.width * i + j + 1];
- uDst += uDataDst[mDestYUV444Image.width * (i + 1) + j];
- uDst += uDataDst[mDestYUV444Image.width * (i + 1) + j + 1];
- uDst = (uDst + 2) >> 2;
- uSqError += (uSrc - uDst) * (uSrc - uDst);
-
- int vSrc = vDataSrc[mRawYuv420Image.width / 2 * (i / 2) + j / 2];
- int vDst = vDataDst[mDestYUV444Image.width * i + j];
- vDst += vDataDst[mDestYUV444Image.width * i + j + 1];
- vDst += vDataDst[mDestYUV444Image.width * (i + 1) + j];
- vDst += vDataDst[mDestYUV444Image.width * (i + 1) + j + 1];
- vDst = (vDst + 2) >> 2;
- vSqError += (vSrc - vDst) * (vSrc - vDst);
- }
- }
- }
- double meanSquareError = (double)ySqError / (mDestYUV444Image.width * mDestYUV444Image.height);
- mPsnr[0] = meanSquareError ? 10 * log10((double)255 * 255 / meanSquareError) : 100;
-
- meanSquareError = (double)uSqError / (mDestYUV444Image.width * mDestYUV444Image.height / 4);
- mPsnr[1] = meanSquareError ? 10 * log10((double)255 * 255 / meanSquareError) : 100;
-
- meanSquareError = (double)vSqError / (mDestYUV444Image.width * mDestYUV444Image.height / 4);
- mPsnr[2] = meanSquareError ? 10 * log10((double)255 * 255 / meanSquareError) : 100;
-
- std::cout << "psnr y :: " << mPsnr[0] << " psnr u:: " << mPsnr[1] << " psnr v :: " << mPsnr[2]
- << std::endl;
-}
-
-static void usage(const char* name) {
- fprintf(stderr, "\n## ultra hdr demo application.\nUsage : %s \n", name);
- fprintf(stderr, " -m mode of operation. [0: encode, 1:decode] \n");
- fprintf(stderr, "\n## encoder options : \n");
- fprintf(stderr, " -p raw 10 bit input resource in p010 color format, mandatory. \n");
- fprintf(stderr, " -y raw 8 bit input resource in yuv420, optional. \n"
- " if not provided tonemapping happens internally. \n");
- fprintf(stderr, " -i compressed 8 bit jpeg file path, optional \n");
- fprintf(stderr, " -w input file width, mandatory. \n");
- fprintf(stderr, " -h input file height, mandatory. \n");
- fprintf(stderr, " -C 10 bit input color gamut, optional. [0:bt709, 1:p3, 2:bt2100] \n");
- fprintf(stderr, " -c 8 bit input color gamut, optional. [0:bt709, 1:p3, 2:bt2100] \n");
- fprintf(stderr, " -t input transfer function, optional. [0:linear, 1:hlg, 2:pq] \n");
- fprintf(stderr, " -q quality factor to be used while encoding 8 bit image, optional. [0-100].\n"
- " gain map image does not use this quality factor. \n"
- " for now gain map image quality factor is not configurable. \n");
- fprintf(stderr, " -e compute psnr, optional. [0:yes, 1:no] \n");
- fprintf(stderr, "\n## decoder options : \n");
- fprintf(stderr, " -j ultra hdr input resource, mandatory in decode mode. \n");
- fprintf(stderr, " -o output transfer function, optional. [0:sdr, 1:hdr_linear, 2:hdr_pq, 3:hdr_hlg] \n");
- fprintf(stderr, "\n## examples of usage :\n");
- fprintf(stderr, "\n## encode api-0 :\n");
- fprintf(stderr, " ultrahdr_app -m 0 -p cosmat_1920x1080_p010.yuv -w 1920 -h 1080 -q 97\n");
- fprintf(stderr, " ultrahdr_app -m 0 -p cosmat_1920x1080_p010.yuv -w 1920 -h 1080 -q 97 -C 2 -t 2\n");
- fprintf(stderr, "\n## encode api-1 :\n");
- fprintf(stderr, " ultrahdr_app -m 0 -p cosmat_1920x1080_p010.yuv -y cosmat_1920x1080_420.yuv -w 1920 -h 1080 -q 97\n");
- fprintf(stderr, " ultrahdr_app -m 0 -p cosmat_1920x1080_p010.yuv -y cosmat_1920x1080_420.yuv -w 1920 -h 1080 -q 97\n");
- fprintf(stderr, " ultrahdr_app -m 0 -p cosmat_1920x1080_p010.yuv -y cosmat_1920x1080_420.yuv -w 1920 -h 1080 -q 97 -C 2 -c 1 -t 1\n");
- fprintf(stderr, " ultrahdr_app -m 0 -p cosmat_1920x1080_p010.yuv -y cosmat_1920x1080_420.yuv -w 1920 -h 1080 -q 97 -C 2 -c 1 -t 1 -e 1\n");
- fprintf(stderr, "\n## encode api-2 :\n");
- fprintf(stderr, " ultrahdr_app -m 0 -p cosmat_1920x1080_p010.yuv -y cosmat_1920x1080_420.yuv -i cosmat_1920x1080_420_8bit.jpg -w 1920 -h 1080 -t 1 -o 3 -e 1\n");
- fprintf(stderr, "\n## encode api-3 :\n");
- fprintf(stderr, " ultrahdr_app -m 0 -p cosmat_1920x1080_p010.yuv -i cosmat_1920x1080_420_8bit.jpg -w 1920 -h 1080 -t 1 -o 3 -e 1\n");
- fprintf(stderr, "\n## decode api :\n");
- fprintf(stderr, " ultrahdr_app -m 1 -j cosmat_1920x1080_hdr.jpg \n");
- fprintf(stderr, " ultrahdr_app -m 1 -j cosmat_1920x1080_hdr.jpg -o 2\n");
- fprintf(stderr, "\n");
-}
-
-int main(int argc, char* argv[]) {
- char *p010_file = nullptr, *yuv420_file = nullptr, *jpegr_file = nullptr,
- *yuv420_jpeg_file = nullptr;
- int width = 0, height = 0;
- ultrahdr_color_gamut p010Cg = ULTRAHDR_COLORGAMUT_BT709;
- ultrahdr_color_gamut yuv420Cg = ULTRAHDR_COLORGAMUT_BT709;
- ultrahdr_transfer_function tf = ULTRAHDR_TF_HLG;
- int quality = 100;
- ultrahdr_output_format of = ULTRAHDR_OUTPUT_HDR_HLG;
- int mode = 0;
- int compute_psnr = 0;
- int ch;
- while ((ch = getopt(argc, argv, "p:y:i:w:h:C:c:t:q:o:m:j:e:")) != -1) {
- switch (ch) {
- case 'p':
- p010_file = optarg;
- break;
- case 'y':
- yuv420_file = optarg;
- break;
- case 'i':
- yuv420_jpeg_file = optarg;
- break;
- case 'w':
- width = atoi(optarg);
- break;
- case 'h':
- height = atoi(optarg);
- break;
- case 'C':
- p010Cg = static_cast<ultrahdr_color_gamut>(atoi(optarg));
- break;
- case 'c':
- yuv420Cg = static_cast<ultrahdr_color_gamut>(atoi(optarg));
- break;
- case 't':
- tf = static_cast<ultrahdr_transfer_function>(atoi(optarg));
- break;
- case 'q':
- quality = atoi(optarg);
- break;
- case 'o':
- of = static_cast<ultrahdr_output_format>(atoi(optarg));
- break;
- case 'm':
- mode = atoi(optarg);
- break;
- case 'j':
- jpegr_file = optarg;
- break;
- case 'e':
- compute_psnr = atoi(optarg);
- break;
- default:
- usage(argv[0]);
- return -1;
- }
- }
- if (mode == 0) {
- if (width <= 0 || height <= 0 || p010_file == nullptr) {
- usage(argv[0]);
- return -1;
- }
- UltraHdrAppInput appInput(p010_file, yuv420_file, yuv420_jpeg_file, width, height, p010Cg,
- yuv420Cg, tf, quality, of);
- if (!appInput.encode()) return -1;
- if (compute_psnr == 1) {
- if (!appInput.decode()) return -1;
- if (of == ULTRAHDR_OUTPUT_SDR && yuv420_file != nullptr) {
- appInput.convertYuv420ToRGBImage();
- appInput.computeRGBSdrPSNR();
- appInput.convertRgba8888ToYUV444Image();
- appInput.computeYUVSdrPSNR();
- } else if (of == ULTRAHDR_OUTPUT_HDR_HLG || of == ULTRAHDR_OUTPUT_HDR_PQ) {
- appInput.convertP010ToRGBImage();
- appInput.computeRGBHdrPSNR();
- appInput.convertRgba1010102ToYUV444Image();
- appInput.computeYUVHdrPSNR();
- }
- }
- } else if (mode == 1) {
- if (jpegr_file == nullptr) {
- usage(argv[0]);
- return -1;
- }
- UltraHdrAppInput appInput(jpegr_file, of);
- if (!appInput.decode()) return -1;
- } else {
- std::cerr << "unrecognized input mode " << mode << std::endl;
- usage(argv[0]);
- return -1;
- }
-
- return 0;
-}
diff --git a/third_party/cmake/image_io/CMakeLists.txt b/third_party/image_io/CMakeLists.txt
index d3ca8f1..86d020e 100644
--- a/third_party/cmake/image_io/CMakeLists.txt
+++ b/third_party/image_io/CMakeLists.txt
@@ -16,21 +16,19 @@
cmake_minimum_required(VERSION 3.5)
-project(IMAGE_IO VERSION 1.0.0)
+project(ImageIO CXX)
set(CMAKE_CXX_STANDARD 17)
+set(CMAKE_CXX_STANDARD_REQUIRED ON)
+set(CMAKE_CXX_EXTENSIONS OFF)
-set(IMAGE_IO_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../image_io")
+set(SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR})
-file(GLOB IMAGE_IO_SRC "${IMAGE_IO_DIR}/src/**/*.cc")
+file(GLOB IMAGE_IO_LIST "${SOURCE_DIR}/src/**/*.cc")
-add_library(image_io STATIC
- ${IMAGE_IO_SRC}
-)
+add_library(image_io STATIC ${IMAGE_IO_LIST})
-# Set include directories for the target
target_include_directories(image_io PRIVATE
- "${IMAGE_IO_DIR}/includes"
- "${IMAGE_IO_DIR}/src/modp_b64"
- "${IMAGE_IO_DIR}/src/modp_b64/modp_b64"
-)
+ "${SOURCE_DIR}/includes"
+ "${SOURCE_DIR}/src/modp_b64"
+ "${SOURCE_DIR}/src/modp_b64/modp_b64")
diff --git a/third_party/image_io/includes/image_io/base/data_range.h b/third_party/image_io/includes/image_io/base/data_range.h
index e2e339a..c7404ff 100644
--- a/third_party/image_io/includes/image_io/base/data_range.h
+++ b/third_party/image_io/includes/image_io/base/data_range.h
@@ -59,8 +59,8 @@ class DataRange {
/// @return The DataRange that represents the intersection, or one that is
/// is invalid if the ranges do not overlap at all.
DataRange GetIntersection(const DataRange& data_range) const {
- return DataRange(std::max(data_range.begin_, begin_),
- std::min(data_range.end_, end_));
+ return DataRange((std::max)(data_range.begin_, begin_),
+ (std::min)(data_range.end_, end_));
}
/// @param rhs A DataRange to compare with this one.
diff --git a/third_party/image_io/src/utils/file_utils.cc b/third_party/image_io/src/utils/file_utils.cc
index 626d537..8156d50 100644
--- a/third_party/image_io/src/utils/file_utils.cc
+++ b/third_party/image_io/src/utils/file_utils.cc
@@ -1,9 +1,9 @@
#include "image_io/utils/file_utils.h"
#include <sys/stat.h>
-#import <fstream>
-#import <iostream>
-#import <memory>
+#include <fstream>
+#include <iostream>
+#include <memory>
#include "image_io/base/data_range.h"
diff --git a/utils.cmake b/utils.cmake
deleted file mode 100644
index ddda380..0000000
--- a/utils.cmake
+++ /dev/null
@@ -1,84 +0,0 @@
-include(CheckCXXCompilerFlag)
-# cmake-format: off
-# Adds a target for an executable
-#
-# Arguments:
-# NAME: Name of the executatble
-# LIB: Library that executable depends on
-# SOURCES: Source files
-#
-# Optional Arguments:
-# INCLUDES: Include paths
-# LIBS: Additional libraries
-# FUZZER: flag to specify if the target is a fuzzer binary
-# cmake-format: on
-
-# Adds compiler options for all targets
-function(libultrahdr_add_compile_options)
- if(DEFINED SANITIZE)
- set(CMAKE_REQUIRED_FLAGS -fsanitize=${SANITIZE})
- check_cxx_compiler_flag(-fsanitize=${SANITIZE} COMPILER_HAS_SANITIZER)
- unset(CMAKE_REQUIRED_FLAGS)
-
- if(NOT COMPILER_HAS_SANITIZER)
- message(
- FATAL_ERROR "ERROR: Compiler doesn't support -fsanitize=${SANITIZE}")
- return()
- endif()
- add_compile_options(-fno-omit-frame-pointer -fsanitize=${SANITIZE})
- endif()
-
-endfunction()
-
-function(libultrahdr_add_executable NAME LIB)
- set(multi_value_args SOURCES INCLUDES LIBS)
- set(optional_args FUZZER)
- cmake_parse_arguments(ARG "${optional_args}" "${single_value_args}"
- "${multi_value_args}" ${ARGN})
-
- # Check if compiler supports -fsanitize=fuzzer. If not, skip building fuzzer
- # binary
- if(ARG_FUZZER)
- set(CMAKE_REQUIRED_FLAGS -fsanitize=fuzzer-no-link)
- check_cxx_compiler_flag(-fsanitize=fuzzer-no-link
- COMPILER_HAS_SANITIZE_FUZZER)
- unset(CMAKE_REQUIRED_FLAGS)
- if(NOT COMPILER_HAS_SANITIZE_FUZZER)
- message("Compiler doesn't support -fsanitize=fuzzer. Skipping ${NAME}")
- return()
- endif()
- endif()
-
- add_executable(${NAME} ${ARG_SOURCES})
- target_include_directories(${NAME} PRIVATE ${ARG_INCLUDES})
- add_dependencies(${NAME} ${LIB} ${ARG_LIBS})
-
- target_link_libraries(${NAME} ${LIB} ${ARG_LIBS})
- if(ARG_FUZZER)
- if(DEFINED ENV{LIB_FUZZING_ENGINE})
- set_target_properties(${NAME} PROPERTIES LINK_FLAGS
- $ENV{LIB_FUZZING_ENGINE})
- elseif(DEFINED SANITIZE)
- set_target_properties(${NAME} PROPERTIES LINK_FLAGS
- -fsanitize=fuzzer,${SANITIZE})
- else()
- set_target_properties(${NAME} PROPERTIES LINK_FLAGS -fsanitize=fuzzer)
- endif()
- else()
- if(DEFINED SANITIZE)
- set_target_properties(${NAME} PROPERTIES LINK_FLAGS
- -fsanitize=${SANITIZE})
- endif()
- endif()
-endfunction()
-
-# cmake-format: off
-# Adds a target for a fuzzer binary
-# Calls libultrahdr_add_executable with all arguments with FUZZER set to 1
-# Arguments:
-# Refer to libultrahdr_add_executable's arguments
-# cmake-format: on
-
-function(libultrahdr_add_fuzzer NAME LIB)
- libultrahdr_add_executable(${NAME} ${LIB} FUZZER 1 ${ARGV})
-endfunction()