cmake_minimum_required(VERSION 3.13) set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/modules" "${CMAKE_SOURCE_DIR}/cmake/") include(VersionFromGit) version_from_git(LOG ON TIMESTAMP "%Y-%m-%d-%H:%M:%S") project( hdrview DESCRIPTION "A simple research-oriented image viewer with an emphasis on examining and comparing high-dynamic range (HDR) images." VERSION ${VERSION} LANGUAGES C CXX ) # Defines HDRVIEW_TARGET_ARCHS, HDRVIEW_TARGET_ARCH, HDRVIEW_BITNESS, and HDRVIEW_IS_UNIVERSAL include(DetectArchitecture) detect_architecture() # Affects `cmake --install`/packaging layout only, not build-tree asset lookup (HDRVIEW_BUILD_TREE_ASSETS_DIR # below). ON: exe and assets/ install together in one relocatable prefix. OFF (the linux-appimage preset): # exe to CMAKE_INSTALL_BINDIR, assets to an FHS location baked in as a compile definition. option(HDRVIEW_PORTABLE_INSTALL "Install in a portable way" ON) option(HDRVIEW_ENABLE_LIBPNG "Enable LIBPNG support" ON) option(HDRVIEW_ENABLE_LIBJPEG "Enable libjpeg(-turbo) support" ON) option(UHDR_BUILD_DEPS "Whether to build uhdr dependencies instead of using system-installed ones" ON) option(HDRVIEW_ENABLE_LIBUHDR "Whether to build with Ultra HDR support" ON) option(HDRVIEW_ENABLE_LIBJXL "Enable JPEG-XL support" ON) option(HDRVIEW_ENABLE_J2K "Enable JPEG-2000 support" ON) option(HDRVIEW_ENABLE_HTJ2K "Enable high-throughput JPEG2000 (HTJ2K) support" ON) option(HDRVIEW_ENABLE_AVIF "Enable AVIF support" ON) option(HDRVIEW_ENABLE_DAV1D "Decode AVIF with dav1d, which is roughly twice as fast as libaom's decoder" ON) option(HDRVIEW_ENABLE_HEIC "Enable HEIC support" ON) option(HDRVIEW_ENABLE_AVCI "Enable AVCI support" ON) option(HDRVIEW_ENABLE_LIBWEBP "Enable LIBWEBP support" ON) option(HDRVIEW_ENABLE_LIBTIFF "Enable LIBTIFF support" ON) option(HDRVIEW_ENABLE_LIBRAW "Enable LibRaw support" ON) option(HDRVIEW_ENABLE_X3F "Enable LibRaw X3F support" ON) # receiving pixels pushed in by a renderer, over tev's protocol; a browser has no raw sockets to listen on option(HDRVIEW_ENABLE_IPC "Enable receiving live image updates from renderers (tev-compatible)" ON) if(EMSCRIPTEN) set(HDRVIEW_ENABLE_IPC OFF) endif() option( HDRVIEW_ENABLE_HDR_DISPLAY "Enable true HDR/EDR display output on Windows and Linux/Wayland, via a patched GLFW fork (Tom94/glfw). No effect on macOS, which always uses that fork and gets EDR through Metal." ON ) # declared here, not alongside HDRVIEW_BUILD_TESTS at the bottom, since the hello_imgui CPMAddPackage() below # needs it to decide whether to request HELLOIMGUI_WITH_TEST_ENGINE option(HDRVIEW_BUILD_GUI_TESTS "Build Dear ImGui Test Engine GUI regression tests" OFF) set(HDRVIEW_ENABLE_LIBHEIF OFF) if(HDRVIEW_ENABLE_J2K OR HDRVIEW_ENABLE_HTJ2K OR HDRVIEW_ENABLE_AVIF OR HDRVIEW_ENABLE_HEIC OR HDRVIEW_ENABLE_AVCI ) set(HDRVIEW_ENABLE_LIBHEIF ON) endif() set(CMAKE_INCLUDE_DIRECTORIES_PROJECT_BEFORE ON) # Set the framework search order to last, so that we can find libraries in the system directories first. This is to # prevent CMAKE from finding old versions of headers, e.g. libjpeg in /Library/Frameworks/Mono.framework/Headers, and # first check other install locations like brew. set(CMAKE_FIND_FRAMEWORK LAST) # cmake-format: off set(HDRVIEW_ICONSET "HDRVIEW_ICONSET_MS" CACHE STRING "Choose the icon set: HDRVIEW_ICONSET_FA6, HDRVIEW_ICONSET_LC, HDRVIEW_ICONSET_MS, HDRVIEW_ICONSET_MD, HDRVIEW_ICONSET_MDI" ) # Ensure the icon set is one of the allowed values set(ALLOWED_ICONSETS "HDRVIEW_ICONSET_FA6" "HDRVIEW_ICONSET_LC" "HDRVIEW_ICONSET_MS" "HDRVIEW_ICONSET_MD" "HDRVIEW_ICONSET_MDI" ) list(FIND ALLOWED_ICONSETS ${HDRVIEW_ICONSET} ICONSET_INDEX) if(ICONSET_INDEX EQUAL -1) message(FATAL_ERROR "Invalid icon set: ${HDRVIEW_ICONSET}. Allowed values are: ${ALLOWED_ICONSETS}") endif() # cmake-format: on # copy assets to the build directory set(MY_RESOURCE_FILES "assets/app_settings/icon.png" "assets/app_settings/icon-256.png") if(EMSCRIPTEN) file( GLOB_RECURSE ADDITIONAL_RESOURCE_FILES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/assets/fonts/Roboto/**.* ${CMAKE_CURRENT_SOURCE_DIR}/assets/app_settings/emscripten/**.* ${CMAKE_CURRENT_SOURCE_DIR}/assets/*.png ) list(APPEND MY_RESOURCE_FILES ${ADDITIONAL_RESOURCE_FILES}) elseif(APPLE) file( GLOB_RECURSE ADDITIONAL_RESOURCE_FILES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/assets/fonts/Roboto/**.* ${CMAKE_CURRENT_SOURCE_DIR}/assets/app_settings/apple/**.* ${CMAKE_CURRENT_SOURCE_DIR}/assets/*.png ) list(APPEND MY_RESOURCE_FILES ${ADDITIONAL_RESOURCE_FILES}) else() file( GLOB_RECURSE ADDITIONAL_RESOURCE_FILES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/assets/fonts/Roboto/**.* ${CMAKE_CURRENT_SOURCE_DIR}/assets/*.png ) list(APPEND MY_RESOURCE_FILES ${ADDITIONAL_RESOURCE_FILES}) endif() # add our chosen icon font if(HDRVIEW_ICONSET STREQUAL "HDRVIEW_ICONSET_FA6") # this file is included by hello imgui # # list(APPEND MY_RESOURCE_FILES "assets/fonts/Font_Awesome_6_Free-Solid-900.otf") elseif(HDRVIEW_ICONSET STREQUAL "HDRVIEW_ICONSET_LC") list(APPEND MY_RESOURCE_FILES "assets/fonts/lucide.ttf") elseif(HDRVIEW_ICONSET STREQUAL "HDRVIEW_ICONSET_MS") list(APPEND MY_RESOURCE_FILES "assets/fonts/MaterialSymbolsRounded_Filled-Regular.ttf") elseif(HDRVIEW_ICONSET STREQUAL "HDRVIEW_ICONSET_MD") list(APPEND MY_RESOURCE_FILES "assets/fonts/MaterialIcons-Regular.ttf") elseif(HDRVIEW_ICONSET STREQUAL "HDRVIEW_ICONSET_MDI") list(APPEND MY_RESOURCE_FILES "assets/fonts/materialdesignicons-webfont.ttf") endif() foreach(MY_RESOURCE_FILE ${MY_RESOURCE_FILES}) configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/${MY_RESOURCE_FILE} ${CMAKE_CURRENT_BINARY_DIR}/${MY_RESOURCE_FILE} COPYONLY ) endforeach() set(CMD "${CMAKE_COMMAND}") list(APPEND CMD "-D" "SRC_DIR=${CMAKE_CURRENT_SOURCE_DIR}/src") list(APPEND CMD "-D" "BIN_DIR=${CMAKE_CURRENT_BINARY_DIR}/src") list(APPEND CMD "-D" "CMAKE_SIZEOF_VOID_P=${CMAKE_SIZEOF_VOID_P}") list(APPEND CMD "-D" "CMAKE_SYSTEM_PROCESSOR=${CMAKE_SYSTEM_PROCESSOR}") list(APPEND CMD "-D" "EMSCRIPTEN=${EMSCRIPTEN}") list(APPEND CMD "-D" "HDRVIEW_TARGET_ARCH=${HDRVIEW_TARGET_ARCH}") list(APPEND CMD "-P" "${CMAKE_SOURCE_DIR}/cmake/generate_version.cmake") # Runs on every build so version.cpp tracks the current commit and branch. configure_file() rewrites it only # when the git-derived contents change, so this does not force a relink every build. add_custom_target( hdrview_check_version ALL COMMAND ${CMD} BYPRODUCTS ${CMAKE_CURRENT_BINARY_DIR}/src/version.cpp COMMENT "Checking git version" ) message(STATUS "CMAKE_SYSTEM_PROCESSOR: ${CMAKE_SYSTEM_PROCESSOR}") message(STATUS "CMAKE_OSX_ARCHITECTURES: ${CMAKE_OSX_ARCHITECTURES}") include(sanitizers) # Set ourselves as the startup project in visual studio. Not available until cmake 3.6, but doesn't break older # versions. set_property(DIRECTORY PROPERTY VS_STARTUP_PROJECT HDRView) # =====================================================================# Set a default build configuration (Release) # ============================================================================ get_cmake_property(IS_MULTI GENERATOR_IS_MULTI_CONFIG) if(NOT CMAKE_BUILD_TYPE AND NOT IS_MULTI) set(DEFAULT_BUILD_TYPE Release) if(EMSCRIPTEN) set(DEFAULT_BUILD_TYPE MinSizeRel) endif() message(STATUS "Setting build type to '${DEFAULT_BUILD_TYPE}' as none was specified.") set(CMAKE_BUILD_TYPE ${DEFAULT_BUILD_TYPE} CACHE STRING "Choose the type of build." FORCE ) set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" "MinSizeRel" "RelWithDebInfo") endif() string(TOUPPER "${CMAKE_BUILD_TYPE}" U_CMAKE_BUILD_TYPE) # ============================================================================ # Enable folders for projects in Visual Studio # ============================================================================ if(CMAKE_GENERATOR MATCHES "Visual Studio") set_property(GLOBAL PROPERTY USE_FOLDERS ON) endif() if(CMAKE_CXX_COMPILER_ID MATCHES "Clang" OR CMAKE_CXX_COMPILER_ID MATCHES "GNU") # Enable link time optimization and set the default symbol visibility to hidden (very important to obtain small # binaries) if(NOT ${U_CMAKE_BUILD_TYPE} MATCHES DEBUG) # Default symbol visibility set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility=hidden") # set(CMAKE_CXX_VISIBILITY_PRESET hidden) endif() # Disable specific GCC 7 warnings if(CMAKE_COMPILER_IS_GNUCC AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 7.0) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-deprecated-declarations -Wno-misleading-indentation -Wformat-truncation=0 -Wno-int-in-bool-context -Wimplicit-fallthrough=0" ) endif() endif() # ============================================================================ # Sanitize build environment for static build with C++11/17 # ============================================================================ if(MSVC) # Disable annoying secure CRT warnings set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /D_CRT_SECURE_NO_WARNINGS") # drop CMake's default /W3 so no warning level is inherited globally; each target sets its own, and cl warns # (D9025) when it sees two string(REGEX REPLACE "/W[0-4]" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") string(REGEX REPLACE "/W[0-4]" "" CMAKE_C_FLAGS "${CMAKE_C_FLAGS}") # Parallel build on MSVC (all targets) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP") if(NOT CMAKE_SIZEOF_VOID_P EQUAL 8) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /arch:SSE2") endif() # Static build set(CompilerFlags CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO CMAKE_C_FLAGS CMAKE_C_FLAGS_DEBUG CMAKE_C_FLAGS_RELEASE CMAKE_C_FLAGS_MINSIZEREL CMAKE_C_FLAGS_RELWITHDEBINFO ) foreach(CompilerFlag ${CompilerFlags}) string(REPLACE "/MD" "/MT" ${CompilerFlag} "${${CompilerFlag}}") endforeach() endif() include(FindPkgConfig) # ===================================================================================== # Add dependencies via CPM (see https://github.com/TheLartians/CPM.cmake for more info) # ===================================================================================== include(cmake/CPM.cmake) # Function to mark a library's includes as SYSTEM to suppress compiler warnings # Usage: mark_as_system_library(target_name) function(mark_as_system_library target_name) if(TARGET ${target_name}) # Resolve the actual target if this is an alias get_target_property(aliased_target ${target_name} ALIASED_TARGET) if(aliased_target) set(actual_target ${aliased_target}) else() set(actual_target ${target_name}) endif() # Get the library's existing interface includes get_target_property(lib_includes ${actual_target} INTERFACE_INCLUDE_DIRECTORIES) if(lib_includes) # Get existing system includes (if any) to avoid overwriting get_target_property(existing_system_includes ${actual_target} INTERFACE_SYSTEM_INCLUDE_DIRECTORIES) # Combine existing system includes with the library's includes if(existing_system_includes) set(new_system_includes "${existing_system_includes};${lib_includes}") else() set(new_system_includes "${lib_includes}") endif() # Update the target to use SYSTEM includes set_target_properties(${actual_target} PROPERTIES INTERFACE_SYSTEM_INCLUDE_DIRECTORIES "${new_system_includes}" ) message(STATUS "Marked ${target_name} includes as SYSTEM (actual target: ${actual_target})") endif() else() message(WARNING "Target ${target_name} does not exist, cannot mark as SYSTEM") endif() endfunction() # Function to disable warnings when compiling a third-party library target # Usage: disable_target_warnings(target_name) function(disable_target_warnings target_name) if(TARGET ${target_name}) # Resolve the actual target if this is an alias get_target_property(aliased_target ${target_name} ALIASED_TARGET) if(aliased_target) set(actual_target ${aliased_target}) else() set(actual_target ${target_name}) endif() # Determine the target type so we can set appropriate compile option scope. get_target_property(_target_type ${actual_target} TYPE) if(NOT _target_type) set(_target_type "") endif() if(MSVC) # Remove any existing warning flags set by the target's own CMakeLists get_target_property(existing_flags ${actual_target} COMPILE_OPTIONS) if(existing_flags) list(FILTER existing_flags EXCLUDE REGEX "^/[Ww][0-4]?") set_target_properties(${actual_target} PROPERTIES COMPILE_OPTIONS "${existing_flags}") endif() # Use /w to disable all warnings, add it before other flags to avoid D9025 warnings if(_target_type STREQUAL "INTERFACE_LIBRARY") target_compile_options(${actual_target} BEFORE INTERFACE /w) else() target_compile_options(${actual_target} BEFORE PRIVATE /w) endif() else() # For INTERFACE targets we must set INTERFACE properties; for others use PRIVATE if(_target_type STREQUAL "INTERFACE_LIBRARY") target_compile_options(${actual_target} INTERFACE -w) else() target_compile_options(${actual_target} PRIVATE -w) endif() endif() message(STATUS "Disabled warnings for ${target_name} (actual target: ${actual_target})") else() message(WARNING "Target ${target_name} does not exist, cannot disable warnings") endif() endfunction() # ===================================================================================== # Shader cross-compilation via sokol-shdc # ===================================================================================== include(cmake/SokolShaderCompiler.cmake) # Generates the image-shader_{vert,frag}.{glsl,metal} that Shader::from_asset() loads at runtime. # colorspaces.sglsl is @include_block'd into the compiled output, so it needs no build step of its own. # The output goes into ${CMAKE_CURRENT_BINARY_DIR}/assets/shaders, which hello_imgui_add_app(... # ASSETS_LOCATION ...) below already treats as the runtime asset root. # # sokol-shdc writes one shader dialect, matching the rendering backend, with a #version line at the top. # Desktop GL targets glsl410, so src/app.cpp requests a GL 4.1 core context. if(EMSCRIPTEN) set(HDRVIEW_SHADER_SLANG glsl300es) set(HDRVIEW_SHADER_EXT glsl) elseif(APPLE) # macOS is Metal-only set(HDRVIEW_SHADER_SLANG metal_macos) set(HDRVIEW_SHADER_EXT metal) else() set(HDRVIEW_SHADER_SLANG glsl410) set(HDRVIEW_SHADER_EXT glsl) endif() sokol_shdc_generate( INPUT ${CMAKE_CURRENT_SOURCE_DIR}/assets/shaders/image-shader.sglsl OUTPUT_DIR ${CMAKE_CURRENT_BINARY_DIR}/assets/shaders NAME image-shader PROGRAM image_shader SLANG ${HDRVIEW_SHADER_SLANG} EXTRA_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/assets/shaders/colorspaces.sglsl RENAME image-shader_image_shader_${HDRVIEW_SHADER_SLANG}_vertex.${HDRVIEW_SHADER_EXT} image-shader_vert.${HDRVIEW_SHADER_EXT} image-shader_image_shader_${HDRVIEW_SHADER_SLANG}_fragment.${HDRVIEW_SHADER_EXT} image-shader_frag.${HDRVIEW_SHADER_EXT} ) # The final HDR/EDR display color-management pass (see colorpass.sglsl). Only the desktop OpenGL backend # needs it: macOS/Metal EDR consumes extended sRGB directly, and Emscripten has no HDR path. if(NOT APPLE AND NOT EMSCRIPTEN) sokol_shdc_generate( INPUT ${CMAKE_CURRENT_SOURCE_DIR}/assets/shaders/colorpass.sglsl OUTPUT_DIR ${CMAKE_CURRENT_BINARY_DIR}/assets/shaders NAME colorpass PROGRAM colorpass SLANG glsl410 EXTRA_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/assets/shaders/colorspaces.sglsl RENAME colorpass_colorpass_glsl410_vertex.glsl colorpass_vert.glsl colorpass_colorpass_glsl410_fragment.glsl colorpass_frag.glsl ) endif() if(APPLE AND NOT EMSCRIPTEN) # default to GLFW+Metal on macOS set(HELLOIMGUI_HAS_METAL ON) set(HELLOIMGUI_USE_GLFW3 ON) # display_headroom_cocoa.mm queries the one display property GLFW's Cocoa backend leaves unreported list(APPEND EXTRA_SOURCES src/renderpass_metal.mm src/shader_metal.mm src/texture_metal.mm src/display_headroom_cocoa.mm ) # need to enable OBJC language for the custom glfw fork below enable_language(OBJC) endif() # Tom94/glfw adds HDR/EDR display output (float framebuffer on Windows, wp_color_manager_v1 on Wayland, EDR # on macOS) and the macOS drag-onto-icon support src/app.cpp needs, so it is required on macOS regardless of # HDRVIEW_ENABLE_HDR_DISPLAY. Nothing upstream yet (glfw/glfw#890 since 2016, glfw/glfw#1907 since 2021), so # every use of its API is behind GLFW_FLOATBUFFER / GLFW_WAYLAND_COLOR_MANAGEMENT and builds SDR-only against # stock GLFW. On Linux the fork needs dbus-1 and xkbcommon (see .github/workflows/ci-linux.yml). if((APPLE OR ((WIN32 OR (CMAKE_SYSTEM_NAME MATCHES "Linux")) AND HDRVIEW_ENABLE_HDR_DISPLAY)) AND NOT EMSCRIPTEN) set(HELLOIMGUI_DOWNLOAD_GLFW_IF_NEEDED OFF) set(HELLOIMGUI_USE_GLFW3 ON) if(NOT APPLE) # macOS selects Metal above; Windows/Linux render through OpenGL/GLES set(HELLOIMGUI_HAS_OPENGL3 ON) endif() if(WIN32) # the fork's Win32 backend reports peak luminance only as a flag, so display_luminance_win32.cpp asks # DXGI for the real number list(APPEND EXTRA_SOURCES src/display_luminance_win32.cpp) list(APPEND HDRVIEW_DEPENDENCIES dxgi) endif() CPMAddPackage( NAME glfw GITHUB_REPOSITORY Tom94/glfw GIT_TAG 2cc924c234423f635aee654073e5bb5a216392dd OPTIONS "GLFW_BUILD_EXAMPLES OFF" "GLFW_BUILD_TESTS OFF" "GLFW_BUILD_DOCS OFF" "GLFW_BUILD_INSTALL OFF" "GLFW_INSTALL OFF" "GLFW_USE_CHDIR OFF" "BUILD_SHARED_LIBS OFF" EXCLUDE_FROM_ALL YES ) if(NOT glfw_ADDED) message(FATAL_ERROR "Could not download HDR-enabled fork of glfw library.") endif() message(STATUS "Using HDR-enabled fork of glfw (Tom94/glfw)") mark_as_advanced( GLFW_BUILD_DOCS GLFW_BUILD_EXAMPLES GLFW_BUILD_INSTALL GLFW_BUILD_TESTS GLFW_DOCUMENT_INTERNALS GLFW_INSTALL GLFW_USE_CHDIR GLFW_USE_MENUBAR GLFW_USE_OSMESA GLFW_VULKAN_STATIC GLFW_USE_RETINA GLFW_USE_MIR ) endif() set(HELLOIMGUI_EMSCRIPTEN_PTHREAD OFF) set(HELLOIMGUI_EMSCRIPTEN_PTHREAD_ALLOW_MEMORY_GROWTH OFF) set(IMGUI_DISABLE_OBSOLETE_FUNCTIONS ON) # Prevent dependencies from installing their files set(CMAKE_SKIP_INSTALL_ALL_DEPENDENCY TRUE) set(HELLOIMGUI_CPM_OPTIONS "HELLOIMGUI_EMSCRIPTEN_PTHREAD OFF" "HELLOIMGUI_EMSCRIPTEN_PTHREAD_ALLOW_MEMORY_GROWTH OFF" "IMGUI_DISABLE_OBSOLETE_FUNCTIONS ON" ) if(HDRVIEW_BUILD_GUI_TESTS AND NOT EMSCRIPTEN) # fetches imgui_test_engine and links it into the shared `imgui` target, for hdrview_gui_tests below list(APPEND HELLOIMGUI_CPM_OPTIONS "HELLOIMGUI_WITH_TEST_ENGINE ON") endif() # HDR display output needs two things from this version: RunnerCallbacks::BeforeSwap, the one point in the # render loop where a full-screen pass can still reach the whole frame, and requestFloatBuffer on the # GLFW3+OpenGL backend, not just Metal. The latter needs a GLFW defining GLFW_FLOATBUFFER (see the pin above). CPMAddPackage( NAME hello_imgui GITHUB_REPOSITORY pthom/hello_imgui GIT_TAG v1.92.900 OPTIONS ${HELLOIMGUI_CPM_OPTIONS} EXCLUDE_FROM_ALL YES SYSTEM YES ) if(hello_imgui_ADDED) message(STATUS "Will build hello_imgui library") if(HELLOIMGUI_EMSCRIPTEN_PTHREAD) add_compile_options(-pthread) endif() # Disable warnings for third-party compiled targets disable_target_warnings(imgui) disable_target_warnings(hello_imgui) disable_target_warnings(stb_hello_imgui) disable_target_warnings(plutosvg) elseif(hello_imgui_FOUND OR TARGET HELLOIMGUI::hello_imgui) message(STATUS "Using system-installed hello_imgui library") else() message(FATAL_ERROR "Could not find or download hello_imgui library.") endif() # Must track the Dear ImGui that hello_imgui vendors: implot draws through ImDrawList directly, and # IMGUI_DISABLE_OBSOLETE_FUNCTIONS (set above) deletes superseded overloads instead of keeping shims. CPMAddPackage("gh:epezent/implot#7eeb9168d2e5e6b14e266d8782ecf7e649dfc3a4") if(NOT implot_ADDED) message(FATAL_ERROR "Could not download implot library.") endif() message(STATUS "implot library added") # implot has no CMake support, so we create our own target add_library( implot STATIC ${implot_SOURCE_DIR}/implot.h ${implot_SOURCE_DIR}/implot_internal.h ${implot_SOURCE_DIR}/implot.cpp ${implot_SOURCE_DIR}/implot_items.cpp ${implot_SOURCE_DIR}/implot_demo.cpp ) target_include_directories(implot PUBLIC ${implot_SOURCE_DIR}) target_link_libraries(implot PUBLIC imgui) set_target_properties(implot PROPERTIES CXX_STANDARD 17) disable_target_warnings(implot) mark_as_system_library(implot) list(APPEND HDRVIEW_DEPENDENCIES "implot") CPMAddPackage( NAME imgui_command_palette GITHUB_REPOSITORY wkjarosz/imgui-command-palette GIT_TAG 320bd7903175beab286a85381866c95fd9143ab8 DOWNLOAD_ONLY YES SYSTEM YES ) if(NOT imgui_command_palette_ADDED) message(FATAL_ERROR "Could not download imgui-command-palette library.") endif() message(STATUS "imgui-command-palette library added") add_library( imgui_command_palette "${imgui_command_palette_SOURCE_DIR}/imcmd_command_palette.h" "${imgui_command_palette_SOURCE_DIR}/imcmd_command_palette.cpp" "${imgui_command_palette_SOURCE_DIR}/imcmd_fuzzy_search.h" "${imgui_command_palette_SOURCE_DIR}/imcmd_fuzzy_search.cpp" ) target_compile_features(imgui_command_palette PUBLIC cxx_std_11) target_include_directories(imgui_command_palette PUBLIC ${imgui_command_palette_SOURCE_DIR}) target_link_libraries(imgui_command_palette PUBLIC imgui) disable_target_warnings(imgui_command_palette) mark_as_system_library(imgui_command_palette) list(APPEND HDRVIEW_DEPENDENCIES "imgui_command_palette") CPMAddPackage("gh:sgorsten/linalg@2.2") if(NOT linalg_ADDED) message(FATAL_ERROR "Could not download linalg library.") endif() message(STATUS "linalg library added") add_library(linalg INTERFACE IMPORTED) target_include_directories(linalg INTERFACE "${linalg_SOURCE_DIR}") list(APPEND HDRVIEW_DEPENDENCIES "linalg") CPMAddPackage( NAME spdlog VERSION 1.14.0 # this is the first version that has MDC support, unfortunately ubuntu 22.04 has 1.12.0 URL https://github.com/gabime/spdlog/archive/refs/tags/v1.16.0.zip OPTIONS "SPDLOG_INSTALL NO" # don't create an installable target SYSTEM YES EXCLUDE_FROM_ALL YES ) if(spdlog_ADDED) message(STATUS "Will build spdlog library") elseif(spdlog_FOUND OR TARGET spdlog::spdlog) message(STATUS "Using system-installed spdlog library") else() message(FATAL_ERROR "Could not find or download spdlog library.") endif() list(APPEND HDRVIEW_DEPENDENCIES "spdlog::spdlog") CPMAddPackage("gh:wkjarosz/smallthreadpool#323c20dd0b54e66d7153665e7571612d40b6c48d") if(NOT smallthreadpool_ADDED) message(FATAL_ERROR "Could not download smallthreadpool library.") endif() message(STATUS "smallthreadpool library added") # Create implementation library with wrapper configure_file("${CMAKE_SOURCE_DIR}/cmake/stp_impl.cpp.in" "${CMAKE_CURRENT_BINARY_DIR}/stp_impl.cpp" COPYONLY) add_library(stp STATIC "${CMAKE_CURRENT_BINARY_DIR}/stp_impl.cpp") target_include_directories(stp PUBLIC "${smallthreadpool_SOURCE_DIR}") target_link_libraries(stp PRIVATE spdlog::spdlog) target_compile_features(stp PUBLIC cxx_std_17) # stp doesn't link against hello_imgui, the usual source of HELLOIMGUI_EMSCRIPTEN_PTHREAD, so translate it # into smallthreadpool's own macro name here if(HELLOIMGUI_EMSCRIPTEN_PTHREAD) target_compile_definitions(stp PRIVATE STP_EMSCRIPTEN_PTHREAD) endif() # add_library(stp INTERFACE IMPORTED) # target_include_directories(stp INTERFACE "${smallthreadpool_SOURCE_DIR}") list(APPEND HDRVIEW_DEPENDENCIES "stp") CPMAddPackage("gh:wkjarosz/stb#491e9c3ac2add5bda0fffb5a8cb99868c483506b") if(NOT stb_ADDED) message(FATAL_ERROR "Could not download stb library.") endif() message(STATUS "stb library added") add_library(stb INTERFACE IMPORTED) target_include_directories(stb INTERFACE "${stb_SOURCE_DIR}") list(APPEND HDRVIEW_DEPENDENCIES "stb") CPMAddPackage("gh:phoboslab/qoi#b0b926ee70108b0113dd2718d13d2207c474ed2e") if(NOT qoi_ADDED) message(FATAL_ERROR "Could not download qoi library.") endif() message(STATUS "qoi library added") # Create implementation library with wrapper configure_file("${CMAKE_SOURCE_DIR}/cmake/qoi_impl.cpp.in" "${CMAKE_CURRENT_BINARY_DIR}/qoi_impl.cpp" COPYONLY) add_library(qoi STATIC "${CMAKE_CURRENT_BINARY_DIR}/qoi_impl.cpp") target_include_directories(qoi PUBLIC "${qoi_SOURCE_DIR}") disable_target_warnings(qoi) mark_as_system_library(qoi) list(APPEND HDRVIEW_DEPENDENCIES "qoi") if(EMSCRIPTEN) CPMAddPackage("gh:Armchair-Software/emscripten-browser-file#d21dec772050d581c02aa9820e4d877fdb060dc8") if(NOT emscripten-browser-file_ADDED) message(FATAL_ERROR "Could not download emscripten-browser-file library.") endif() message(STATUS "emscripten-browser-file library added") add_library(emscripten-browser-file INTERFACE IMPORTED) target_include_directories(emscripten-browser-file INTERFACE "${emscripten-browser-file_SOURCE_DIR}") list(APPEND HDRVIEW_DEPENDENCIES "emscripten-browser-file") else() CPMAddPackage( NAME portable-file-dialogs GITHUB_REPOSITORY wkjarosz/portable-file-dialogs GIT_TAG 1d755d814b77e546f544266bc24ac76029b8763b SYSTEM YES EXCLUDE_FROM_ALL YES ) if(NOT portable-file-dialogs_ADDED) message(FATAL_ERROR "Could not download portable-file-dialogs library.") endif() message(STATUS "portable-file-dialogs library added") add_library(portable-file-dialogs INTERFACE IMPORTED) target_include_directories(portable-file-dialogs INTERFACE "${portable-file-dialogs_SOURCE_DIR}") list(APPEND HDRVIEW_DEPENDENCIES "portable-file-dialogs") endif() CPMAddPackage( NAME libexif GITHUB_REPOSITORY Tom94/libexif GIT_TAG f99967d09f1424170388cb2c0b65dcf67d6f5ac3 FIND_PACKAGE_ARGUMENTS "QUIET" OPTIONS "BUILD_SHARED_LIBS OFF" VERSION 0.6.24 SYSTEM YES EXCLUDE_FROM_ALL YES ) if(libexif_ADDED AND TARGET exif_static) message(STATUS "Will build exif library") disable_target_warnings(exif_static) list(APPEND HDRVIEW_DEPENDENCIES "exif_static") elseif(libexif_FOUND AND TARGET exif) message(STATUS "Using system-installed exif library (version ${LIBEXIF_VERSION})") list(APPEND HDRVIEW_DEPENDENCIES "exif") else() message(FATAL_ERROR "Could not find or download exif library.") endif() # TinyXML-2 for XMP parsing CPMAddPackage( NAME tinyxml2 # GITHUB_REPOSITORY leethomason/tinyxml2 # GIT_TAG 9.0.0 URL https://github.com/leethomason/tinyxml2/archive/refs/tags/11.0.0.zip VERSION 11.0.0 EXCLUDE_FROM_ALL YES DOWNLOAD_ONLY YES SYSTEM YES ) if(tinyxml2_ADDED) message(STATUS "tinyxml2 library added") add_library( tinyxml2 "${tinyxml2_SOURCE_DIR}/tinyxml2.h" "${tinyxml2_SOURCE_DIR}/tinyxml2.cpp" ) target_compile_features(tinyxml2 PUBLIC cxx_std_17) target_include_directories(tinyxml2 PUBLIC "${tinyxml2_SOURCE_DIR}") disable_target_warnings(tinyxml2) mark_as_system_library(tinyxml2) list(APPEND HDRVIEW_DEPENDENCIES "tinyxml2") elseif(TARGET tinyxml2::tinyxml2) # CPM's find_package() result (tinyxml2_FOUND) is scoped to CPM's internal function and never reaches this # scope, but the imported target it creates is global, so check for that instead message(STATUS "Using system-installed tinyxml2 library") list(APPEND HDRVIEW_DEPENDENCIES "tinyxml2::tinyxml2") else() message(FATAL_ERROR "Could not find or download tinyxml2 library.") endif() if(HDRVIEW_ENABLE_LIBPNG OR HDRVIEW_ENABLE_LIBHEIF) # cmake-format: off # Build zlib ourselves if: # - On Windows/Emscripten (system zlib may not be available) # - Building a universal binary on macOS (need both architectures) # - Cross-compiling on macOS (CMAKE_OSX_ARCHITECTURES is set and differs from host) set(NEED_CUSTOM_ZLIB FALSE) if(WIN32 OR MSVC OR EMSCRIPTEN) set(NEED_CUSTOM_ZLIB TRUE) elseif(APPLE AND CMAKE_OSX_ARCHITECTURES) # Check if building universal binary if(CMAKE_OSX_ARCHITECTURES MATCHES ".*arm64.*" AND CMAKE_OSX_ARCHITECTURES MATCHES ".*x86_64.*") set(NEED_CUSTOM_ZLIB TRUE) else() # Check if cross-compiling (target arch differs from host) if(NOT CMAKE_OSX_ARCHITECTURES STREQUAL "${CMAKE_HOST_SYSTEM_PROCESSOR}") set(NEED_CUSTOM_ZLIB TRUE) endif() endif() endif() if(NEED_CUSTOM_ZLIB) CPMAddPackage( NAME zlib-cmake VERSION 1.2.12 URL https://github.com/jimmy-park/zlib-cmake/archive/main.tar.gz #FIND_PACKAGE_ARGUMENTS "NAMES zlib ZLIB" OPTIONS "ZLIB_TEST OFF" "ZLIB_INSTALL ON" SYSTEM YES EXCLUDE_FROM_ALL YES ) if(zlib-cmake_ADDED) message(STATUS "Will build zlib library") set_target_properties(ZLIB PROPERTIES POSITION_INDEPENDENT_CODE TRUE) disable_target_warnings(ZLIB) elseif(TARGET ZLIB::ZLIB) # Target exists but wasn't built by CPM, so it was found by find_package() if(DEFINED ZLIB_VERSION_STRING) message(STATUS "Using system-installed zlib library (version ${ZLIB_VERSION_STRING})") else() message(STATUS "Using system-installed zlib library") endif() else() message(FATAL_ERROR "zlib not found.") endif() endif() # cmake-format: on endif() if(HDRVIEW_ENABLE_LIBPNG) CPMAddPackage( NAME PNG GITHUB_REPOSITORY pnggroup/libpng GIT_TAG 62954d33fe729566e9d17b78ddaf013896a1f3c5 VERSION 1.6.0 # URL https://github.com/pnggroup/libpng/archive/refs/tags/v1.6.51.zip OPTIONS "PNG_SHARED OFF" "PNG_STATIC ON" "PNG_FRAMEWORK OFF" "PNG_TESTS OFF" "PNG_TOOLS OFF" "PNG_ARM_NEON off" # for now we disable NEON to avoid build issues on some CI machines "SKIP_INSTALL_ALL ON" EXCLUDE_FROM_ALL YES SYSTEM YES ) if(PNG_ADDED) message(STATUS "Will build libpng library") # set_target_properties(png_shared PROPERTIES POSITION_INDEPENDENT_CODE TRUE) set_target_properties(png_static # PROPERTIES POSITION_INDEPENDENT_CODE TRUE) # mark_as_system_library(png_static) disable_target_warnings(png_static) list(APPEND HDRVIEW_DEPENDENCIES "png_static") elseif(TARGET PNG::PNG) message(STATUS "Using system-installed libpng library") list(APPEND HDRVIEW_DEPENDENCIES "PNG::PNG") else() message(FATAL_ERROR "libpng not found. Please install it or turn off HDRVIEW_ENABLE_LIBPNG.") endif() endif() CPMAddPackage( NAME Imath VERSION 3.1.9 # we set the minimium version to 3.1.9, but if manually building use a more recent version FIND_PACKAGE_ARGUMENTS "REQUIRED" # the git repo is incredibly large, so we download the archived include directory URL https://github.com/AcademySoftwareFoundation/Imath/archive/refs/tags/v3.1.12.zip OPTIONS "BUILD_SHARED_LIBS OFF" "IMATH_INSTALL OFF" "IMATH_INSTALL_PKG_CONFIG OFF" "BUILD_TESTING OFF" EXCLUDE_FROM_ALL YES ) if(Imath_ADDED) message(STATUS "Will build Imath library") elseif(TARGET Imath::Imath) message(STATUS "Will link against system-installed Imath library") else() message(FATAL_ERROR "Imath library not found. Please install it.") endif() if(EMSCRIPTEN AND NOT HELLOIMGUI_EMSCRIPTEN_PTHREAD) set(OPENEXR_THREADING_OPTIONS "OPENEXR_ENABLE_THREADING OFF") else() set(OPENEXR_THREADING_OPTIONS "OPENEXR_ENABLE_THREADING ON") endif() CPMAddPackage( NAME OpenEXR # GITHUB_REPOSITORY AcademySoftwareFoundation/openexr FIND_PACKAGE_ARGUMENTS "REQUIRED" # NOTE: OpenEXR v3.3.0 broke subsampled channel loading, but it seems to be fixed starting with v3.4 VERSION 3.1.5 # this is the version available in ubuntu 24.04 GIT_TAG v3.4.13 URL https://github.com/AcademySoftwareFoundation/openexr/archive/refs/tags/v3.4.13.zip OPTIONS "BUILD_SHARED_LIBS OFF" "BUILD_TESTING OFF" "OPENEXR_FORCE_INTERNAL_DEFLATE ON" "OPENEXR_BUILD_TOOLS OFF" "OPENEXR_BUILD_EXAMPLES OFF" "OPENEXR_INSTALL OFF" "OPENEXR_INSTALL_TOOLS OFF" "OPENEXR_INSTALL_EXAMPLES OFF" "OPENEXR_INSTALL_PKG_CONFIG OFF" ${OPENEXR_THREADING_OPTIONS} EXCLUDE_FROM_ALL YES SYSTEM YES ) if(OpenEXR_ADDED) message(STATUS "Will build OpenEXR library with threading options: ${OPENEXR_THREADING_OPTIONS}") # Disable warnings for all OpenEXR component targets disable_target_warnings(OpenEXR) disable_target_warnings(Iex) disable_target_warnings(IlmThread) disable_target_warnings(OpenEXRCore) disable_target_warnings(OpenEXRUtil) disable_target_warnings(Imath) elseif(TARGET OpenEXR::OpenEXR) message(STATUS "Will link against system-installed OpenEXR library") else() message(FATAL_ERROR "OpenEXR library not found. Please install it.") endif() list(APPEND HDRVIEW_DEPENDENCIES "OpenEXR::OpenEXR") # cmake-format: off # libjpeg-turbo if(HDRVIEW_ENABLE_LIBJPEG) find_package(JPEG) if(JPEG_FOUND) list(APPEND HDRVIEW_DEFINITIONS "HDRVIEW_ENABLE_LIBJPEG=1") # Test whether libjpeg provides jpeg_write_icc_profile include(CheckCXXSourceCompiles) set(JPEG_ICC_TEST_SOURCE " #include #include #include int main() { jpeg_write_icc_profile((j_compress_ptr)0, (const JOCTET*)0, 0); return 0; } " ) # Prefer linking via imported target if available if(TARGET JPEG::JPEG) # write source and run try_compile linking the imported target file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/cmake_check_jpeg_icc.cpp" "${JPEG_ICC_TEST_SOURCE}") try_compile(HAVE_JPEG_WRITE_ICC_PROFILE "${CMAKE_CURRENT_BINARY_DIR}/cmake_check_jpeg_icc" SOURCES "${CMAKE_CURRENT_BINARY_DIR}/cmake_check_jpeg_icc.cpp" LINK_LIBRARIES JPEG::JPEG ) else() # fallback: use the existing check with required includes/libs set(CMAKE_REQUIRED_LIBRARIES ${JPEG_LIBRARIES}) set(CMAKE_REQUIRED_INCLUDES ${JPEG_INCLUDE_DIRS}) check_cxx_source_compiles("${JPEG_ICC_TEST_SOURCE}" HAVE_JPEG_WRITE_ICC_PROFILE) unset(CMAKE_REQUIRED_LIBRARIES) unset(CMAKE_REQUIRED_INCLUDES) endif() message(STATUS "Found libjpeg with HAVE_JPEG_WRITE_ICC_PROFILE=${HAVE_JPEG_WRITE_ICC_PROFILE}") find_package(TurboJpeg QUIET) message(STATUS "JPEG is libjpeg-turbo = ${TurboJpeg_FOUND}") mark_as_system_library(JPEG::JPEG) list(APPEND HDRVIEW_DEPENDENCIES JPEG::JPEG) else() message(STATUS "libjpeg library not found, will build libjpeg-turbo") set(JPEGTURBO_TARGET_NAME turbojpeg-hdrview) set(JPEGTURBO_PREFIX_DIR ${CMAKE_CURRENT_BINARY_DIR}/${JPEGTURBO_TARGET_NAME}) set(JPEGTURBO_SOURCE_DIR ${JPEGTURBO_PREFIX_DIR}/source) set(JPEGTURBO_BINARY_DIR ${JPEGTURBO_PREFIX_DIR}/build) file(MAKE_DIRECTORY ${JPEGTURBO_SOURCE_DIR}/src) set(JPEG_INCLUDE_DIRS ${JPEGTURBO_BINARY_DIR} ${JPEGTURBO_SOURCE_DIR}/src) if(MSVC) set(JPEG_LIB ${CMAKE_STATIC_LIBRARY_PREFIX}jpeg-static${CMAKE_STATIC_LIBRARY_SUFFIX}) else() set(JPEG_LIB ${CMAKE_STATIC_LIBRARY_PREFIX}jpeg${CMAKE_STATIC_LIBRARY_SUFFIX}) endif() if(IS_MULTI) set(JPEG_LIB_PREFIX ${JPEGTURBO_BINARY_DIR}/$/) else() set(JPEG_LIB_PREFIX ${JPEGTURBO_BINARY_DIR}/) endif() set(JPEG_LIBRARIES ${JPEG_LIB_PREFIX}${JPEG_LIB}) message(STATUS "JPEGTURBO_BINARY_DIR: ${JPEGTURBO_BINARY_DIR}") message(STATUS "JPEGTURBO_SOURCE_DIR: ${JPEGTURBO_SOURCE_DIR}") include(ExternalProject) if(EMSCRIPTEN) ExternalProject_Add( ${JPEGTURBO_TARGET_NAME} GIT_REPOSITORY https://github.com/libjpeg-turbo/libjpeg-turbo.git GIT_TAG 3.1.0 PREFIX ${JPEGTURBO_PREFIX_DIR} SOURCE_DIR ${JPEGTURBO_SOURCE_DIR} BINARY_DIR ${JPEGTURBO_BINARY_DIR} CONFIGURE_COMMAND emcmake cmake ${JPEGTURBO_SOURCE_DIR} -DENABLE_SHARED=0 -DWITH_SIMD=0 BUILD_COMMAND ${CMAKE_COMMAND} --build --config $ --target jpeg-static BUILD_BYPRODUCTS ${JPEG_LIBRARIES} INSTALL_COMMAND "" ) else() ExternalProject_Add( ${JPEGTURBO_TARGET_NAME} GIT_REPOSITORY https://github.com/libjpeg-turbo/libjpeg-turbo.git GIT_TAG 3.1.0 PREFIX ${JPEGTURBO_PREFIX_DIR} SOURCE_DIR ${JPEGTURBO_SOURCE_DIR} BINARY_DIR ${JPEGTURBO_BINARY_DIR} BUILD_COMMAND ${CMAKE_COMMAND} --build --config $ --target jpeg-static CMAKE_ARGS -DENABLE_SHARED=0 -DCMAKE_OSX_DEPLOYMENT_TARGET=${CMAKE_OSX_DEPLOYMENT_TARGET} -DCMAKE_OSX_ARCHITECTURES=${CMAKE_OSX_ARCHITECTURES} -DWITH_JPEG8=1 -DWITH_TURBOJPEG=1 BUILD_BYPRODUCTS ${JPEG_LIBRARIES} INSTALL_COMMAND "" ) # ExternalProject_Add_Step(${JPEGTURBO_TARGET_NAME} print_jpeglib_h # COMMAND ${CMAKE_COMMAND} -E echo "========== src/jpeglib.h ==========" # COMMAND ${CMAKE_COMMAND} -E cat ${JPEGTURBO_SOURCE_DIR}/src/jpeglib.h # DEPENDEES download # ALWAYS 1 # ) endif() # add_custom_command( # TARGET ${JPEGTURBO_TARGET_NAME} # POST_BUILD # COMMAND ${CMAKE_COMMAND} -E copy_if_different # ${JPEGTURBO_SOURCE_DIR}/src/jconfig.h # ${JPEGTURBO_BINARY_DIR}/jconfig.h # COMMENT "Copying jconfig.h to build directory" # ) # The following variables ensure that find_package can find libjpeg-turbo set(JPEG_LIBRARY ${JPEG_LIBRARIES}) # Expose both the libjpeg-turbo build/include (binary dir) and the # source include directory so external projects (CPM/ExternalProject) # that look for `JPEG_INCLUDE_DIR`/`JPEG_INCLUDE_DIRS` will find # the generated `jconfig.h` as well as the public headers. # Provide a path that some CMake Find modules expect to be a file # (e.g. FindJPEG may read the file with file(STRINGS ...)). Point it # at the primary header so modules don't accidentally try to read a # directory as a file on Windows. set(JPEG_INCLUDE_DIR ${JPEGTURBO_SOURCE_DIR}/src/jpeglib.h CACHE INTERNAL "JPEG include dir") set(JPEG_INCLUDE_DIRS "${JPEGTURBO_BINARY_DIR};${JPEGTURBO_SOURCE_DIR}/src" CACHE INTERNAL "JPEG include dirs") set(HAVE_JPEG_WRITE_ICC_PROFILE TRUE) add_library(jpeg-turbo INTERFACE IMPORTED GLOBAL) add_dependencies(jpeg-turbo ${JPEGTURBO_TARGET_NAME}) set_target_properties( jpeg-turbo PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${JPEG_INCLUDE_DIRS}" INTERFACE_LINK_LIBRARIES "${JPEG_LIBRARIES}" ) add_library(JPEG::JPEG ALIAS jpeg-turbo) # disable_target_warnings(jpeg-turbo) mark_as_system_library(jpeg-turbo) list(APPEND HDRVIEW_DEPENDENCIES jpeg-turbo) # Pre-populate JPEG variables so other dependency's find_package(JPEG) will use this one set(JPEG_FOUND TRUE CACHE INTERNAL "JPEG found") set(JPEG_LIBRARIES JPEG::JPEG CACHE INTERNAL "JPEG library") get_target_property(JPEG_INCLUDE_DIRS_TMP JPEG::JPEG INTERFACE_INCLUDE_DIRECTORIES) if(JPEG_INCLUDE_DIRS_TMP) # Try to find a usable header path from the interface include directories. # Prefer an explicit file entry, then a directory that contains jpeglib.h. set(_JPEG_HEADER_FOUND "") foreach(_inc IN LISTS JPEG_INCLUDE_DIRS_TMP) if(EXISTS "${_inc}" AND NOT IS_DIRECTORY "${_inc}") # _inc is a file (some packages export a direct header path) set(_JPEG_HEADER_FOUND "${_inc}") break() endif() if(EXISTS "${_inc}/jpeglib.h") set(_JPEG_HEADER_FOUND "${_inc}/jpeglib.h") break() endif() endforeach() if(_JPEG_HEADER_FOUND) set(JPEG_INCLUDE_DIR "${_JPEG_HEADER_FOUND}" CACHE INTERNAL "JPEG include dir" FORCE) else() # Fallback: use the source header so FindJPEG won't try to read a directory on Windows. set(JPEG_INCLUDE_DIR "${JPEGTURBO_SOURCE_DIR}/src/jpeglib.h" CACHE INTERNAL "JPEG include dir" FORCE) endif() unset(_JPEG_HEADER_FOUND) endif() set(JPEG_VERSION TRUE CACHE INTERNAL 80) message(STATUS "Providing JPEG target to other dependencies") endif() if (HAVE_JPEG_WRITE_ICC_PROFILE) add_definitions(-DHAVE_JPEG_WRITE_ICC_PROFILE=1) endif () message(STATUS "JPEG_LIBRARIES: ${JPEG_LIBRARIES}") message(STATUS "JPEG_INCLUDE_DIR: ${JPEG_INCLUDE_DIR}") message(STATUS "JPEG_INCLUDE_DIRS: ${JPEG_INCLUDE_DIRS}") endif() # cmake-format: on # cmake-format: off if(HDRVIEW_ENABLE_LIBUHDR) CPMAddPackage( NAME libuhdr GITHUB_REPOSITORY google/libultrahdr # VERSION 1.4.0 GIT_TAG 5ed39d67cd31d254e84ebf76b03d4b7bcc12e2f7 OPTIONS "UHDR_BUILD_BENCHMARK OFF" "UHDR_BUILD_FUZZERS OFF" "JPEG_FOUND TRUE" "BUILD_SHARED_LIBS OFF" "UHDR_BUILD_DEPS ${UHDR_BUILD_DEPS}" "UHDR_BUILD_EXAMPLES OFF" "UHDR_BUILD_TESTS OFF" "UHDR_ENABLE_INSTALL OFF" SYSTEM YES EXCLUDE_FROM_ALL YES ) if(libuhdr_ADDED) message(STATUS "Will build uhdr library with UHDR_BUILD_DEPS=${UHDR_BUILD_DEPS}") disable_target_warnings(uhdr) elseif(TARGET core) message(STATUS "Will link against system-installed uhdr library") else() message(FATAL_ERROR "uhdr library not found. Please install it or turn off HDRVIEW_ENABLE_LIBUHDR.") endif() message(STATUS "setting uhdr dependent on ${JPEGTURBO_TARGET_NAME}") if (TARGET core) add_dependencies(core JPEG::JPEG) list(APPEND HDRVIEW_DEPENDENCIES "core") endif() if (TARGET uhdr) add_dependencies(uhdr JPEG::JPEG) list(APPEND HDRVIEW_DEPENDENCIES "uhdr") endif() endif() # cmake-format: on if(HDRVIEW_ENABLE_LIBJXL) # Set Highway options before CPMAddPackage so they affect the transitive dependency set(HWY_ENABLE_TESTS OFF) set(HWY_ENABLE_INSTALL OFF) set(HWY_ENABLE_EXAMPLES OFF) set(HWY_ENABLE_CONTRIB OFF) CPMAddPackage( NAME JXL GITHUB_REPOSITORY libjxl/libjxl # VERSION 0.7.0 # version in ubuntu 24.04 VERSION 0.11.1 URL https://github.com/libjxl/libjxl/archive/refs/tags/v0.11.1.zip # GIT_TAG 6e83d97e73c68ff7d3374f939971bab617be4c3c OPTIONS "BUILD_SHARED_LIBS OFF" "BUILD_TESTING OFF" "JPEGXL_ENABLE_FUZZERS OFF" "JPEGXL_ENABLE_DEVTOOLS OFF" "JPEGXL_ENABLE_TOOLS OFF" "JPEGXL_ENABLE_BENCHMARK OFF" "JPEGXL_ENABLE_EXAMPLES OFF" "JPEGXL_ENABLE_JNI OFF" "JPEGXL_ENABLE_OPENEXR OFF" "JPEGXL_ENABLE_DOXYGEN OFF" "JPEGXL_ENABLE_MANPAGES OFF" "JPEGXL_ENABLE_JPEGLI_LIBJPEG OFF" "JPEGXL_ENABLE_SKCMS OFF" "JPEGXL_ENABLE_VIEWERS OFF" "JPEGXL_ENABLE_PLUGINS OFF" "JPEGXL_BUNDLE_LIBPNG OFF" "JPEGXL_ENABLE_SJPEG OFF" # "JPEGXL_FORCE_SYSTEM_LCMS2 ON" SYSTEM YES EXCLUDE_FROM_ALL YES ) if(JXL_ADDED) message(STATUS "Will build JPEG-XL library") # make the lcms2 target available for us to link against # target_link_libraries(jxl PUBLIC lcms2) # Disable warnings for all jxl targets disable_target_warnings(jxl) disable_target_warnings(jxl_dec) # disable_target_warnings(jxl_enc) disable_target_warnings(jxl_threads) disable_target_warnings(jxl_dec-obj) disable_target_warnings(jxl_enc-obj) disable_target_warnings(jxl_dec-internal) disable_target_warnings(jxl-internal) disable_target_warnings(jxl_cms) disable_target_warnings(enc_fast_lossless) disable_target_warnings(hwy) # After CPM adds JXL, use the source directory variable if(JXL_SOURCE_DIR) message(STATUS "Adding include directory for bundled Highway: ${JXL_SOURCE_DIR}/third_party/highway") # Prioritize the bundled Highway over system installation include_directories(BEFORE SYSTEM ${JXL_SOURCE_DIR}/third_party/highway ) else() message(STATUS "Adding include directory for bundled Highway: ${CMAKE_BINARY_DIR}/cpm_modules/jxl/39d3/third_party/highway") include_directories(BEFORE SYSTEM ${CMAKE_BINARY_DIR}/cpm_modules/jxl/39d3/third_party/highway ) endif() elseif(TARGET jxl) message(STATUS "Using system-installed JPEG-XL library") else() message(FATAL_ERROR "JPEG-XL library not found. Please install it or turn off HDRVIEW_ENABLE_LIBJXL.") endif() list(APPEND HDRVIEW_DEPENDENCIES "jxl" "jxl_threads") endif() if(TARGET lcms2) message(STATUS "Using lcms2 target already provided by libjxl") # list(APPEND HDRVIEW_DEPENDENCIES lcms2) list(APPEND HDRVIEW_DEFINITIONS "HDRVIEW_ENABLE_LCMS2=1") # Set cache variables for so other dependency's find_package(LCMS2) finds this one # Get the include directories from the lcms2 target get_target_property(LCMS2_INCLUDE_DIRS lcms2 INTERFACE_INCLUDE_DIRECTORIES) if(NOT LCMS2_INCLUDE_DIRS) get_target_property(LCMS2_INCLUDE_DIRS lcms2 INCLUDE_DIRECTORIES) endif() # Note: FindLCMS2 checks for LCMS2_INCLUDE_DIR (singular) not LCMS2_INCLUDE_DIRS (plural) set(LCMS2_INCLUDE_DIR "${LCMS2_INCLUDE_DIRS}" CACHE PATH "LCMS2 include dir from jxl" FORCE) set(LCMS2_LIBRARIES lcms2 CACHE STRING "LCMS2 library from jxl" FORCE) set(LCMS2_FOUND TRUE CACHE BOOL "LCMS2 found via jxl" FORCE) message(STATUS "Providing lcms2 target (from libjxl) to other dependencies: ${LCMS2_INCLUDE_DIR}") else() find_package(LCMS2 REQUIRED) if(LCMS2_FOUND) message(STATUS "Using system-installed lcms2 library") mark_as_system_library(lcms2) list(APPEND HDRVIEW_DEPENDENCIES "lcms2") list(APPEND HDRVIEW_DEFINITIONS "HDRVIEW_ENABLE_LCMS2=1") else() message(FATAL_ERROR "lcms2 library not found.") endif() endif() # libheif finds its codecs with find_package(), which only turns up an installed library, and Emscripten has # none, so AVIF's AV1 codec is built here. CPM already leaves a redirect in CMAKE_FIND_PACKAGE_REDIRECTS_DIR # pointing libheif's find_package(AOM) at this build; the -extra.cmake below fills in the rest of what # libheif's own FindAOM.cmake would have set. if(EMSCRIPTEN AND HDRVIEW_ENABLE_AVIF) # aom appends the compiler flags it wants straight to the cache with FORCE. A cache entry is global and # read at generate time, so those flags reach every target in the build; the -std=c99 among them stops # OpenEXR's C sources compiling. Snapshot what aom rewrites and put both the cache entry and the # directory-scope variable back afterwards, or a configure bakes this directory's flags into the cache. set(HDRVIEW_AOM_CLOBBERED_VARS "") foreach(_flags CMAKE_C_FLAGS CMAKE_CXX_FLAGS CMAKE_EXE_LINKER_FLAGS) foreach(_config "" "_DEBUG" "_RELEASE" "_MINSIZEREL" "_RELWITHDEBINFO") list(APPEND HDRVIEW_AOM_CLOBBERED_VARS "${_flags}${_config}") endforeach() endforeach() foreach(_var IN LISTS HDRVIEW_AOM_CLOBBERED_VARS) set("HDRVIEW_SAVED_${_var}" "${${_var}}") set("HDRVIEW_SAVED_CACHE_${_var}" "$CACHE{${_var}}") get_property(_doc CACHE "${_var}" PROPERTY HELPSTRING) set("HDRVIEW_SAVED_DOC_${_var}" "${_doc}") endforeach() CPMAddPackage( NAME aom VERSION 3.13.1 URL https://storage.googleapis.com/aom-releases/libaom-3.13.1.tar.gz OPTIONS # no SIMD path, no thread pool, and no CPU to probe: wasm has none of aom's assembly, and this # build has no pthreads "AOM_TARGET_CPU generic" "CONFIG_MULTITHREAD 0" "CONFIG_RUNTIME_CPU_DETECT 0" "CONFIG_AV1_DECODER 1" "CONFIG_AV1_ENCODER 1" # container and scaling code only aom's own command-line tools use "CONFIG_WEBM_IO 0" "CONFIG_LIBYUV 0" "ENABLE_EXAMPLES OFF" "ENABLE_TESTS OFF" "ENABLE_TOOLS OFF" "ENABLE_DOCS OFF" "BUILD_SHARED_LIBS OFF" SYSTEM YES EXCLUDE_FROM_ALL YES ) foreach(_var IN LISTS HDRVIEW_AOM_CLOBBERED_VARS) set("${_var}" "${HDRVIEW_SAVED_CACHE_${_var}}" CACHE STRING "${HDRVIEW_SAVED_DOC_${_var}}" FORCE) set("${_var}" "${HDRVIEW_SAVED_${_var}}") endforeach() if(NOT aom_ADDED) message(FATAL_ERROR "Failed to fetch libaom, which the Emscripten build needs for AVIF support.") endif() message(STATUS "Will build libaom library") mark_as_system_library(aom) disable_target_warnings(aom) # aom publishes its headers only to an install tree, so libheif's plugins include aom/*.h from the sources file( WRITE "${CMAKE_FIND_PACKAGE_REDIRECTS_DIR}/aom-extra.cmake" "set(AOM_LIBRARIES aom)\n" "set(AOM_INCLUDE_DIRS \"${aom_SOURCE_DIR}\")\n" "set(AOM_DECODER_FOUND TRUE)\n" "set(AOM_ENCODER_FOUND TRUE)\n" ) endif() # OpenJPEG serves two consumers: imageio/j2k.cpp, which reads and writes standalone JPEG 2000 files, and # libheif's J2K/HTJ2K plugins, which handle the same codestreams wrapped in a HEIF item. libheif finds it # only through find_package(OpenJPEG), which turns up nothing under Emscripten and nothing on a bare Windows # runner, so build it here when no installed copy answers and hand libheif the result via the redirect file. # Both paths leave the same `openjp2` target name. if(HDRVIEW_ENABLE_J2K) CPMAddPackage( NAME OpenJPEG VERSION 2.5.0 # 2.5.0 is the first release whose decoder handles HTJ2K codestreams GITHUB_REPOSITORY uclouvain/openjpeg GIT_TAG v2.5.4 FIND_PACKAGE_ARGUMENTS "QUIET" OPTIONS "BUILD_SHARED_LIBS OFF" "BUILD_CODEC OFF" "BUILD_TESTING OFF" "BUILD_PKGCONFIG_FILES OFF" SYSTEM YES EXCLUDE_FROM_ALL YES ) if(OpenJPEG_ADDED) # OpenJPEG puts the legacy EXECUTABLE_OUTPUT_PATH and LIBRARY_OUTPUT_PATH into the cache, which is # global, so left there every target in the build lands in OpenJPEG's bin directory. Nothing else sets # them. unset(EXECUTABLE_OUTPUT_PATH CACHE) unset(LIBRARY_OUTPUT_PATH CACHE) message(STATUS "Will build OpenJPEG library") mark_as_system_library(openjp2) disable_target_warnings(openjp2) # openjpeg.h lives beside the sources, and the opj_config.h it includes is generated into the build tree. # OpenJPEG publishes both only to an install tree, so state them for the build tree: on the target for # our own use, and in the redirect file for libheif's find module. target_include_directories(openjp2 INTERFACE $ $ ) file( WRITE "${CMAKE_FIND_PACKAGE_REDIRECTS_DIR}/openjpeg-extra.cmake" "set(OPENJPEG_FOUND TRUE)\n" "set(OPENJPEG_LIBRARIES openjp2)\n" "set(OPENJPEG_INCLUDE_DIRS \"${OpenJPEG_SOURCE_DIR}/src/lib/openjp2;${OpenJPEG_BINARY_DIR}/src/lib/openjp2\")\n" ) elseif(TARGET openjp2) message(STATUS "Using system-installed OpenJPEG library") else() message(FATAL_ERROR "OpenJPEG not found. Please install it or turn off HDRVIEW_ENABLE_J2K.") endif() list(APPEND HDRVIEW_DEPENDENCIES "openjp2") endif() # dav1d decodes the AV1 in an AVIF about twice as fast as libaom's decoder, to the same pixels, so libheif # prefers it (plugin priority 150 vs 100) when both are compiled in. libaom stays in the build either way: # it is the AVIF encoder, and its decoder is the fallback when dav1d is absent. # # dav1d builds only under meson, so there is no CPM fallback: this uses a dav1d the system already has, and # leaves libheif on libaom when there is none. Emscripten never has one. if(HDRVIEW_ENABLE_DAV1D AND HDRVIEW_ENABLE_AVIF AND NOT EMSCRIPTEN) find_package(PkgConfig QUIET) if(PkgConfig_FOUND) pkg_check_modules(HDRVIEW_DAV1D QUIET dav1d) endif() if(NOT HDRVIEW_DAV1D_FOUND) find_path(HDRVIEW_DAV1D_INCLUDE_DIR NAMES dav1d/dav1d.h) find_library(HDRVIEW_DAV1D_LIBRARY NAMES dav1d libdav1d) if(HDRVIEW_DAV1D_INCLUDE_DIR AND HDRVIEW_DAV1D_LIBRARY) set(HDRVIEW_DAV1D_FOUND TRUE) endif() endif() if(HDRVIEW_DAV1D_FOUND) # libheif's dav1d plugin sets Dav1dSettings::n_threads, which arrived in dav1d 1.0.0; against an older # dav1d (Ubuntu 22.04 ships 0.9.2) the plugin, and with it all of libheif, fails to compile. Probe the # header for the member, since the find_path fallback above locates a dav1d without learning its version. include(CheckStructHasMember) set(CMAKE_REQUIRED_INCLUDES ${HDRVIEW_DAV1D_INCLUDE_DIRS} ${HDRVIEW_DAV1D_INCLUDE_DIR}) check_struct_has_member("Dav1dSettings" n_threads "dav1d/dav1d.h" HDRVIEW_DAV1D_HAS_N_THREADS LANGUAGE C) unset(CMAKE_REQUIRED_INCLUDES) if(NOT HDRVIEW_DAV1D_HAS_N_THREADS) message(STATUS "dav1d ${HDRVIEW_DAV1D_VERSION} is too old for libheif's dav1d plugin, which needs " "1.0.0 or newer; AVIF will be decoded with libaom." ) set(HDRVIEW_ENABLE_DAV1D OFF) endif() else() message(STATUS "dav1d not found; AVIF will be decoded with libaom. Install dav1d's development " "package for roughly twice the AVIF decoding speed." ) set(HDRVIEW_ENABLE_DAV1D OFF) endif() else() set(HDRVIEW_ENABLE_DAV1D OFF) endif() if(HDRVIEW_ENABLE_LIBHEIF) CPMAddPackage( NAME Libheif # GITHUB_REPOSITORY strukturag/libheif VERSION 1.17.6 # set minimum version to the one in ubuntu 24.04 URL https://github.com/strukturag/libheif/archive/refs/tags/v1.23.3.zip # GIT_TAG 95428fdf22b2e0ec3ad4515c882fa0d6dd663f0a OPTIONS "BUILD_SHARED_LIBS OFF" "BUILD_TESTING OFF" "WITH_EXAMPLES OFF" "WITH_EXAMPLE_HEIF_VIEW OFF" "WITH_FUZZERS OFF" "WITH_GDK_PIXBUF OFF" "WITH_INSTALL OFF" "CMAKE_COMPILE_WARNING_AS_ERROR OFF" # codecs "ENABLE_PLUGIN_LOADING OFF" "WITH_LIBSHARPYUV OFF" "WITH_LIBDE265 ${HDRVIEW_ENABLE_HEIC}" "WITH_X265 ${HDRVIEW_ENABLE_HEIC}" "WITH_OpenJPEG_ENCODER ${HDRVIEW_ENABLE_J2K}" "WITH_OpenJPEG_DECODER ${HDRVIEW_ENABLE_J2K}" "WITH_OPENJPH_ENCODER ${HDRVIEW_ENABLE_HTJ2K}" "WITH_OpenH264_ENCODER ${HDRVIEW_ENABLE_AVCI}" "WITH_OpenH264_DECODER ${HDRVIEW_ENABLE_AVCI}" "WITH_JPEG_ENCODER ${HDRVIEW_ENABLE_LIBJPEG}" "WITH_JPEG_DECODER ${HDRVIEW_ENABLE_LIBJPEG}" "WITH_AOM_ENCODER ${HDRVIEW_ENABLE_AVIF}" "WITH_AOM_DECODER ${HDRVIEW_ENABLE_AVIF}" "WITH_DAV1D ${HDRVIEW_ENABLE_DAV1D}" "HAVE_JPEG_WRITE_ICC_PROFILE ${HAVE_JPEG_WRITE_ICC_PROFILE}" SYSTEM YES EXCLUDE_FROM_ALL YES ) if(Libheif_ADDED) message(STATUS "Will build libheif library") if(HDRVIEW_ENABLE_DAV1D) message(STATUS "Will decode AVIF with dav1d ${HDRVIEW_DAV1D_VERSION}") endif() if(EMSCRIPTEN) # leaves out the embind bindings for libheif's JavaScript API, which fail to compile because they bind # raw pointers, an error in current embind; the same switch libheif's build-emscripten.sh uses target_compile_definitions(heif PRIVATE __EMSCRIPTEN_STANDALONE_WASM__=1) endif() # Remove the doc_doxygen target created with 'ALL' if(TARGET doc_doxygen) set_target_properties(doc_doxygen PROPERTIES EXCLUDE_FROM_ALL TRUE) endif() target_include_directories( heif INTERFACE $ $ $ ) # Ensure heif waits for turbojpeg to be built if(TARGET ${JPEGTURBO_TARGET_NAME}) add_dependencies(heif ${JPEGTURBO_TARGET_NAME}) endif() # Ensure heif has the JPEG include paths and link information so its plugins can find jpeglib.h if(TARGET JPEG::JPEG) target_link_libraries(heif PRIVATE JPEG::JPEG) elseif(JPEG_INCLUDE_DIRS) target_include_directories(heif PRIVATE ${JPEG_INCLUDE_DIRS}) endif() mark_as_system_library(heif) disable_target_warnings(heif) elseif(TARGET heif) # a system libheif brings whichever decoders the distribution compiled into it, so WITH_DAV1D above was # never read; the image info panel reports which AV1 decoder libheif chose set(HDRVIEW_ENABLE_DAV1D OFF) message(STATUS "Using system-installed libheif library") else() message( FATAL_ERROR "libheif not found. Please install it or turn off HDRVIEW_ENABLE_J2K, HDRVIEW_ENABLE_HTJ2K, HDRVIEW_ENABLE_AVIF, HDRVIEW_ENABLE_HEIC, and HDRVIEW_ENABLE_AVCI." ) endif() list(APPEND HDRVIEW_DEPENDENCIES "heif") endif() if(HDRVIEW_ENABLE_LIBWEBP) # Disable SIMD for Emscripten to avoid AVX2 pointer type issues if(EMSCRIPTEN) set(WEBP_SIMD_ENABLED OFF) else() set(WEBP_SIMD_ENABLED ON) endif() CPMAddPackage( NAME libwebp VERSION 1.3.2 # version in ubuntu 24.04 URL https://github.com/webmproject/libwebp/archive/refs/tags/v1.6.0.zip FIND_PACKAGE_ARGUMENTS "QUIET" # DOWNLOAD_ONLY YES OPTIONS "WEBP_LINK_STATIC ON" "WEBP_ENABLE_SIMD ${WEBP_SIMD_ENABLED}" "WEBP_BUILD_ANIM_UTILS OFF" "WEBP_BUILD_CWEBP OFF" "WEBP_BUILD_DWEBP OFF" "WEBP_BUILD_GIF2WEBP OFF" "WEBP_BUILD_IMG2WEBP OFF" "WEBP_BUILD_VWEBP OFF" "WEBP_BUILD_WEBPINFO OFF" "WEBP_BUILD_LIBWEBPMUX OFF" "WEBP_BUILD_WEBPMUX ON" "WEBP_BUILD_EXTRAS OFF" "WEBP_BUILD_WEBP_JS OFF" "WEBP_BUILD_FUZZTEST OFF" "WEBP_USE_THREAD ON" "WEBP_NEAR_LOSSLESS ON" "WEBP_ENABLE_SWAP_16BIT_CSP ON" SYSTEM YES EXCLUDE_FROM_ALL YES ) if(libwebp_ADDED) message(STATUS "Will build libwebp library") elseif(libwebp_FOUND AND TARGET webp AND TARGET webpdemux) message(STATUS "Using system-installed libwebp library (version ${WEBP_VERSION})") elseif(TARGET webp AND TARGET webpdemux) message(STATUS "Using previously built libwebp library") else() message(FATAL_ERROR "libwebp library not found. Please install it.") endif() # Link both decoder and encoder libraries, plus demux for metadata extraction list(APPEND HDRVIEW_DEPENDENCIES "webp" "webpdemux") endif() if(HDRVIEW_ENABLE_LIBTIFF) CPMAddPackage( NAME TIFF VERSION 4.7.1 GITHUB_REPOSITORY Tom94/libtiff GIT_TAG 392d93518a813fe407e119aeb8596eb4891f3fd1 OPTIONS "BUILD_SHARED_LIBS OFF" # "HAVE_JPEGTURBO_DUAL_MODE_8_12 TRUE" "tiff-tools OFF" "tiff-tests OFF" "tiff-contrib OFF" "tiff-docs OFF" "tiff-deprecated OFF" "tiff-install OFF" "jbig OFF" "lerc OFF" "lzma OFF" "zstd OFF" SYSTEM YES EXCLUDE_FROM_ALL YES ) if(TIFF_ADDED) message(STATUS "Will build libtiff library") disable_target_warnings(tiff) list(APPEND HDRVIEW_DEPENDENCIES "tiff") elseif(TARGET TIFF::TIFF) message(STATUS "Will link against system-installed libtiff library") list(APPEND HDRVIEW_DEPENDENCIES "TIFF::TIFF") else() message(FATAL_ERROR "libtiff library not found. Please install it.") endif() endif() if(HDRVIEW_ENABLE_LIBRAW) CPMAddPackage( NAME LibRaw_src GITHUB_REPOSITORY LibRaw/LibRaw GIT_TAG 0.21.4 VERSION 0.21.4 DOWNLOAD_ONLY YES SYSTEM YES ) # Before the LibRaw CPMAddPackage: if(WIN32 OR MSVC OR EMSCRIPTEN) set(MATH_LIBRARY "" CACHE STRING "Math library not needed on Windows/MSVC/Emscripten" FORCE) message(STATUS "No math library needed on Windows/MSVC/Emscripten for LibRaw") endif() # Emscripten's OpenMP runtime is pthread-backed, so in a non-pthread build it drags the -mt sysroot # libraries onto a link line that already carries the single-threaded ones and every pthread symbol comes # up duplicated. LibRaw-cmake defaults ENABLE_OPENMP to ON and only probes whether OpenMP works. if(EMSCRIPTEN AND NOT HELLOIMGUI_EMSCRIPTEN_PTHREAD) set(LIBRAW_OPENMP OFF) else() set(LIBRAW_OPENMP ON) endif() CPMAddPackage( NAME LibRaw GITHUB_REPOSITORY LibRaw/LibRaw-cmake GIT_TAG eb98e4325aef2ce85d2eb031c2ff18640ca616d3 VERSION 0.21.0 FIND_PACKAGE_ARGUMENTS "QUIET" OPTIONS "LIBRAW_PATH ${LibRaw_src_SOURCE_DIR}" "BUILD_SHARED_LIBS OFF" "LIBRAW_BUILD_TESTS OFF" "LIBRAW_BUILD_EXAMPLES OFF" "LIBRAW_INSTALL OFF" "ENABLE_OPENMP ${LIBRAW_OPENMP}" "ENABLE_X3FTOOLS ${HDRVIEW_ENABLE_X3F}" EXCLUDE_FROM_ALL YES SYSTEM YES ) if(LibRaw_ADDED AND TARGET libraw::libraw_r) message(STATUS "Will build libraw library") mark_as_system_library(libraw::libraw_r) disable_target_warnings(libraw::libraw_r) # LibRaw sizes its raw store from raw_width x raw_height but its unpackers fill only the active area, # so on a sensor whose frame carries a masked margin the rest is whatever malloc last held. HDRView # keeps that margin (see the CFA part in imageio/raw.cpp), so this switches those allocations to calloc # and zeroes the untouched photosites. Both LibRaw-cmake targets get it, since they compile the same # sources under the same symbol names and both reach the link line. foreach(libraw_target raw raw_r) if(TARGET ${libraw_target}) target_compile_definitions(${libraw_target} PRIVATE LIBRAW_CALLOC_RAWSTORE) endif() endforeach() elseif(LibRaw_FOUND AND TARGET libraw::libraw_r) message(STATUS "Using system-installed libraw library (version ${LIBRAW_VERSION})") else() message(FATAL_ERROR "libraw_r library not found. Please install it.") endif() list(APPEND HDRVIEW_DEPENDENCIES "libraw::libraw_r") endif() CPMAddPackage( NAME CLI11 VERSION 2.4.1 URL https://github.com/CLIUtils/CLI11/archive/v2.4.2.zip DOWNLOAD_ONLY YES SYSTEM YES ) if(CLI11_ADDED) message(STATUS "Will build CLI11 library") add_library(CLI11::CLI11 INTERFACE IMPORTED) target_include_directories(CLI11::CLI11 INTERFACE "${CLI11_SOURCE_DIR}/include") elseif(TARGET CLI11::CLI11) message(STATUS "Will link against system-installed CLI11 library") else() message(FATAL_ERROR "CLI11 library not found. Please install it.") endif() list(APPEND HDRVIEW_DEPENDENCIES "CLI11::CLI11") CPMAddPackage("gh:wkjarosz/smalldds#cece9a892e5dbbd0603b1863cc1b2dbf21cf331d") if(NOT smalldds_ADDED) message(FATAL_ERROR "Could not download smalldds library.") endif() message(STATUS "smalldds library added") # Create implementation library with wrapper configure_file("${CMAKE_SOURCE_DIR}/cmake/smalldds_impl.cpp.in" "${CMAKE_CURRENT_BINARY_DIR}/smalldds_impl.cpp" COPYONLY) add_library(smalldds STATIC "${CMAKE_CURRENT_BINARY_DIR}/smalldds_impl.cpp") target_include_directories(smalldds PUBLIC "${smalldds_SOURCE_DIR}") target_compile_features(smalldds PUBLIC cxx_std_17) disable_target_warnings(smalldds) mark_as_system_library(smalldds) list(APPEND HDRVIEW_DEPENDENCIES "smalldds") CPMAddPackage( NAME bcdec GITHUB_REPOSITORY iOrange/bcdec GIT_TAG 963c5e56b7a335e066cff7d16a3de75f4e8ad366 DOWNLOAD_ONLY YES ) if(NOT bcdec_ADDED) message(FATAL_ERROR "Could not download bcdec library.") endif() message(STATUS "bcdec library added") # Create implementation library with wrapper configure_file("${CMAKE_SOURCE_DIR}/cmake/bcdec_impl.cpp.in" "${CMAKE_CURRENT_BINARY_DIR}/bcdec_impl.cpp" COPYONLY) add_library(bcdec STATIC "${CMAKE_CURRENT_BINARY_DIR}/bcdec_impl.cpp") target_include_directories(bcdec PUBLIC "${bcdec_SOURCE_DIR}") disable_target_warnings(bcdec) mark_as_system_library(bcdec) list(APPEND HDRVIEW_DEPENDENCIES "bcdec") CPMAddPackage( NAME astc_dec GITHUB_REPOSITORY wkjarosz/astc_dec GIT_TAG 0b3a1decbcf1d49b9af65b3afb7499d7486252d2 DOWNLOAD_ONLY YES ) if(NOT astc_dec_ADDED) message(FATAL_ERROR "Could not download astc_dec library.") endif() message(STATUS "astc_dec library added") add_library(astc_dec "${astc_dec_SOURCE_DIR}/astc_decomp.cpp" "${astc_dec_SOURCE_DIR}/astc_decomp.h") target_compile_features(astc_dec PUBLIC cxx_std_11) target_include_directories(astc_dec PUBLIC ${astc_dec_SOURCE_DIR}) disable_target_warnings(astc_dec) mark_as_system_library(astc_dec) list(APPEND HDRVIEW_DEPENDENCIES astc_dec) CPMAddPackage( NAME miniz # GITHUB_REPOSITORY richgel999/miniz VERSION 3.0.2 URL https://github.com/richgel999/miniz/releases/download/3.0.2/miniz-3.0.2.zip DOWNLOAD_ONLY YES ) if(NOT miniz_ADDED AND NOT miniz_FOUND) message(FATAL_ERROR "Could not find or download miniz library.") endif() message(STATUS "miniz library added") add_library(miniz "${miniz_SOURCE_DIR}/miniz.c" "${miniz_SOURCE_DIR}/miniz.h") target_include_directories(miniz PUBLIC ${miniz_SOURCE_DIR}) disable_target_warnings(miniz) mark_as_system_library(miniz) list(APPEND HDRVIEW_DEPENDENCIES miniz) # Helper function to add XXX_ENABLED=0 or XXX_ENABLED=1 definitions based on HDRVIEW_ENABLE_XXX option # Also adds HDRVIEW_ENABLE_XXX itself when the option is ON function(add_enabled_definition option_name) # Extract the feature name (e.g., "J2K" from "HDRVIEW_ENABLE_J2K") string(REPLACE "HDRVIEW_ENABLE_" "" feature_name "${option_name}") if(${option_name}) list(APPEND HDRVIEW_DEFINITIONS "${option_name}=1") else() list(APPEND HDRVIEW_DEFINITIONS "${option_name}=0") endif() set(HDRVIEW_DEFINITIONS ${HDRVIEW_DEFINITIONS} PARENT_SCOPE) endfunction() # Add XXX_ENABLED=0 or XXX_ENABLED=1 definitions for all configurable options add_enabled_definition(HDRVIEW_ENABLE_LIBPNG) add_enabled_definition(HDRVIEW_ENABLE_LIBJPEG) add_enabled_definition(HDRVIEW_ENABLE_LIBUHDR) add_enabled_definition(HDRVIEW_ENABLE_LIBJXL) add_enabled_definition(HDRVIEW_ENABLE_LIBHEIF) add_enabled_definition(HDRVIEW_ENABLE_J2K) add_enabled_definition(HDRVIEW_ENABLE_HTJ2K) add_enabled_definition(HDRVIEW_ENABLE_AVIF) add_enabled_definition(HDRVIEW_ENABLE_DAV1D) add_enabled_definition(HDRVIEW_ENABLE_HEIC) add_enabled_definition(HDRVIEW_ENABLE_AVCI) add_enabled_definition(HDRVIEW_ENABLE_LIBWEBP) add_enabled_definition(HDRVIEW_ENABLE_LIBTIFF) add_enabled_definition(HDRVIEW_ENABLE_LIBRAW) add_enabled_definition(HDRVIEW_ENABLE_X3F) add_enabled_definition(HDRVIEW_ENABLE_IPC) # ============================================================================ # Compiler warnings for HDRView's own code # ============================================================================ # Applied per-target, in the loop after the targets are declared. CMAKE_CXX_FLAGS would also reach the # third-party targets declared in this directory and leave them compiling at two warning levels at once. if(MSVC) # /wd4100 is MSVC's spelling of -Wno-unused-parameter below set(HDRVIEW_WARNING_OPTIONS /W4 /wd4100) elseif(CMAKE_CXX_COMPILER_ID MATCHES "Clang" OR CMAKE_CXX_COMPILER_ID MATCHES "GNU") set(HDRVIEW_WARNING_OPTIONS -Wall -Wextra -Wno-unused-parameter) if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") list(APPEND HDRVIEW_WARNING_OPTIONS -Wno-gnu-anonymous-struct -Wno-c99-extensions -Wno-nested-anon-types -Wno-deprecated-register ) endif() endif() include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include) # Now build the HDRView viewer app string(TIMESTAMP YEAR "%Y") set(output_name "HDRView") set(HELLO_IMGUI_BUNDLE_IDENTIFIER_URL_PART "com.im.HDRView") set(HELLO_IMGUI_BUNDLE_IDENTIFIER_NAME_PART ${app_name}) set(HELLO_IMGUI_ICON_DISPLAY_NAME ${output_name}) set(HELLO_IMGUI_BUNDLE_NAME ${output_name}) set(HELLO_IMGUI_BUNDLE_COPYRIGHT "© Wojciech Jarosz, ${YEAR}") set(HELLO_IMGUI_BUNDLE_EXECUTABLE ${output_name}) set(HELLO_IMGUI_BUNDLE_VERSION ${VERSION}) set(HELLO_IMGUI_BUNDLE_SHORT_VERSION ${VERSION}) set(HELLO_IMGUI_BUNDLE_ICON_FILE icon.icns) if(NOT HDRVIEW_PORTABLE_INSTALL) # Disable HelloImGui default install behavior set(HELLOIMGUI_ADD_APP_WITH_INSTALL OFF CACHE BOOL "" FORCE ) endif() if(HDRVIEW_ENABLE_IPC) list(APPEND EXTRA_SOURCES src/ipc/ipc_packet.cpp src/ipc/ipc_server.cpp src/app-ipc.cpp) # left empty when the IPC sources are not built, so the test target names no file whose subject is gone set(HDRVIEW_IPC_TESTS tests/test_ipc_packet.cpp tests/test_ipc_server.cpp) if(WIN32) # Winsock is not among the libraries a Windows link pulls in by default list(APPEND HDRVIEW_DEPENDENCIES ws2_32) endif() endif() set(HDRVIEW_SOURCES src/imageio/image_loader.cpp src/imageio/dds.cpp src/imageio/alpha.cpp src/imageio/exif.cpp src/imageio/xmp.cpp src/imageio/exr.cpp src/imageio/gainmap.cpp src/imageio/heif.cpp src/imageio/icc.cpp src/imageio/j2k.cpp src/imageio/jpg.cpp src/imageio/jxl.cpp src/imageio/pfm.cpp src/imageio/png.cpp src/imageio/psd.cpp src/imageio/stb.cpp src/imageio/qoi.cpp src/imageio/raw.cpp src/imageio/tiff.cpp src/imageio/uhdr.cpp src/imageio/webp.cpp src/imageio/xmp.cpp src/app-annotations.cpp src/app-colorpass.cpp src/app-draw.cpp src/app-edit.cpp src/app-file-io.cpp src/app-gui.cpp src/app-windows.cpp src/app-zoom.cpp src/app.cpp src/colormap.cpp src/colorspace.cpp src/common.cpp src/display_colorspace.cpp src/edit/commands.cpp src/edit/commands/clipboard.cpp src/edit/commands/channels.cpp src/edit/commands/mipmap.cpp src/edit/commands/color.cpp src/edit/commands/envmap_commands.cpp src/edit/commands/filter.cpp src/edit/commands/tonal.cpp src/edit/commands/transform.cpp src/edit/envmap.cpp src/edit/poisson.cpp src/edit/filters.cpp src/edit/edit_ops.cpp src/edit/subject.cpp src/edit/undo.cpp src/dithermatrix256.cpp src/platform_utils.cpp src/fonts.cpp src/hdrview.cpp src/image.cpp src/imgui_ext.cpp src/image-colorspace-gui.cpp src/image-gui.cpp src/image-histogram.cpp src/opengl_check.cpp src/shader.cpp src/shader_gl.cpp src/renderpass_gl.cpp src/texture_gl.cpp src/texture.cpp src/theme.cpp src/annotations.cpp src/vector_overlay.cpp ${EXTRA_SOURCES} ) # Compiled once here and linked into both HDRView and hdrview_tests: a shared generated source listed in # several targets makes the Visual Studio generator emit its build rule into every consuming .vcxproj, where # parallel MSBuild can regenerate it concurrently. add_library(hdrview_version OBJECT ${CMAKE_CURRENT_BINARY_DIR}/src/version.cpp) add_dependencies(hdrview_version hdrview_check_version) target_link_libraries(hdrview_version PRIVATE hello_imgui) target_include_directories(hdrview_version PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src) set_target_properties(hdrview_version PROPERTIES CXX_STANDARD 17) hello_imgui_add_app(HDRView ${HDRVIEW_SOURCES} ASSETS_LOCATION ${CMAKE_CURRENT_BINARY_DIR}/assets) if(HDRVIEW_SOKOL_SHDC_TARGETS) add_dependencies(HDRView ${HDRVIEW_SOKOL_SHDC_TARGETS}) endif() if(WIN32) # Opt in to Windows 10 1607+ long-path support, removing the ~260-char MAX_PATH limit for CreateFileW & # friends, which std::filesystem and the fstreams route through. The system-wide "Enable Win32 long paths" # policy must be on as well (see README). target_sources(HDRView PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/resources/windows/HDRView.manifest) endif() target_include_directories(HDRView PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src) set_target_properties(HDRView PROPERTIES OUTPUT_NAME ${output_name} CXX_STANDARD 17) target_compile_definitions(HDRView PRIVATE ${HDRVIEW_ICONSET}) if(DEFINED CMAKE_CONFIGURATION_TYPES) list(APPEND HDRVIEW_DEFINITIONS "HDRVIEW_BUILD_TYPE=\"$\"") else() list(APPEND HDRVIEW_DEFINITIONS "HDRVIEW_BUILD_TYPE=\"${CMAKE_BUILD_TYPE}\"") endif() message(STATUS "HDRView build dependencies: ${HDRVIEW_DEPENDENCIES}") message(STATUS "HDRView build definitions: ${HDRVIEW_DEFINITIONS}") target_link_libraries(HDRView PRIVATE ${HDRVIEW_DEPENDENCIES} hdrview_version) target_compile_definitions(HDRView PRIVATE ${HDRVIEW_DEFINITIONS}) if(NOT EMSCRIPTEN) # Multi-config generators (every preset here) build the exe into a per-config subdirectory, below where # HelloImGui's build-time asset copy lands, and HelloImGui's own fallback for that mismatch is MSVC-only. # hdrview.cpp passes this to HelloImGui::AddAssetsSearchPath(). Distinct from ASSETS_LOCATION below, which # is an absolute installed path for non-portable builds. target_compile_definitions(HDRView PRIVATE HDRVIEW_BUILD_TREE_ASSETS_DIR="${CMAKE_CURRENT_BINARY_DIR}/assets") endif() if(APPLE) target_compile_options(HDRView PRIVATE "-fobjc-arc") target_link_libraries(HDRView PRIVATE "-framework ApplicationServices") target_compile_definitions(HDRView PRIVATE IMGUI_ENABLE_OSX_DEFAULT_CLIPBOARD_FUNCTIONS) endif(APPLE) # The shared recipe for every executable built from HDRVIEW_SOURCES besides HDRView itself: the test, GUI-test # and fuzzer binaries. Anything specific to one of them stays at its call site. function(hdrview_setup_target name) target_include_directories(${name} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src) set_target_properties(${name} PROPERTIES CXX_STANDARD 17) target_compile_definitions(${name} PRIVATE ${HDRVIEW_ICONSET} ${HDRVIEW_DEFINITIONS}) # mirrors the HDRVIEW_BUILD_TREE_ASSETS_DIR on HDRView above, so the app's own asset lookup is exercised target_compile_definitions(${name} PRIVATE HDRVIEW_BUILD_TREE_ASSETS_DIR="${CMAKE_CURRENT_BINARY_DIR}/assets") target_link_libraries(${name} PRIVATE ${HDRVIEW_DEPENDENCIES} hello_imgui hdrview_version) if(WIN32) # same long-path opt-in as the HDRView target above, which tests/test_long_paths.cpp needs target_sources(${name} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/resources/windows/HDRView.manifest) endif() if(APPLE) target_compile_options(${name} PRIVATE "-fobjc-arc") target_link_libraries(${name} PRIVATE "-framework ApplicationServices") target_compile_definitions(${name} PRIVATE IMGUI_ENABLE_OSX_DEFAULT_CLIPBOARD_FUNCTIONS) endif() endfunction() # Unit tests, off by default. They reuse the app's own HDRVIEW_SOURCES/HDRVIEW_DEPENDENCIES/HDRVIEW_DEFINITIONS # minus src/hdrview.cpp, which defines the main() doctest supplies itself. option(HDRVIEW_BUILD_TESTS "Build unit tests (requires doctest, fetched via CPM)" OFF) if(HDRVIEW_BUILD_TESTS AND NOT EMSCRIPTEN) CPMAddPackage("gh:doctest/doctest@2.4.11") list(REMOVE_ITEM HDRVIEW_SOURCES src/hdrview.cpp) add_executable(hdrview_tests ${HDRVIEW_SOURCES} tests/test_pixel_stats.cpp tests/test_colorspace.cpp tests/test_exr_io.cpp tests/test_png_io.cpp tests/test_tiff_io.cpp tests/test_gainmap.cpp tests/test_alpha.cpp tests/test_image_groups.cpp tests/test_assets.cpp tests/test_long_paths.cpp tests/test_loader_limits.cpp tests/test_short_names.cpp tests/test_path_helpers.cpp tests/test_index_helpers.cpp tests/test_psd_metadata.cpp tests/test_endian_utils.cpp tests/test_icc_gray_alpha.cpp tests/test_qoi_io.cpp tests/test_icc_cicp.cpp tests/test_dds_io.cpp tests/test_line_helpers.cpp tests/test_orientation_windows.cpp tests/test_undo.cpp tests/test_edit_commands.cpp tests/test_filters.cpp tests/test_envmap.cpp tests/test_poisson.cpp tests/test_export_roundtrip.cpp tests/test_heif_io.cpp tests/test_j2k_io.cpp tests/test_jxl_io.cpp tests/test_xmp.cpp tests/test_exif.cpp tests/test_vector_overlay.cpp tests/test_annotations.cpp ${HDRVIEW_IPC_TESTS} ) hdrview_setup_target(hdrview_tests) target_link_libraries(hdrview_tests PRIVATE doctest::doctest) # fuzzer-found inputs that can't be synthesized; see tests/fuzz/artifacts/README.md target_compile_definitions( hdrview_tests PRIVATE HDRVIEW_TEST_ARTIFACTS_DIR="${CMAKE_CURRENT_SOURCE_DIR}/tests/fuzz/artifacts" ) # Several tests run against the real-world image corpora OpenEXR and libpng vendor in their source trees. # Either path can also be set directly, at a checkout of the corresponding upstream tag: # # -DHDRVIEW_TEST_OPENEXR_DIR=/src/test/bin/test_images # -DHDRVIEW_TEST_PNG_CONTRIB_DIR=/contrib # # -cpm/-universal presets fill them in from the CPM-fetched sources; -local presets get the libraries from # find_package and so have no source tree to read them from. With neither set, the tests that need them are # compiled out (see the guards in tests/test_exr_io.cpp and tests/test_png_io.cpp). set(HDRVIEW_TEST_OPENEXR_DIR "" CACHE PATH "OpenEXR's vendored test images (src/test/bin/test_images in its source tree)" ) set(HDRVIEW_TEST_PNG_CONTRIB_DIR "" CACHE PATH "libpng's contrib directory, holding the pngsuite and testpngs image sets" ) # a supplied path that does not exist is an error, while having no source tree to default to is the # ordinary -local case and only warrants a status line if(HDRVIEW_TEST_OPENEXR_DIR) set(_hdrview_exr_images "${HDRVIEW_TEST_OPENEXR_DIR}") set(_hdrview_exr_explicit TRUE) elseif(DEFINED OpenEXR_SOURCE_DIR) set(_hdrview_exr_images "${OpenEXR_SOURCE_DIR}/src/test/bin/test_images") set(_hdrview_exr_explicit FALSE) endif() if(DEFINED _hdrview_exr_images AND EXISTS "${_hdrview_exr_images}") target_compile_definitions(hdrview_tests PRIVATE HDRVIEW_TEST_OPENEXR_DIR="${_hdrview_exr_images}") elseif(_hdrview_exr_explicit) # fatal, since compiling the tests out instead would leave a build that looks like it ran them message(FATAL_ERROR "HDRVIEW_TEST_OPENEXR_DIR is set to '${_hdrview_exr_images}', which does not exist") else() message(STATUS "OpenEXR test images not available (no fetched source; set HDRVIEW_TEST_OPENEXR_DIR to " "use a checkout); skipping tests/test_exr_io.cpp's vendored-data tests" ) endif() # Same for libpng's two vendored image sets: contrib/pngsuite (the PngSuite conformance set, a bit # depth/color type matrix plus interlaced/non-interlaced pairs) and contrib/testpngs (a matrix of # color-signaling methods: sRGB chunk, gAMA at various gammas, no chunk, and a cICP/Display-P3 example). if(HDRVIEW_TEST_PNG_CONTRIB_DIR) set(_hdrview_png_contrib "${HDRVIEW_TEST_PNG_CONTRIB_DIR}") set(_hdrview_png_explicit TRUE) elseif(DEFINED PNG_SOURCE_DIR) set(_hdrview_png_contrib "${PNG_SOURCE_DIR}/contrib") set(_hdrview_png_explicit FALSE) endif() if(DEFINED _hdrview_png_contrib AND EXISTS "${_hdrview_png_contrib}/pngsuite" AND EXISTS "${_hdrview_png_contrib}/testpngs" ) target_compile_definitions(hdrview_tests PRIVATE HDRVIEW_TEST_PNG_CONTRIB_DIR="${_hdrview_png_contrib}") elseif(_hdrview_png_explicit) message(FATAL_ERROR "HDRVIEW_TEST_PNG_CONTRIB_DIR is set to '${_hdrview_png_contrib}', which does not " "contain both pngsuite and testpngs" ) else() message(STATUS "libpng test images not available (no fetched source; set HDRVIEW_TEST_PNG_CONTRIB_DIR to " "use a checkout); skipping tests/test_png_io.cpp's vendored-data tests" ) endif() # The CMYK conversions cannot be exercised without a CMYK ICC profile, and the smallest real one is half a # megabyte, too large to vendor. libjxl carries lcms in-tree and lcms's testbed carries two, alongside real # RGB profiles and the three malformed ones lcms keeps for rejecting, so a build that fetched libjxl # already has them on disk and the cases can run in CI like the OpenEXR ones. set(HDRVIEW_TEST_LCMS_TESTBED_DIR "" CACHE PATH "lcms2's testbed directory of real and deliberately malformed ICC profiles" ) if(HDRVIEW_TEST_LCMS_TESTBED_DIR) set(_hdrview_lcms_testbed "${HDRVIEW_TEST_LCMS_TESTBED_DIR}") set(_hdrview_lcms_explicit TRUE) elseif(DEFINED JXL_SOURCE_DIR) set(_hdrview_lcms_testbed "${JXL_SOURCE_DIR}/third_party/lcms/testbed") set(_hdrview_lcms_explicit FALSE) endif() if(DEFINED _hdrview_lcms_testbed AND EXISTS "${_hdrview_lcms_testbed}/test1.icc") target_compile_definitions(hdrview_tests PRIVATE HDRVIEW_TEST_LCMS_TESTBED_DIR="${_hdrview_lcms_testbed}") elseif(_hdrview_lcms_explicit) message(FATAL_ERROR "HDRVIEW_TEST_LCMS_TESTBED_DIR is set to '${_hdrview_lcms_testbed}', which holds no " "test1.icc" ) else() message(STATUS "lcms2's test profiles not available (no fetched libjxl source; set " "HDRVIEW_TEST_LCMS_TESTBED_DIR to use a checkout); skipping the profile cases in " "tests/test_j2k_io.cpp and tests/test_icc_cicp.cpp" ) endif() # No JPEG 2000 corpus is vendored by anything the build fetches, so this one is opt-in: point it at a # directory of real .jp2/.j2k/.jph files and tests/test_j2k_io.cpp sweeps it. set(HDRVIEW_TEST_J2K_DIR "" CACHE PATH "A directory of real JPEG 2000 files for tests/test_j2k_io.cpp to sweep" ) if(HDRVIEW_TEST_J2K_DIR) if(NOT EXISTS "${HDRVIEW_TEST_J2K_DIR}") message(FATAL_ERROR "HDRVIEW_TEST_J2K_DIR is set to '${HDRVIEW_TEST_J2K_DIR}', which does not exist") endif() target_compile_definitions(hdrview_tests PRIVATE HDRVIEW_TEST_J2K_DIR="${HDRVIEW_TEST_J2K_DIR}") endif() # libexif's own regression files: nine camera JPEGs, one per maker-note variant its parsers handle, each # beside the dump libexif produces from it. Real notes are what the synthetic blobs in tests/test_exif.cpp # cannot be, since every vendor lays its own out differently and several lie about their offsets. set(HDRVIEW_TEST_LIBEXIF_DIR "" CACHE PATH "libexif's test/testdata directory, holding camera JPEGs and their parsed dumps" ) if(HDRVIEW_TEST_LIBEXIF_DIR) set(_hdrview_libexif_data "${HDRVIEW_TEST_LIBEXIF_DIR}") set(_hdrview_libexif_explicit TRUE) elseif(DEFINED libexif_SOURCE_DIR) set(_hdrview_libexif_data "${libexif_SOURCE_DIR}/test/testdata") set(_hdrview_libexif_explicit FALSE) endif() if(DEFINED _hdrview_libexif_data AND EXISTS "${_hdrview_libexif_data}") target_compile_definitions(hdrview_tests PRIVATE HDRVIEW_TEST_LIBEXIF_DIR="${_hdrview_libexif_data}") elseif(_hdrview_libexif_explicit) message(FATAL_ERROR "HDRVIEW_TEST_LIBEXIF_DIR is set to '${_hdrview_libexif_data}', which does not exist") else() message(STATUS "libexif test files not available (no fetched source; set HDRVIEW_TEST_LIBEXIF_DIR to use " "a checkout); skipping tests/test_exif.cpp's camera-file cases" ) endif() # libtiff's own test images: one per photometric/channels/bits combination, named for what they hold, plus # the encodings and malformed directories its regressions were written against. The loader's other cases # build their TIFFs here, which cannot reach OJPEG, fax, or a directory that points at itself. set(HDRVIEW_TEST_LIBTIFF_DIR "" CACHE PATH "libtiff's test/images directory" ) if(HDRVIEW_TEST_LIBTIFF_DIR) set(_hdrview_libtiff_data "${HDRVIEW_TEST_LIBTIFF_DIR}") set(_hdrview_libtiff_explicit TRUE) elseif(DEFINED tiff_SOURCE_DIR) set(_hdrview_libtiff_data "${tiff_SOURCE_DIR}/test/images") set(_hdrview_libtiff_explicit FALSE) endif() if(DEFINED _hdrview_libtiff_data AND EXISTS "${_hdrview_libtiff_data}") target_compile_definitions(hdrview_tests PRIVATE HDRVIEW_TEST_LIBTIFF_DIR="${_hdrview_libtiff_data}") elseif(_hdrview_libtiff_explicit) message(FATAL_ERROR "HDRVIEW_TEST_LIBTIFF_DIR is set to '${_hdrview_libtiff_data}', which does not exist") else() message(STATUS "libtiff test images not available (no fetched source; set HDRVIEW_TEST_LIBTIFF_DIR to use " "a checkout); skipping tests/test_tiff_io.cpp's vendored-data cases" ) endif() # bcdec's own test images, one per block format it decodes. HDRView decodes DDS through that very library, # so these are the reference files for the code under test. set(HDRVIEW_TEST_BCDEC_DIR "" CACHE PATH "bcdec's test_images directory" ) if(HDRVIEW_TEST_BCDEC_DIR) set(_hdrview_bcdec_data "${HDRVIEW_TEST_BCDEC_DIR}") set(_hdrview_bcdec_explicit TRUE) elseif(DEFINED bcdec_SOURCE_DIR) set(_hdrview_bcdec_data "${bcdec_SOURCE_DIR}/test_images") set(_hdrview_bcdec_explicit FALSE) endif() if(DEFINED _hdrview_bcdec_data AND EXISTS "${_hdrview_bcdec_data}") target_compile_definitions(hdrview_tests PRIVATE HDRVIEW_TEST_BCDEC_DIR="${_hdrview_bcdec_data}") elseif(_hdrview_bcdec_explicit) message(FATAL_ERROR "HDRVIEW_TEST_BCDEC_DIR is set to '${_hdrview_bcdec_data}', which does not exist") else() message(STATUS "bcdec test images not available (no fetched source; set HDRVIEW_TEST_BCDEC_DIR to use a " "checkout); skipping tests/test_dds_io.cpp's vendored-data cases" ) endif() # libjxl's test data: the Compact-ICC-Profiles set, which encodes each color space four ways over ICC v2 # and v4, and a handful of its own codestreams. set(HDRVIEW_TEST_LIBJXL_DIR "" CACHE PATH "libjxl's testdata directory" ) if(HDRVIEW_TEST_LIBJXL_DIR) set(_hdrview_libjxl_data "${HDRVIEW_TEST_LIBJXL_DIR}") set(_hdrview_libjxl_explicit TRUE) elseif(DEFINED JXL_SOURCE_DIR) set(_hdrview_libjxl_data "${JXL_SOURCE_DIR}/testdata") set(_hdrview_libjxl_explicit FALSE) endif() if(DEFINED _hdrview_libjxl_data AND EXISTS "${_hdrview_libjxl_data}/external/Compact-ICC-Profiles/profiles") target_compile_definitions(hdrview_tests PRIVATE HDRVIEW_TEST_LIBJXL_DIR="${_hdrview_libjxl_data}") elseif(_hdrview_libjxl_explicit) message(FATAL_ERROR "HDRVIEW_TEST_LIBJXL_DIR is set to '${_hdrview_libjxl_data}', which holds no " "external/Compact-ICC-Profiles/profiles" ) else() message(STATUS "libjxl test data not available (no fetched source; set HDRVIEW_TEST_LIBJXL_DIR to use a " "checkout); skipping the vendored-data cases in tests/test_icc_cicp.cpp and " "tests/test_jxl_io.cpp" ) endif() include(${doctest_SOURCE_DIR}/scripts/cmake/doctest.cmake) enable_testing() doctest_discover_tests(hdrview_tests) endif() # libFuzzer targets, off by default and Clang-only. load_image() sniffs magic bytes to pick a loader, so the # one target reaches every format in default_loaders(). Pair with -DUSE_SANITIZER. option(HDRVIEW_BUILD_FUZZERS "Build libFuzzer targets for the image loaders (Clang only)" OFF) if(HDRVIEW_BUILD_FUZZERS AND NOT EMSCRIPTEN) if(NOT CMAKE_CXX_COMPILER_ID MATCHES "Clang") message(FATAL_ERROR "HDRVIEW_BUILD_FUZZERS requires Clang (libFuzzer)") endif() list(REMOVE_ITEM HDRVIEW_SOURCES src/hdrview.cpp) # one entry per fuzz target, "|"; everything below is applied to each set(HDRVIEW_FUZZERS "hdrview_fuzz_load_image|tests/fuzz/fuzz_load_image.cpp") if(HDRVIEW_ENABLE_IPC) list(APPEND HDRVIEW_FUZZERS "hdrview_fuzz_ipc_packet|tests/fuzz/fuzz_ipc_packet.cpp") endif() # Apple's clang instruments for libFuzzer but ships no fuzzer runtime, so on macOS one is linked # explicitly, from Homebrew's LLVM unless HDRVIEW_FUZZER_RUNTIME says otherwise. Elsewhere # -fsanitize=fuzzer supplies its own. if(APPLE AND NOT HDRVIEW_FUZZER_RUNTIME) file(GLOB HDRVIEW_FUZZER_RUNTIME_CANDIDATES "/opt/homebrew/opt/llvm/lib/clang/*/lib/darwin/libclang_rt.fuzzer_osx.a" "/usr/local/opt/llvm/lib/clang/*/lib/darwin/libclang_rt.fuzzer_osx.a" ) if(HDRVIEW_FUZZER_RUNTIME_CANDIDATES) list(GET HDRVIEW_FUZZER_RUNTIME_CANDIDATES -1 HDRVIEW_FUZZER_RUNTIME) message(STATUS "Fuzzer runtime: ${HDRVIEW_FUZZER_RUNTIME}") else() message(FATAL_ERROR "No libFuzzer runtime found. Install one with `brew install llvm`, or point " "HDRVIEW_FUZZER_RUNTIME at a libclang_rt.fuzzer_osx.a." ) endif() endif() foreach(fuzzer IN LISTS HDRVIEW_FUZZERS) string(REPLACE "|" ";" fuzzer_parts "${fuzzer}") list(GET fuzzer_parts 0 fuzz_target) list(GET fuzzer_parts 1 fuzz_source) add_executable(${fuzz_target} ${HDRVIEW_SOURCES} ${fuzz_source}) hdrview_setup_target(${fuzz_target}) target_compile_options(${fuzz_target} PRIVATE -fsanitize=fuzzer-no-link) if(HDRVIEW_FUZZER_RUNTIME) target_link_libraries(${fuzz_target} PRIVATE "${HDRVIEW_FUZZER_RUNTIME}") else() target_link_options(${fuzz_target} PRIVATE -fsanitize=fuzzer) endif() endforeach() endif() # GUI regression tests, driven by Dear ImGui Test Engine (https://github.com/ocornut/imgui_test_engine), which # hello_imgui's HELLOIMGUI_WITH_TEST_ENGINE option fetches and builds. A GUI test needs HelloImGui::Run() to # own the frame loop for the process's whole lifetime, so this cannot share hdrview_tests' binary and main(). # HDRVIEW_ENABLE_GUI_TEST_ENGINE gates the opt-in hook on HDRViewApp (src/app.h, src/app-windows.cpp) that # turns on useImGuiTestEngine and registers tests, leaving HDRView and hdrview_tests untouched. if(HDRVIEW_BUILD_GUI_TESTS AND NOT EMSCRIPTEN) list(REMOVE_ITEM HDRVIEW_SOURCES src/hdrview.cpp) add_executable( hdrview_gui_tests ${HDRVIEW_SOURCES} tests/gui/test_gui_main.cpp tests/gui/test_gui_smoke.cpp tests/gui/test_gui_dialogs.cpp tests/gui/test_gui_image_io.cpp tests/gui/test_gui_view.cpp tests/gui/test_gui_viewport.cpp tests/gui/test_gui_display.cpp tests/gui/test_gui_tools.cpp tests/gui/test_gui_edit.cpp tests/gui/test_gui_stats.cpp tests/gui/test_gui_filtering.cpp tests/gui/test_gui_navigation.cpp tests/gui/test_gui_multipart.cpp tests/gui/test_gui_session.cpp tests/gui/test_gui_session_bundle.cpp tests/gui/test_gui_lifetime.cpp tests/gui/test_gui_loader.cpp tests/gui/test_gui_info.cpp tests/gui/test_gui_screenshots.cpp ) hdrview_setup_target(hdrview_gui_tests) target_compile_definitions(hdrview_gui_tests PRIVATE HDRVIEW_ENABLE_GUI_TEST_ENGINE) # the app's own icons, as small always-present image fixtures for the tests/gui/ suite target_compile_definitions( hdrview_gui_tests PRIVATE HDRVIEW_GUI_TEST_IMAGE="${CMAKE_CURRENT_SOURCE_DIR}/assets/app_settings/icon.png" HDRVIEW_GUI_TEST_IMAGE_2="${CMAKE_CURRENT_SOURCE_DIR}/assets/app_settings/icon-256.png" ) # where tests/gui/test_gui_screenshots.cpp looks for sample images when the environment names none target_compile_definitions( hdrview_gui_tests PRIVATE HDRVIEW_SCREENSHOT_FIXTURE_DIR="${CMAKE_CURRENT_SOURCE_DIR}/resources" ) # the OpenEXR guard above, for hdrview_gui_tests: tests/gui/test_gui_multipart.cpp needs a real multi-part # EXR fixture, and registers no tests at all when this define is absent if(DEFINED OpenEXR_SOURCE_DIR AND EXISTS "${OpenEXR_SOURCE_DIR}/src/test/bin/test_images") target_compile_definitions( hdrview_gui_tests PRIVATE HDRVIEW_TEST_OPENEXR_DIR="${OpenEXR_SOURCE_DIR}/src/test/bin/test_images" ) endif() # its own ctest entry, so GUI-test failures and logs are never mixed into the doctest suite's output enable_testing() add_test(NAME hdrview_gui_tests COMMAND hdrview_gui_tests) endif() # Turn warnings on for the targets built from HDRVIEW_SOURCES, now that all of them exist. The optional ones # only exist when HDRVIEW_BUILD_TESTS/_GUI_TESTS/_FUZZERS asked for them. foreach(hdrview_target HDRView hdrview_version hdrview_tests hdrview_gui_tests hdrview_fuzz_load_image) if(TARGET ${hdrview_target}) target_compile_options(${hdrview_target} PRIVATE ${HDRVIEW_WARNING_OPTIONS}) endif() endforeach() if(EMSCRIPTEN) target_link_options( HDRView PRIVATE -fexceptions -sEXPORTED_RUNTIME_METHODS=[ccall,HEAPU8] -sEXPORTED_FUNCTIONS=[_main,_malloc,_free] -sNO_DISABLE_EXCEPTION_CATCHING -sMAX_WEBGL_VERSION=2 -sMIN_WEBGL_VERSION=2 # Emscripten's 64 kB default stack is too small for libaom's AV1 decoder, which overflows it on HDR # AVIFs and aborts the whole module. Memory growth is enabled, so a native-thread-sized stack is free. -sSTACK_SIZE=4MB ) target_compile_options(HDRView PRIVATE -fexceptions -sNO_DISABLE_EXCEPTION_CATCHING) if(HELLOIMGUI_EMSCRIPTEN_PTHREAD) target_link_options(HDRView PRIVATE -pthread) target_link_options(HDRView PRIVATE -sPTHREAD_POOL_SIZE=3) endif() hello_imgui_set_emscripten_target_initial_memory_megabytes(HDRView 120) endif() # Emscripten is excluded even though it sets UNIX: the target file there is the HTML shell, which GNU strip # rejects outright (Apple's only warns), and emcc has already stripped the wasm it links. if(UNIX AND NOT EMSCRIPTEN AND NOT IS_MULTI AND NOT ${U_CMAKE_BUILD_TYPE} MATCHES DEBUG) add_custom_command( TARGET HDRView POST_BUILD COMMAND strip $ ) endif() # Force colored output for the ninja generator if(CMAKE_GENERATOR STREQUAL "Ninja") if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fcolor-diagnostics") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fcolor-diagnostics") elseif(CMAKE_CXX_COMPILER_ID MATCHES "GNU") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fdiagnostics-color=always") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fdiagnostics-color=always") endif() endif() # Install and packaging support if(NOT HDRVIEW_PORTABLE_INSTALL) # cmake-format: off # If not portable, we have to # - install the app manually # - install the assets manually # - pass the assets location to the app, and set this assets location in main() via the compile definition ASSETS_LOCATION: # #ifdef ASSETS_LOCATION \n HelloImGui::SetAssetsFolder(ASSETS_LOCATION); \n #endif # cmake-format: on include(GNUInstallDirs) install(TARGETS HDRView DESTINATION ${CMAKE_INSTALL_BINDIR}) if(DEFINED ASSETS_LOCATION) message(STATUS "Using user-defined ASSETS_LOCATION: ${ASSETS_LOCATION}") # Set assets_install_dir to the parent directory of ASSETS_LOCATION get_filename_component(assets_install_dir "${ASSETS_LOCATION}" DIRECTORY) if(NOT IS_ABSOLUTE "${assets_install_dir}") set(assets_install_dir "${CMAKE_INSTALL_PREFIX}/${assets_install_dir}") endif() install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/assets DESTINATION ${assets_install_dir}) target_compile_definitions(HDRView PRIVATE ASSETS_LOCATION="${ASSETS_LOCATION}") else() message(STATUS "Installing assets to default location") set(assets_install_dir ${CMAKE_INSTALL_DATADIR}/HDRView) if(NOT IS_ABSOLUTE ${assets_install_dir}) set(assets_install_dir ${CMAKE_INSTALL_PREFIX}/${assets_install_dir}) endif() install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/assets DESTINATION ${assets_install_dir}) set(ASSETS_LOCATION "${assets_install_dir}/assets") target_compile_definitions(HDRView PRIVATE ASSETS_LOCATION="${assets_install_dir}/assets") endif() endif() set(CPACK_PACKAGE_VENDOR "Wojciech Jarosz") set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "${PROJECT_DESCRIPTION}") set(CPACK_PACKAGE_HOMEPAGE_URL "https://github.com/wkjarosz/hdrview") set(CPACK_PACKAGE_VERSION_MAJOR ${VERSION_MAJOR}) set(CPACK_PACKAGE_VERSION_MINOR ${VERSION_MINOR}) set(CPACK_PACKAGE_VERSION_PATCH ${VERSION_PATCH}) set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/LICENSE.txt") set(CPACK_RESOURCE_FILE_README "${CMAKE_CURRENT_SOURCE_DIR}/README.md") set(CPACK_PACKAGE_INSTALL_DIRECTORY "${PROJECT_NAME}") if(APPLE) install(TARGETS HDRView BUNDLE DESTINATION . COMPONENT Runtime) set(CPACK_GENERATOR "DragNDrop") # Strip leading 'v' from GIT_TAG to get full version with prerelease suffix string(REGEX REPLACE "^v" "" VERSION_NO_V "${GIT_TAG}") # Clear extended attributes (resource forks/quarantine) from the bundle before bundling/signing install(CODE " execute_process( COMMAND xattr -rc \"\${CMAKE_INSTALL_PREFIX}/HDRView.app\" RESULT_VARIABLE _XATTR_RESULT OUTPUT_QUIET ERROR_QUIET ) if(NOT _XATTR_RESULT EQUAL 0) message(WARNING \"xattr -rc returned \${_XATTR_RESULT} for HDRView.app\") endif() " COMPONENT Runtime) # Bundle dependencies using dylibbundler when packaging find_program(DYLIBBUNDLER_EXECUTABLE dylibbundler) if(DYLIBBUNDLER_EXECUTABLE) install(CODE " execute_process( COMMAND ${DYLIBBUNDLER_EXECUTABLE} -od -b -x \"\${CMAKE_INSTALL_PREFIX}/HDRView.app/Contents/MacOS/HDRView\" -d \"\${CMAKE_INSTALL_PREFIX}/HDRView.app/Contents/libs/\" RESULT_VARIABLE DYLIB_RESULT ) if(NOT DYLIB_RESULT EQUAL 0) message(WARNING \"dylibbundler failed with code \${DYLIB_RESULT}\") endif() " COMPONENT Runtime) else() message(STATUS "dylibbundler not found - DMG will not include bundled dependencies") endif() # CPack DragNDrop settings set(CPACK_DMG_VOLUME_NAME "HDRView-Installer") set(CPACK_DMG_FORMAT "UDZO") # Set custom DMG filename: e.g. HDRView-2.7.0-Universal.dmg set(CPACK_PACKAGE_FILE_NAME "HDRView-${VERSION_NO_V}-${HDRVIEW_TARGET_ARCH}") # Configure the AppleScript with the versioned app name configure_file( "${CMAKE_SOURCE_DIR}/cmake/DMGSetup.scpt.in" "${CMAKE_BINARY_DIR}/DMGSetup.scpt" @ONLY ) set(CPACK_DMG_DS_STORE_SETUP_SCRIPT "${CMAKE_BINARY_DIR}/DMGSetup.scpt") # Only install Runtime component to avoid dependency files set(CPACK_COMPONENTS_ALL Runtime) set(CPACK_INSTALL_CMAKE_PROJECTS "${CMAKE_BINARY_DIR};${PROJECT_NAME};Runtime;/") elseif(UNIX AND NOT EMSCRIPTEN) # Linux installation and AppImage generation install(TARGETS HDRView) # Prepare linux icons output directory (icon generation moved to packaging script) set(LINUX_ICONS_OUTPUT_DIR "${CMAKE_CURRENT_BINARY_DIR}/linux_icons") # Install desktop file and icons install(FILES "${CMAKE_SOURCE_DIR}/assets/app_settings/linux/HDRView.desktop" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/applications") # register the .hsess session file's mime type so file managers offer HDRView to open it install(FILES "${CMAKE_SOURCE_DIR}/assets/app_settings/linux/hdrview-session-mime.xml" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/mime/packages") # Strip leading 'v' from GIT_TAG to get full version string(REGEX REPLACE "^v" "" VERSION_NO_V "${GIT_TAG}") # CPack configuration for AppImage list(APPEND CPACK_GENERATOR External) set(CPACK_EXTERNAL_PACKAGE_SCRIPT "${CMAKE_CURRENT_SOURCE_DIR}/cmake/create-appimage.cmake") set(CPACK_EXTERNAL_ENABLE_STAGING YES) # CPack reads the global COMPONENTS property, so one dependency's install(... COMPONENT ...) is enough to # put it in component mode, and staging then lands each component in a subdirectory of its own: # `Unspecified/usr/bin/HDRView` rather than `usr/bin/HDRView`, which linuxdeploy cannot find. Nothing here # names a component, so ask for the single-pass install the AppDir layout needs. set(CPACK_MONOLITHIC_INSTALL ON) # When packaging, instruct CPack to set DESTDIR so install uses a staging dir set(CPACK_SET_DESTDIR ON) # Place generated packages in the build directory instead of the source root set(CPACK_PACKAGE_DIRECTORY "${CMAKE_BINARY_DIR}") set(CPACK_PACKAGE_FILE_NAME "HDRView-${VERSION_NO_V}-${HDRVIEW_TARGET_ARCH}") set(CPACK_PACKAGING_INSTALL_PREFIX /usr) # AppDir expects this endif() include(CPack)