OpenEXR/Imath 2.x to 3.x Porting Guide¶
This porting guide outlines the several areas where switching from OpenEXR2.x to OpenEXR 3.x + Imath 3.x will require source code or build changes ofdownstream software.
In each case, we will often explain both how to change if you are expecting3.x only hereafter, or usually a more complex accommodation if you want tokeep compatibility with both 2.x and 3.x.
OpenEXR and Imath Are Different Packages¶
If your use of OpenEXR was only for the sake of using the math classes andutilities, maybe you were unhappy that you needed to download and build thefull OpenEXR dependency. You are in luck – now Imath is a separate, verylightweight open source package. You can use Imath functionality withoutneeding any of OpenEXR, which as of 3.x only includes the parts you need toread and write OpenEXR image files.
The parts of “IlmBase” that wereImath andhalf are now repackagedas theImath library. TheIlmThread andIex libraries have beenfolded into the OpenEXR package, since they were were not necessary tothe rest of Imath.
When building OpenEXR 3.x, note that if Imath 3.x library is not foundalready installed at build time, it will be automatically downloaded andbuilt as part of the OpenEXR build.
Background¶
Why is this happening? Here is the relevant history.
The OpenEXR project has historically consisted of four separate subprojects:
OpenEXR- the Imf image formatIlmBase- supporting utilities (Imath, Half, Iex, IlmThread)PyIlmBase- python bindings for the IlmBase librariesOpenEXR_Viewers- code for an example EXR image viewer
Prior to the 2.4 release in 2019, OpenEXR relied primarily on the Gnuautotools build system and was released as four separate tarballs(ilmbase, pyilmbase, openexr, openexr_viewers) that were constructedvia the Gnu tools. This gave direct access to the “IlmBase” librariesindependent of the OpenEXR format library. The project also includedCMake files but CMake support was incomplete.
With the adoption of OpenEXR by the Academy Software Foundation in2019, the technical steering committee made several key changes:
Drop support for autotools in favor of CMake. A significant portionof the OpenEXR user base uses Windows, which the Gnu autotools doesnot support. Supporting two build systems is a maintenance burdenthat the TSC opted to avoid. We now assume that all modern users ofOpenEXR can reasonably be expected to rely on CMake.
Rely on GitHub’s automatic release packaging mechanism. Thispackages the entire contents of package in a singletarball. Separate tarballs are no longer generated by the Gnuautotools setup.
Deprecate the OpenEXR_Viewers code. It was impossibly out of dateand of little modern value.
Thus, with the 2.4 release, the “IlmBase” libraries are no longerdistributed in a form that is readily separable from the rest ofOpenEXR. The build and installation process for the overall OpenEXRproject is complicated by the fact it consists of four separateprojects, which added significant complexity to the CMake setup.
Because Imath is generally useful to the community, the TSC decided tosimplify the configuration by separating Imath into its own independentproject, maintained and released independently of OpenEXR, andintroducing it as a new external dependency of OpenEXR.
To further simplify matters, the new Imath library includes the halfdata type directly, rather than maintaining it in a separatelibrary. Also, the community at large has a strong desire for simplevector/matrix utilities that are unencumbered by Iex, the IlmBaselibrary that provides higher-level exception classes, and evenfurther, a clear delineation between functionality that (1) relies onexception handlings and (2) is free from exceptions. As a result,support for Iex has been removed from Imath, and the Iex library isnow packaged as a component of OpenEXR.
The Imath python bindings are a part of Imath as a configurationoption, although support is off by default to simplify the build processfor most users.
New Library Names and Repository Structures¶
The new repositories place all source code under thesrc top-levelsubdirectory.
Imath:¶
src├── Imath├── ImathTest└── python ├── config ├── PyImath ├── PyImathNumpy ├── PyImathTest ├── PyImathNumpyTest └── PyImathSpeedTest
OpenEXR:¶
TheIlmImf library has been renamedOpenEXR. No header files havechanged names, only their locations in the repo have changes.
src├── bin│ ├── exr2aces│ ├── exrbuild│ ├── exrcheck│ ├── exrenvmap│ ├── exrheader│ ├── exrmakepreview│ ├── exrmaketiled│ ├── exrmultipart│ ├── exrmultiview│ └── exrstdattr├── lib│ ├── Iex│ ├── IexMath│ ├── IlmThread│ ├── OpenEXR│ └── OpenEXRUtil├── examples└── test ├── IexTest ├── OpenEXRTest ├── OpenEXRUtilTest └── OpenEXRFuzzTest
Finding and Using OpenEXR and Imath CMake Configs¶
OpenEXR/Imath 3.x Only¶
If you areonly concerned with OpenEXR/Imath 3.x going forward, this isthe recommended way to find the libraries in a downstream project that usesthe CMake build system:
find_package(ImathCONFIG)find_package(OpenEXRCONFIG)
Note that the second line may be omitted if you only need the Imathportions.
And then your project can reference the imported targets like this:
target_link_libraries(my_targetPRIVATEOpenEXR::OpenEXRImath::ImathImath::Half)
You only need the parts you use, so for example, if you only need Half andImath, you can omit the OpenEXR target. Also note that in our example above,we have used thePRIVATE label, but you should specify them asPUBLIC ifyou are exposing those classes in your own package’s public interface.
Accommodating OpenEXR/Imath 3.x or OpenEXR 2.x¶
On the other hand, to accommodate both 2.x and 3.x, it’s admittedlyinconvenient because the packages and the import targets have changed theirnames. We have found the following idioms to work:
Finding either/both packages:
# First, try to find just the right config filesfind_package(ImathCONFIG)if(NOTTARGETImath::Imath)# Couldn't find Imath::Imath, maybe it's older and has IlmBase?find_package(IlmBaseCONFIG)endif()find_package(OpenEXRCONFIG)
To link against them, we use CMake generator expressions so that we canreferenceboth sets of targets, but it will only use the onescorresponding to the package version that was found.
target_link_libraries (my_target PRIVATE # For OpenEXR/Imath 3.x: $<$<TARGET_EXISTS:OpenEXR::OpenEXR>:OpenEXR::OpenEXR> $<$<TARGET_EXISTS:Imath::Imath>:Imath::Imath> $<$<TARGET_EXISTS:Imath::Half>:Imath::Half> # For OpenEXR 2.4/2.5: $<$<TARGET_EXISTS:OpenEXR::IlmImf>:OpenEXR::IlmImf> $<$<TARGET_EXISTS:IlmBase::Imath>:IlmBase::Imath> $<$<TARGET_EXISTS:IlmBase::Half>:IlmBase::Half> $<$<TARGET_EXISTS:IlmBase::IlmThread>:IlmBase::IlmThread> $<$<TARGET_EXISTS:IlmBase::Iex>:IlmBase::Iex> )
Again, you can eliminate the references to any of the individual librariesthat you don’t actually need for your application.
Simultaneous Static/Shared Build¶
The OpenEXR 2.x CMake configuration had options to simultaneouslybuild both shared and statically linked libraries. This has beendeprecated. A CMake configuration setting specifies whether to buildstatic or shared, but if you want both, you will need to run cmake andbuild twice.
Simultaneous Python 2/3 Build¶
The PyIlmBase 2.x CMake configuration had options to simultaneouslybuild both python2 and python3 bindings. This has been deprecated.A CMake configuration setting specifies whether to build forpython 2 or python 3, but if you want both, you will need to runcmake and build twice.
Imath Include Files Are in a Different Subdirectory¶
Imath 3.0 will copy its headers to someinclude/Imath subdirectoryinstead of the oldinclude/OpenEXR.
OpenEXR/Imath 3.x Only¶
If you know that you are only using Imath 3.x, then just change anyinclude directions, like this:
#include <OpenEXR/ImathVec.h>#include <OpenEXR/half.h>
to the new locations:
#include <Imath/ImathVec.h>#include <Imath/half.h>
Accommodating OpenEXR/Imath 3.x or OpenEXR 2.x¶
If you want your software to be able to build against either OpenEXR 2.x or3.x (depending on which dependency is available at build time), we recommendusing a more complicated idiom:
//TheversioncanreliablybefoundinthisheaderfilefromOpenEXR,//forboth2.xand3.x:#include <OpenEXR/OpenEXRConfig.h>#define COMBINED_OPENEXR_VERSION ((10000*OPENEXR_VERSION_MAJOR) + \(100*OPENEXR_VERSION_MINOR)+ \OPENEXR_VERSION_PATCH)//There's just no easy way to have an ``#include`` that works in both//cases,soweusetheversiontoswitchwhichsetofincludefileswe//use.#if COMBINED_OPENEXR_VERSION >= 20599 /* 2.5.99: pre-3.0 */# include <Imath/ImathVec.h># include <Imath/half.h>#else//OpenEXR2.x,usetheoldlocations# include <OpenEXR/ImathVec.h># include <OpenEXR/half.h>#endif
Include Files Include Fewer Other Headers¶
Extraneous#include statements have been removed from some headerfiles, which can lead to compile failures in application code thatpreviously included certain headers indirectly.
For example, the Imath header files no longer includefloat.h, soapplication code that references symbols such asFLT_MAX may needto add an explicit#include<float.h> or equivalent.
If your application code reports compile errors due to undefined orincompletely-defined Imath or OpenEXR data types, locate the Imath orOpenEXR header file that defines the type and include it explicitly.
Symbols Are Hidden by Default¶
To reduce library size and make linkage behavior similar acrossplatforms, Imath and OpenEXR now build with directives that makesymbol visibility hidden by default, with specific externally-visiblesymbols explicitly marked for export. See theSymbol Visibility in OpenEXRand the appropriate*Export.h header file for more details.
Imath Now Uses Standard C++ Exceptions andnoexcept¶
In OpenEXR 2.x, the Imath functions that threw exceptions used to throwvarious Iex varieties.
In Imath 3.x, these functions just throwstd::exception varieties thatcorrespond to the failure (e.g.,std::invalid_argument,std::domain_error, etc.). For that reason, all of the Iex exceptions arenow only part of the OpenEXR library (where they are still used in the samemanner they were for OpenEXR 2.x).
Imath 3.x has very few functions that throw exceptions. Each is clearlymarked as such, and each has a version that does not throw exceptions (sothat it may be used from code where exceptions are avoided). The functionsthat do not throw exceptions are now markednoexcept.
Some Headers and Classes Have Been Removed from Imath 3.x¶
The
Math<T>class (andImathMath.hheader file) aredeprecated. All of theMath<T>functionality is subsumed by C++11std::math functions. For example, calls toImath::Math<T>::abs(x)should be replaced withstd::abs(x).The
Limits<T>class (and theImathLimits.handImathHalfLimits.hheaders) have been removed entirely. All uses ofLimits<>should be replaced with the appropriatestd::numeric_limits<>method call. The Imath-specific versionspredated C++11, and were not only redundant in a C++11 world, butalso potentially confusing because some of their functions behavedquite differently than thestd::numeric_limitsmethod with thesame name. We are following the precept that if C++11 does somethingin a standard way, we should not define our own equivalent function(and especially not define it in a way that doesn’t match thestandard behavior).Vec<T>::normalize()andlength()methods, for integerTtypes,have been removed. Also the standaloneproject()andorthogonal()functions are no longer defined for vectors made ofinteger elements. These all had behavior that was hard to understandand probably useless. They still work as expected for vectors offloating-point types.The
Int64andSInt64types are deprecated in favor of thenow-standardint64_tanduint64_t.
File/Class-specific Changes¶
half inhalf.h¶
The half type is now in the
Imathnamespace, but a compile-timeoption puts it in the global namespace, except when compiling forCUDA, in which case the ‘half’ type refers to the CUDA type:
#ifndef __CUDACC__ using half = IMATH_INTERNAL_NAMESPACE::half; #else #include <cuda_fp16.h> #endifIf you desire to use Imath::half inside a CUDA kernel, you can referto it via the namespace, or define ``CUDA_NO_HALF`` to avoid the CUDAtype altogether.
HALF_MINhas changed value. It is now the smallestnormalizedpositive value, returned by
std::numeric_limits<half>::min().
New constructor from a bit pattern:
enumFromBitsTag{FromBits};constexprhalf(FromBitsTag,unsignedshortbits)noexcept;
Imath::Box<T> inImathBox.h¶
baseTypeMin()is replaced withbaseTypeLowest()
Color3<T>,Color4<T> inImathColor.h¶
baseTypeMin()is replaced withbaseTypeLowest()
Imath::Frustum<T> inImathFrustum.h¶
Akin to theVec classes, there are now separate API calls forthrowing and non-throwing functions:
These functions previously threw exceptions but now do not throw andare markednoexcept:
Frustum<T>::projectionMatrix()noexceptFrustum<T>::aspect()noexceptFrustum<T>::set()noexceptFrustum<T>::projectPointToScreen()noexceptFrustum<T>::ZToDepth()noexceptFrustum<T>::DepthToZ()noexceptFrustum<T>::screenRadius()noexceptFrustum<T>::localToScreen()noexcept
These functions throwstd::domain_error exceptions when theassociated frustum is degenerate:
Frustum<T>::projectionMatrixExc()Frustum<T>::aspectExc()Frustum<T>::setExc()Frustum<T>::projectPointToScreenExc()Frustum<T>::ZToDepthExc()Frustum<T>::DepthToZExc()Frustum<T>::screenRadiusExc()Frustum<T>::localToScreenExc()
Imath::Interval<T> inImathInterval.h¶
New methods/functions:
Interval<T>::operator!=Interval<T>::makeInfinite()Interval<T>isInfinite()operator<<(std::ostream&s,constInterval<T>&)
ImathMatrixAlgo.h¶
checkForZeroScaleInRow()andextractAndRemoveScalingAndShear()throw
std::domain_errorexceptions instead ofIex::ZeroScale
Matrix22<T>,Matrix33<T>,Matrix44<T> inImathMatrix.h¶
baseTypeMin()is replaced withbaseTypeLowest()invert(boolsingExc=false)is replace by:invert()noexceptinvert(bool)which optionally throws anstd::invalid_argumentexception.
inverse(boolsingExc=false)is replace by:inverse()noexceptinverse(bool)which optionally throws anstd::invalid_argumentexception.
gjInvert(boolsingExc=false)is replace by:gjInvert()noexceptgjInvert(bool)which optionally throws anstd::invalid_argumentexception.
gJinverse(boolsingExc=false)is replace by:gjInverse()noexceptgjInverse(bool)which optionally throws anstd::invalid_argumentexception.
New functions:
operator<<(std::ostream&s,constMatrix22<T>&)operator<<(std::ostream&s,constMatrix33<T>&)operator<<(std::ostream&s,constMatrix44<T>&)
Other changes:
Initialization loops unrolled for efficiency
inline added where appropriate
ImathRoots.h¶
When compiling for CUDA, the
complextype comes fromthrustrather thanstd
Shear6 inImathShear.h¶
baseTypeMin()is replaced withbaseTypeLowest()
ImathVecAlgo.h¶
The following functions are no longer defined for integer-basedvectors, because such behavior is not clearly defined:
project(constVec&s,constVec&t)orgthogonal(constVec&s,constVec&t)reflect(constVec&s,constVec&t)
Vec2<T>,Vec3<T>,Vec4<T> inImathVec.h¶
baseTypeMin()is replaced withbaseTypeLowest()The following methods are removed (via
=delete) for integer-basedvectors because the behavior is not clearly defined and thus proneto confusion:length()- although the length is indeed defined, its proper valueis floating point and can thus not be represented by the ‘T’return type.normalize()normalizeExc()normalizeNonNull()normalized()normalizedExc()normalizedNonNull()
Interoperability Constructors: The Vec and Matrix classes now haveconstructors that take as an argument any data object of similarsize and layout.
Imath Python Changes¶
In general, the changes in Imath at the C++ level are reflected in thepython bindings. In particular:
The following methods are removed for integer-basedvector and matrix objects and arrays:
length()normalize()normalizeExc()normalizeNonNull()normalized()normalizedExc()normalizedNonNull()
baseTypeMin()is replaced withbaseTypeLowest()for:Vec2,Vec3,Vec4Color3,Color4Matrix22,Matrix33,Matrix44BoxShear6
