Magnum::Trade::OpenExrImageConverter class new in Git master

OpenEXR image converter plugin.

Creates OpenEXR (*.exr) files using the OpenEXR library. You can use OpenExrImporter to import images in this format.

Usage

This plugin depends on the Trade library and is built if MAGNUM_WITH_OPENEXRIMAGECONVERTER is enabled when building Magnum Plugins. To use as a dynamic plugin, load "OpenExrImageConverter" via Corrade::PluginManager::Manager.

Additionally, if you're using Magnum as a CMake subproject, bundle the magnum-plugins and openexr repositories (pin OpenEXR at v3.0.1 at least) and do the same as shown in the OpenExrImporter usage docs, with the end being the following instead. If you want to use system-installed OpenEXR, omit the first part and point CMAKE_PREFIX_PATH to its installation dir if necessary.

# (Same setup and add_subdirectory(openexr) as with OpenExrImporter)

set(MAGNUM_WITH_OPENEXRIMAGECONVERTER ON CACHE BOOL "" FORCE)
add_subdirectory(magnum-plugins EXCLUDE_FROM_ALL)

# So the dynamically loaded plugin gets built implicitly
add_dependencies(your-app MagnumPlugins::OpenExrImageConverter)

To use as a static plugin or as a dependency of another plugin with CMake, put FindMagnumPlugins.cmake and FindOpenEXR.cmake into your modules/ directory, request the OpenExrImageConverter component of the MagnumPlugins package and link to the MagnumPlugins::OpenExrImageConverter target:

find_package(MagnumPlugins REQUIRED OpenExrImageConverter)

# ...
target_link_libraries(your-app PRIVATE MagnumPlugins::OpenExrImageConverter)

See Downloading and building plugins, Plugin usage with CMake, Loading and using plugins and File format support for more information.

Behavior and limitations

Accepts 2D and cubemap images with optional mip levels in PixelFormat::R16F / RG16F / RGB16F / RGBA16F, PixelFormat::R32F / RG32F / RGB32F / RGBA32F or PixelFormat::R32UI / RG32UI / RGB32UI / RGBA32UI and PixelFormat::Depth32F.

As OpenEXR doesn't have a registered MIME type, mimeType() returns "image/x-exr".

The plugin recognizes ImageConverterFlag::Quiet, which will cause all conversion warnings to be suppressed.

Channel mapping

Images with PixelFormat::R16F / RG16F / RGB16F / RGBA16F, PixelFormat::R32F / RG32F / RGB32F / RGBA32F or PixelFormat::R32UI / RG32UI / RGB32UI / RGBA32UI are implicitly written to channels named R, G, B and A; images with PixelFormat::Depth32F are implicitly written to a Z channel.

If the default behavior is not sufficient, custom channel mapping can be supplied in the configuration.

Multilayer and multipart images, deep images

Channels can be prefixed with a custom layer name by specifying the layer configuration option. Combining multiple layers into a single image isn't supported right now, writing multipart files or deep images is not supported either.

Cube and lat/lon environment maps

A 2D image can be annotated as being a lat/lon environment map by setting envmap=latlon in the configuration. This requires it to have the width twice of the height.

A cube map image can be saved from an ImageView3D where each slice is one face in order +X, -X, +Y, -Y, +Z and -Z if you set envmap=cube. In this case, the image is expected to have six rectangular faces.

Array and 3D images

Apart from cube maps, saving of arbitrary 3D and 2D array (or "deep") images isn't implemented right now.

The OpenEXR file format doesn't have a way to distinguish between 2D and 1D array images. If an image has ImageFlag2D::Array set, a warning is printed and the file is saved as a regular 2D image.

Multilevel images

Both 2D and cube map images can be saved with multiple levels by using the list variants of convertToFile() / convertToData(). Largest level is expected to be first, with each following level having width and height divided by two, rounded down. Cube map images additionally have the restrictions specified above. OpenEXR has no builtin concept of an incomplete mip chain, unspecified levels at the end result in a file with missing tiles. This may cause problems with 3rd party tools, however the OpenExrImporter detects such case and reports the file as having less levels.

Multilevel images result in a tiled OpenEXR file, with a tile size taken from the tileSize configuration option. Single-level images are implicitly written as scanline files, you can override that with the forceTiledOutput option.

Plugin-specific configuration

It's possible to tune various options mainly for channel mapping through configuration(). See below for all options and their default values:

[configuration]
# Number of threads OpenEXR should use for compression and writing. A value
# of 1 performs compression and writing serially in the calling thread, 2
# adds one additional worker thread for compression, etc. 0 sets it to the
# value returned by std::thread::hardware_concurrency().
#
# Note that while the amount of threads can be controlled per-file, OpenEXR
# has a global thread pool and its size will remain at the largest set value
# until the plugin is unloaded. OpenExrImporter shares the same thread pool.
threads=1

# Save channels with given layer
layer=

# Map R/RG/RGB/RGBA image channels to given channel names. If the image is in
# a PixelFormat::Depth32F, depth is used instead. If the channel is not
# present in the input or if its name is empty, it's not written to the file.
r=R
g=G
b=B
a=A
depth=Z

# Set to latlong for 2D images to annotate the image as a lat/long
# environment map. If empty, no environment map metadata are saved. 3D images
# should instead have ImageFlag3D::CubeMap set to be saved as a cube map.
envmap=

# Display and data window. OpenEXR allows the image to annotate a crop border
# or specify that the data are just a small portion of a bigger image. The
# display window is a four-component vector with (inclusive) Xmin, Ymin,
# Xmax, Ymax coordinates. If left empty, it's implicitly set to 0 0 width-1
# height-1. The data window size is always set to width-1 height-1 and you
# can only set the offset. For details see the pixel space, see here:
# https://openexr.readthedocs.io/en/latest/TechnicalIntroduction.html#display-window
displayWindow=
dataOffset=0 0

# Compression. Allowed values are rle, zip, zips, piz, pxr24, b44, b44a, dwaa
# and dwab; leave it empty to write the output uncompressed. More info here:
# https://openexr.readthedocs.io/en/latest/TechnicalIntroduction.html#data-compression
compression=zip
# ZIP compression level. Available since OpenEXR 3.1.3, older versions have
# it hardcoded to Z_DEFAULT_COMPRESSION, which is 6.
zipCompressionLevel=4
# DWA compression level
dwaCompressionLevel=45.0

# OpenEXR files with mipmaps are always tiled, single level is implicitly
# written as scanlines, unless this option is enabled. Tile size 32x32 is
# library default, larger sizes may lead to less overhead and better
# compression ratios at the cost of more unused space at smallest image
# levels.
forceTiledOutput=false
tileSize=32 32

See Editing plugin-specific configuration for more information and an example showing how to edit the configuration values.

Enabling multithreading

On Linux it may happen that setting the threads option to something else than 1 will cause std::system_error to be thrown (or, worst case, crashing with a null function pointer call on some systems). There's no way to solve this from within the dynamically loaded module itself, the application has to be linked to pthread instead. With CMake it can be done like this:

find_package(Threads REQUIRED)
target_link_libraries(your-application PRIVATE Threads::Threads)

Base classes

class AbstractImageConverter
Base for image converter plugins.

Constructors, destructors, conversion operators

OpenExrImageConverter(PluginManager::AbstractManager& manager, const Containers::StringView& plugin) explicit
Plugin manager constructor.