本文操作按照《c&c++开源库编译指南》中内容规范编写,编译环境配置、工具下载、目录规划,及更多其他开源库编译方法请参考该文章。
c&c++开源库编译指南:https://blog.csdn.net/binary0006/article/details/144086155
本文章中的源代码已提交到gitee仓库,地址:https://gitee.com/binary0010/depends/tree/master/c/openexr-2.3.0
1.openexr概述
OpenEXR 是由工业光魔(Industrial Light & Magic,ILM)开发的一种用于存储高动态范围(HDR)图像数据的文件格式,同时也是一套实现该格式读写操作的开源库,在影视、动画、游戏等视觉特效领域有着广泛应用。
从技术角度来看,OpenEXR 采用了无损压缩算法,如 PIZ、PXR24 等,能够在有效减小文件体积的同时,最大程度保留图像的原始信息,确保图像的高质量和高保真度。它支持多通道数据存储,除了常见的红、绿、蓝(RGB)通道外,还可以存储深度、法线、透明度等额外信息,为后期制作提供了丰富的数据支持。OpenEXR 库使用 C++ 编写,提供了一套完整的 API,方便开发者进行图像的读写操作。在编译方面,它具有良好的跨平台性,可在 Linux、Windows、macOS 等多种操作系统上编译,不过编译时可能依赖一些其他的库,如 zlib、Imath 等。
在应用场景上,OpenEXR 主要用于影视制作中的合成、渲染和后期处理环节。在渲染过程中,渲染器可以将生成的高动态范围图像保存为 OpenEXR 格式,方便后续的颜色校正、合成等操作。在后期合成阶段,OpenEXR 的多通道数据可以帮助合成师更精确地控制图像的各个部分,实现复杂的特效。此外,在游戏开发中,OpenEXR 也逐渐被用于存储和处理高质量的纹理和光照信息。
在开源社区,OpenEXR 遵循 BSD 许可协议,代码托管在 GitHub 上。社区非常活跃,不断有开发者对其进行维护和更新,添加新的功能、优化性能以及修复潜在的漏洞。许多知名的影视制作公司、动画工作室和游戏开发团队都在使用 OpenEXR,它已经成为了高动态范围图像存储和处理的行业标准之一。
2.openexr编译
2.1.源代码下载
openexr源代码可以在github上下载,源代码下载地址:https://github.com/AcademySoftwareFoundation/openexr/releases,这里我们使用2.3.0版本,使用较低的版本能确保vs2008编译不会出错,在这个界面往后翻直到找到2.3.0版本后,按照下面的截图选择下载openexr-2.3.0.tar.gz。
2.2.windows编译
先解压源代码到指定目录,openexr-2.3.0版本已经了提供CMakeLists.txt编译脚步,我们按照规范会对该脚本进行修改。
2.2.1.openexr源代码工程分析
当我们打开 openexr 源代码根目录下的 CMakeLists.txt 文件时,能够清晰地看到其中包含了多个关键子目录,分别为 IlmBase、OpenEXR、PyIlmBase 和 OpenEXR_Viewers,并且每个子目录下又涵盖了多个工程,下面将详细阐述这些子目录及其包含工程的作用。
2.2.1.1.IlmBase 子目录
IlmBase 是 OpenEXR 的基础库,它为 OpenEXR 提供了核心的数据结构和算法支持,是整个 OpenEXR 项目的基石。该子目录下的工程主要实现了一些基础的功能,这些功能对于处理高动态范围图像数据至关重要。
Half 工程:Half 工程主要负责实现半精度浮点数(16 位浮点数)的处理。在处理高动态范围图像时,半精度浮点数可以在减少存储空间的同时,保留足够的精度来表示图像数据。Half 工程提供了半精度浮点数与单精度浮点数(32 位浮点数)之间的转换函数,以及一些基本的数学运算函数,方便开发者在不同精度之间进行切换。
Iex 工程:Iex 工程是一个异常处理库,它为 OpenEXR 提供了统一的异常处理机制。在处理图像数据时,可能会遇到各种错误情况,如文件读取错误、数据格式错误等。Iex 工程定义了一系列的异常类,开发者可以使用这些异常类来捕获和处理不同类型的错误,提高代码的健壮性和可维护性。
Imath 工程:Imath 工程实现了一组数学库,包括向量、矩阵、四元数等基本数学对象的定义和操作。在图像处理中,这些数学对象被广泛用于表示图像的几何信息,如位置、方向、旋转等。Imath 工程提供了丰富的数学运算函数,方便开发者进行图像的变换和处理。
2.2.1.2.OpenEXR 子目录
OpenEXR 子目录是整个项目的核心部分,它实现了 OpenEXR 文件格式的读写功能,以及相关的压缩和解压缩算法。该子目录下的工程主要围绕 OpenEXR 文件的处理展开。
OpenEXR 核心库工程:该工程实现了 OpenEXR 文件的读写功能,包括文件头的解析、数据块的读写、通道信息的处理等。它提供了一组 API 接口,开发者可以使用这些接口来创建、读取和修改 OpenEXR 文件。
压缩算法工程:OpenEXR 支持多种压缩算法,如 PIZ、PXR24、ZIP 等。压缩算法工程实现了这些压缩算法的具体实现,通过对图像数据进行压缩,可以有效减少文件的存储空间,提高数据传输效率。
测试工程:为了确保 OpenEXR 库的正确性和稳定性,测试工程包含了一系列的单元测试和集成测试。这些测试用例覆盖了 OpenEXR 库的各个功能模块,通过运行这些测试用例,可以及时发现和修复潜在的问题。
2.2.1.3.PyIlmBase 子目录
PyIlmBase 子目录提供了 Python 绑定,使得开发者可以在 Python 环境中使用 OpenEXR 的功能。该子目录下的工程主要实现了 Python 与 C++ 代码之间的交互。
Python 绑定工程:该工程使用 SWIG(Simplified Wrapper and Interface Generator)工具生成 Python 与 C++ 代码之间的绑定。通过这些绑定,开发者可以在 Python 脚本中直接调用 OpenEXR 的 C++ 库函数,方便地进行图像处理和分析。
示例脚本工程:为了帮助开发者快速上手,示例脚本工程提供了一些使用 PyIlmBase 的示例脚本。这些脚本展示了如何在 Python 环境中创建、读取和修改 OpenEXR 文件,以及如何进行简单的图像处理操作。
2.2.1.4.OpenEXR_Viewers 子目录
OpenEXR_Viewers 子目录包含了一些用于查看和显示 OpenEXR 文件的工具。这些工具可以帮助开发者直观地查看 OpenEXR 文件的内容,进行图像的预览和调试。
在实际的 C++ 开发中,通常仅需要将 IlmBase 和 OpenEXR 作为 SDK(软件开发工具包)来使用。IlmBase 提供的基础数据结构和算法,是构建 OpenEXR 相关功能的根基。开发人员在处理图像数据时,会频繁使用到 IlmBase 中定义的半精度浮点数处理、异常处理以及数学运算等功能。而 OpenEXR 则提供了直接操作 OpenEXR 文件的核心功能,开发人员可以利用其提供的 API 接口,轻松实现对 OpenEXR 文件的读写操作,以及进行数据的压缩和解压缩处理。
将 IlmBase 和 OpenEXR 作为 SDK 集成到项目中,能够让开发人员专注于业务逻辑的实现,而无需过多关注底层的细节。通过调用这些 SDK 提供的接口,开发人员可以高效地开发出与 OpenEXR 文件处理相关的应用程序,如影视后期制作软件、图像编辑工具等。而 PyIlmBase 和 OpenEXR_Viewers 子目录中的内容,对于大多数 C++ 开发场景来说并非必需,它们更多地是为 Python 开发者或者需要进行文件查看和调试的人员提供便利。
通过对这些子目录及其包含工程的分析,我们可以全面了解 openexr 源代码的结构和功能,为后续的编译和开发工作打下坚实的基础。
2.2.2.工程预览
OpenEXR的子工程很多,这里先预览一下修改完CMakeLists.txt之后生产的vs工程,一屏幕的高度快放不下了。
2.2.3.修改CMake脚本
上面已经看到OpenEXR的子工程很多,修改关联的CMakeLists.txt脚本也会很多,这里给出其中一个修改的示例。
IlmBase\Half\CMakeLists.txt
# yue.nicholas@gmail.com
ADD_EXECUTABLE(eLut eLut.cpp)
ADD_CUSTOM_COMMAND(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/eLut.h
COMMAND $<TARGET_FILE:eLut> ARGS > ${CMAKE_CURRENT_BINARY_DIR}/eLut.h
DEPENDS eLut)
SET_SOURCE_FILES_PROPERTIES(
${CMAKE_CURRENT_BINARY_DIR}/eLut.h
PROPERTIES HEADER_FILE_ONLY TRUE)
ADD_EXECUTABLE(toFloat toFloat.cpp)
ADD_CUSTOM_COMMAND(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/toFloat.h
COMMAND $<TARGET_FILE:toFloat> ARGS > ${CMAKE_CURRENT_BINARY_DIR}/toFloat.h
DEPENDS toFloat)
SET_SOURCE_FILES_PROPERTIES(
${CMAKE_CURRENT_BINARY_DIR}/toFloat.h
PROPERTIES HEADER_FILE_ONLY TRUE)
SET_SOURCE_FILES_PROPERTIES(
half.cpp
PROPERTIES
OBJECT_DEPENDS
"${CMAKE_CURRENT_BINARY_DIR}/eLut.h;${CMAKE_CURRENT_BINARY_DIR}/toFloat.h"
)
SET(ILMBASE_LIB_TARGETS "")
# IF(OPENEXR_BUILD_SHARED)
# LIST(APPEND ILMBASE_LIB_TARGETS Half)
# ADD_LIBRARY(Half SHARED
# half.cpp
# )
# TARGET_COMPILE_DEFINITIONS(Half PRIVATE HALF_EXPORTS)
# IF(WIN32)
# TARGET_COMPILE_DEFINITIONS(Half PUBLIC OPENEXR_DLL)
# ENDIF()
# SET_TARGET_PROPERTIES(Half
# PROPERTIES
# VERSION ${OPENEXR_VERSION}
# SOVERSION ${OPENEXR_SOVERSION}
# OUTPUT_NAME "Half${ILMBASE_LIBSUFFIX}"
# )
# ADD_DEPENDENCIES(Half toFloat eLut)
# ENDIF()
# IF(BUILD_ILMBASE_STATIC)
# LIST(APPEND ILMBASE_LIB_TARGETS Half_static)
# ADD_LIBRARY(Half_static STATIC
# half.cpp
# )
# SET_TARGET_PROPERTIES(Half_static
# PROPERTIES
# VERSION ${ILMBASE_VERSION_MAJOR}.${ILMBASE_VERSION_MINOR}.${ILMBASE_VERSION_PATCH}
# OUTPUT_NAME "Half${ILMBASE_LIBSUFFIX}_s"
# )
# ADD_DEPENDENCIES(Half_static toFloat eLut)
# ENDIF()
# IF(OPENEXR_BUILD_SHARED OR BUILD_ILMBASE_STATIC)
# INSTALL(TARGETS
# ${ILMBASE_LIB_TARGETS}
# ARCHIVE DESTINATION lib
# LIBRARY DESTINATION lib
# RUNTIME DESTINATION ${RUNTIME_DIR}
# )
# ENDIF()
# INSTALL(
# FILES
# half.h
# halfFunction.h
# halfExport.h
# halfLimits.h
# DESTINATION
# include/OpenEXR
# )
# if(OPENEXR_BUILD_SHARED)
# add_library(IlmBase::Half ALIAS Half)
# endif()
# if(BUILD_ILMBASE_STATIC)
# add_library(IlmBase::Half_static ALIAS Half_static)
# endif()
# 静态库工程
add_library(Halflib STATIC
half.cpp
)
add_dependencies(Halflib toFloat eLut)
add_library(IlmBase::Halflib ALIAS Halflib)
# 动态库工程
add_library(Halfdll SHARED
half.cpp
)
target_compile_definitions(Halfdll PRIVATE HALF_EXPORTS)
if(WIN32)
target_compile_definitions(Halfdll PUBLIC OPENEXR_DLL)
endif()
add_dependencies(Halfdll toFloat eLut)
add_library(IlmBase::Halfdll ALIAS Halfdll)
if(MSVC)
# 静态库mt工程
add_library(Halflibmt STATIC
half.cpp
)
set_property(TARGET Halflibmt PROPERTY
MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
add_dependencies(Halflibmt toFloat eLut)
add_library(IlmBase::Halflibmt ALIAS Halflibmt)
# 动态库mt工程
add_library(Halfdllmt SHARED
half.cpp
)
set_property(TARGET Halfdllmt PROPERTY
MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
target_compile_definitions(Halfdllmt PRIVATE HALF_EXPORTS PUBLIC OPENEXR_DLL)
add_dependencies(Halfdllmt toFloat eLut)
add_library(IlmBase::Halfdllmt ALIAS Halfdllmt)
# 设置静态库pdb文件
set_lib_pdb_file(Half lib)
set_lib_pdb_file(Half libmt)
endif(MSVC)
#安装sdk相关文件
#安装头文件
if(NOT SKIP_INSTALL_HEADERS AND NOT SKIP_INSTALL_ALL)
install(
FILES
half.h
halfFunction.h
halfExport.h
halfLimits.h
DESTINATION ${INSTALL_INC_DIR}
)
endif()
if(NOT SKIP_INSTALL_LIBRARIES AND NOT SKIP_INSTALL_ALL)
#安装库文件
set(Half_targets Halflib Halfdll)
if(MSVC)
list(APPEND Half_targets Halflibmt Halfdllmt)
endif()
install(TARGETS ${Half_targets}
RUNTIME DESTINATION "${INSTALL_BIN_DIR}"
ARCHIVE DESTINATION "${INSTALL_LIB_DIR}"
LIBRARY DESTINATION "${INSTALL_LIB_DIR}")
endif()
#安装pdb文件
if(NOT SKIP_INSTALL_PDB AND NOT SKIP_INSTALL_ALL)
install(FILES $<TARGET_PDB_FILE:Halfdll> $<TARGET_PDB_FILE:Halfdllmt>
DESTINATION "${INSTALL_BIN_DIR}"
OPTIONAL)
# 静态库pdb调用函数安装
install_lib_pdb_file(Half lib)
install_lib_pdb_file(Half libmt)
endif()
2.2.4.vs2008编译
前面我们已经把vs2008编译需要处理的源代码以及CMakeLists.txt脚本按照规范修改完毕,下面我们将使用cmake来构建vs2008的工程和编译。
2.2.4.1.使用cmake构建工程并编译
以前的文章中,已经有很多详细的cmake步骤了,操作流程都是一样的,其实也很简单。这里就不再累述了,注意设置build目录为源代码根目录下的cmake-vs2008绝对路径为D:/x-app/depends/c/libiconv-1.11.1/cmake-vs2008,已经编译器的选择即可。
这里勾选项注意一下,取消不需要的项,否则可能会构建报错。
2.2.4.2.编译工程
使用vs2008打开“OpenEXR.sln”解决方案文件,分别编译Debug和RelWithDebInfo配置。
根据下面截图可以看出来,编译后的库文件还是比较多的。
2.2.4.3.安装库文件、头文件
编译Install工程的Debug和RelWithDebInfo配置,.h文件和编译生成.dll、.lib、pdb文件会拷贝CMakeList.txt中设置的路径下。
2.2.5.vs2015编译
vs2015编译与vs2008相同的,只需要在CMake生成工程时选择vs2015即可,其他版本的vs也是一样的,选择需要使用的vs版本即可,此处只演示一下vs2015的编译。
因为前面生成过vs2008的工程,需要将build目录设置成cmake-vs2015,再“Configure”选择vs2015即可,其他操作就是一样的了。