Reading and Writing OpenEXR Image Files with the C-language API

The OpenEXRCore library has a few notable features to consider prior to beginning use:

stateless

The library is largely state-free, enabling multiple plugins or just different areas within the same process to customize the errors being reported (i.e. swallow them, report them differently, etc.), provide custom memory allocation routines, or replace the file I/O routines with their own. To do this, every file is called a “context”, and each context can be initialized with a customized set of routines or values. There are global defaults for all of this behavior, but they are just that: defaults.

result codes

Every routine returns a result code (success or an error code). Besides some low level routines where a good error message is not possible, the rest of the library attempts to provide a meaningful message as to what is going wrong via a formatted message. This rule of every function returning a result code means that in some scenarios, the code may be slightly more verbose, but it should be a safer library in the long run.

The C api is designed around the fundamental units:

  • Header and attributes

  • Multiple parts (optional)

  • Chunks of image or deep data

  • Transformations of those chunks into the requested form

All requests to read or write image or data from a file must happen in “chunk” requests. There are API differences depending on whether the OpenEXR part being read contains scanline data or tiled data, and to handle deep vs shallow image files. However, largely all the same functions are used to read and write this atomic data.

Simple Example

To get started, here is a simple code snippet that starts to read a file:

1 exr_context_initializer_t ctxtinit = EXR_DEFAULT_CONTEXT_INITIALIZER;
2 exr_context_t myfile;
3 exr_result_t rv = exr_start_read(&myfile, “/tmp/foo.exr”, &ctxtinit);
4 if (rv != EXR_ERR_SUCCESS)
5 {
6   /* do something about the error */
7 }

Reading and Writing

This will open a file and parse the header. One can immediately notice this context initializer structure. This provides a path to specify custom handlers for I/O, memory, error reporting, etc. Writing is a near mirror, just using exr_start_write(), with the additional option about whether to handle creation of a temporary file and rename it, or to write directly. In both cases, along with the path to quick edit attributes in place, the context is closed / destroyed with a call to exr_finish.

For reading image data, once the file is opened using this exr_start_read() call, the remaining calls are all thread-safe and as lock free as possible.

Assuming the parsing of the header and opening of the file are successful, the remaining API then takes both this context that is initialized as well as a part index. In this way, the C library provides transparent, integrated multi-part OpenEXR support, and does not further differentiate between the variants. One can query how many parts are in the file via exr_get_count(). Furthermore, there are a wealth of routines provided to access the attributes (metadata) contained within the file. These will not be described here beyond where necessary.

There is a set of functions to query (or set while writing) the information for a particular part index. They are too numerous to list here, but there are functions to query the required attributes of the file, and then more generic type-specific functions to access attributes by type. Further, the attributes can be accessed either in a sorted order, or the order within which they appear in the file. One example might be to retrieve the data window:

1 exr_attr_box2i_t datawindow;
2 exr_result_t rv = exr_get_data_window(ctxt, 0, &datawindow);

Data Types

Notice that this example does not use the traditional Imath types here. The Imath types C++ objects with a rich set of functions, but are not viable in a C api. The types for the C layer are only provided to transport the data, and not to operate on the data. But with the new type conversion constructors present in version 3.1 of Imath, types such as the vector types (i.e. exr_attr_v3f_t) should transparently convert to their respective Imath type.

Chunks

Once the calling program has created the context, inspected the header data to decide the next course of action, and started to operate on a file, one must operate in chunk units. For a scanline-based file, this means requesting scanlines in groups of scanlines contained in one chunk. The routine exr_get_scanlines_per_chunk() returns this count. This is currently 1, 16, or 32. For tiled files, the base unit of I/O is a tile itself: the chunk is a tile. One can use the routines exr_get_tile_descriptor(), exr_get_tile_levels(), and exr_get_tile_sizes() as utilities to introspect tile information.

In order to read data, use exr_read_scanline_block_info() or exr_read_tile_block_info() to initialize a structure with the data to read one of these chunks of data. Then there are the corresponding exr_read_chunk(), exr_read_deep_chunk() which read the data. Analogously, there are write versions of these functions.

Encode and Decode

The above is very simple, but only provides the raw, compressed and packed data. These are preferred if you are writing a utility to copy, combine, or split out parts of EXR files and just want the raw data blocks. However, to actually use the data in an application, the encoding and decoding pipeline methods should be used. These provide and combine the read step above, such that they are free to optimize the data path when the data is uncompressed.

The decoding pipeline consists of a structure that contains relevant channel and data type information, in addition to allocation routines, function pointers to specialize the stages of the pipeline, and pointers to memory buffers to use. This enables the calling application to re-use decode pipeline buffers (where it can determine thread safety), and avoid constant memory allocation / deallocation while performing such tasks as reading scanlines of an image into one large buffer. The decode pipeline consists of 3 (4 when reading deep) simple steps:

  1. Read the data

  2. De-compress the data (if it is compressed)

  3. Optionally update allocation based on sample data read (deep only)

  4. Unpack the data (re-orient from the on-disk representation)

These decoding pipelines (or the mirror for encoding) provide the caller with the ability to use the in-built routines for portions of these steps (exr_decoding_choose_default_routines()), but then customize the stages that make the most sense. So, for simplicity, one could imagine implementing a GPU decoder to use the provided routines for reading and decompressing, but then instead of interleaving the data on the CPU, instead provide a custom routine to do that step on the GPU by overriding the function pointer on the decoding structure.

Once you have decoded or encoded all the chunks required, it is expected you will call exr_decoding_destroy() which will clean up all the buffers associated with that instance of the decoding pipeline. If you will be reading an entire image at once, it is recommended to initialize the decoding pipeline once Regardless of using the raw chunk API, or the richer decoding pipeline, both paths start with a call to query information about the chunk to read, using either exr_read_scanline_block_info() or exr_ead_tile_block_info(). This fills in and initializes a structure with information for that chunk, including how many bits would result from unpacking that chunk, and it’s raw position on disk.

Reference

Basic Types

typedef int32_t exr_result_t

Return type for all functions.

Basic Enumerated Types

enum exr_compression_t

Enum declaring allowed values for uint8_t value stored in built-in compression type.

Values:

enumerator EXR_COMPRESSION_NONE
enumerator EXR_COMPRESSION_RLE
enumerator EXR_COMPRESSION_ZIPS
enumerator EXR_COMPRESSION_ZIP
enumerator EXR_COMPRESSION_PIZ
enumerator EXR_COMPRESSION_PXR24
enumerator EXR_COMPRESSION_B44
enumerator EXR_COMPRESSION_B44A
enumerator EXR_COMPRESSION_DWAA
enumerator EXR_COMPRESSION_DWAB
enumerator EXR_COMPRESSION_LAST_TYPE

Invalid value, provided for range checking.

enum exr_envmap_t

Enum declaring allowed values for uint8_t value stored in built-in env map type.

Values:

enumerator EXR_ENVMAP_LATLONG
enumerator EXR_ENVMAP_CUBE
enumerator EXR_ENVMAP_LAST_TYPE

Invalid value, provided for range checking.

enum exr_lineorder_t

Enum declaring allowed values for uint8_t value stored in lineOrder type.

Values:

enumerator EXR_LINEORDER_INCREASING_Y
enumerator EXR_LINEORDER_DECREASING_Y
enumerator EXR_LINEORDER_RANDOM_Y
enumerator EXR_LINEORDER_LAST_TYPE

Invalid value, provided for range checking.

enum exr_storage_t

Enum declaring allowed values for part type.

Values:

enumerator EXR_STORAGE_SCANLINE

Corresponds to type of scanlineimage.

enumerator EXR_STORAGE_TILED

Corresponds to type of tiledimage.

enumerator EXR_STORAGE_DEEP_SCANLINE

Corresponds to type of deepscanline.

enumerator EXR_STORAGE_DEEP_TILED

Corresponds to type of deeptile.

enumerator EXR_STORAGE_LAST_TYPE

Invalid value, provided for range checking.

enum exr_tile_level_mode_t

Enum representing what type of tile information is contained.

Values:

enumerator EXR_TILE_ONE_LEVEL

Single level of image data.

enumerator EXR_TILE_MIPMAP_LEVELS

Mipmapped image data.

enumerator EXR_TILE_RIPMAP_LEVELS

Ripmapped image data.

enumerator EXR_TILE_LAST_TYPE

Invalid value, provided for range checking.

enum exr_tile_round_mode_t

Enum representing how to scale positions between levels.

Values:

enumerator EXR_TILE_ROUND_DOWN
enumerator EXR_TILE_ROUND_UP
enumerator EXR_TILE_ROUND_LAST_TYPE
enum exr_pixel_type_t

Enum capturing the underlying data type on a channel.

Values:

enumerator EXR_PIXEL_UINT
enumerator EXR_PIXEL_HALF
enumerator EXR_PIXEL_FLOAT
enumerator EXR_PIXEL_LAST_TYPE
enum exr_attr_list_access_mode

Values:

enumerator EXR_ATTR_LIST_FILE_ORDER

Order they appear in the file.

enumerator EXR_ATTR_LIST_SORTED_ORDER

Alphabetically sorted.

enum exr_perceptual_treatment_t

Hint for lossy compression methods about how to treat values (logarithmic or linear), meaning a human sees values like R, G, B, luminance difference between 0.1 and 0.2 as about the same as 1.0 to 2.0 (logarithmic), where chroma coordinates are closer to linear (0.1 and 0.2 is about the same difference as 1.0 and 1.1).

Values:

enumerator EXR_PERCEPTUALLY_LOGARITHMIC
enumerator EXR_PERCEPTUALLY_LINEAR
enum exr_default_write_mode

Enum describing how default files are handled during write.

Values:

enumerator EXR_WRITE_FILE_DIRECTLY

Overwrite filename provided directly, deleted upon error.

enumerator EXR_INTERMEDIATE_TEMP_FILE

Create a temporary file, renaming it upon successful write, leaving original upon error.

Global State

typedef void *(*exr_memory_allocation_func_t)(size_t bytes)

Function pointer used to hold a malloc-like routine.

Providing these to a context will override what memory is used to allocate the context itself, as well as any allocations which happen during processing of a file or stream. This can be used by systems which provide rich malloc tracking routines to override the internal allocations performed by the library.

This function is expected to allocate and return a new memory handle, or NULL if allocation failed (which the library will then handle and return an out-of-memory error).

If one is provided, both should be provided.

See

exr_memory_free_func_t

typedef void (*exr_memory_free_func_t)(void *ptr)

Function pointer used to hold a free-like routine.

Providing these to a context will override what memory is used to allocate the context itself, as well as any allocations which happen during processing of a file or stream. This can be used by systems which provide rich malloc tracking routines to override the internal allocations performed by the library.

This function is expected to return memory to the system, ala free from the C library.

If providing one, probably need to provide both routines.

See

exr_memory_allocation_func_t

void exr_get_library_version(int *maj, int *min, int *patch, const char **extra)

Retrieve the current library version.

The extra string is for custom installs, and is a static string, do not free the returned pointer.

void exr_set_default_maximum_image_size(int w, int h)

Limit the size of image allowed to be parsed or created by the library.

This is used as a safety check against corrupt files, but can also serve to avoid potential issues on machines which have very constrained RAM.

These values are among the only globals in the core layer of OpenEXR. The intended use is for applications to define a global default, which will be combined with the values provided to the individual context creation routine. The values are used to check against parsed header values. This adds some level of safety from memory overruns where a corrupt file given to the system may cause a large allocation to happen, enabling buffer overruns or other potential security issue.

These global values are combined with the values in exr_context_initializer_t using the following rules:

  1. negative values are ignored.

  2. if either value has a positive (non-zero) value, and the other has 0, the positive value is preferred.

  3. If both are positive (non-zero), the minimum value is used.

  4. If both values are 0, this disables the constrained size checks.

This function does not fail.

void exr_get_default_maximum_image_size(int *w, int *h)

Retrieve the global default maximum image size.

This function does not fail.

void exr_set_default_maximum_tile_size(int w, int h)

Limit the size of an image tile allowed to be parsed or created by the library.

Similar to image size, this places constraints on the maximum tile size as a safety check against bad file data

This is used as a safety check against corrupt files, but can also serve to avoid potential issues on machines which have very constrained RAM

These values are among the only globals in the core layer of OpenEXR. The intended use is for applications to define a global default, which will be combined with the values provided to the individual context creation routine. The values are used to check against parsed header values. This adds some level of safety from memory overruns where a corrupt file given to the system may cause a large allocation to happen, enabling buffer overruns or other potential security issue.

These global values are combined with the values in exr_context_initializer_t using the following rules:

  1. negative values are ignored.

  2. if either value has a positive (non-zero) value, and the other has 0, the positive value is preferred.

  3. If both are positive (non-zero), the minimum value is used.

  4. If both values are 0, this disables the constrained size checks.

This function does not fail.

void exr_get_default_maximum_tile_size(int *w, int *h)

Retrieve the global maximum tile size.

This function does not fail.

void exr_set_default_memory_routines(exr_memory_allocation_func_t alloc_func, exr_memory_free_func_t free_func)

Allow the user to override default allocator used internal allocations necessary for files, attributes, and other temporary memory.

These routines may be overridden when creating a specific context, however this provides global defaults such that the default can be applied.

If either pointer is 0, the appropriate malloc/free routine will be substituted.

This function does not fail.

Chunk Reading

exr_result_t exr_read_scanline_chunk_info(exr_const_context_t ctxt, int part_index, int y, exr_chunk_info_t *cinfo)
exr_result_t exr_read_tile_chunk_info(exr_const_context_t ctxt, int part_index, int tilex, int tiley, int levelx, int levely, exr_chunk_info_t *cinfo)
exr_result_t exr_read_chunk(exr_const_context_t ctxt, int part_index, const exr_chunk_info_t *cinfo, void *packed_data)

Read the packed data block for a chunk.

This assumes that the buffer pointed to by packed_data is large enough to hold the chunk block info packed_size bytes.

exr_result_t exr_read_deep_chunk(exr_const_context_t ctxt, int part_index, const exr_chunk_info_t *cinfo, void *packed_data, void *sample_data)

Read chunk for deep data.

This allows one to read the packed data, the sample count data, or both. exr_read_chunk also works to read deep data packed data, but this is a routine to get the sample count table and the packed data in one go, or if you want to pre-read the sample count data, you can get just that buffer.

Chunks

struct exr_chunk_info_t

Struct describing raw data information about a chunk.

A chunk is the generic term for a pixel data block in an EXR file, as described in the OpenEXR File Layout documentation. This is common between all different forms of data that can be stored.

Chunk Writing

exr_result_t exr_write_scanline_chunk_info(exr_context_t ctxt, int part_index, int y, exr_chunk_info_t *cinfo)

Initialize a exr_chunk_info_t structure when encoding scanline data (similar to read but does not do anything with a chunk table).

exr_result_t exr_write_tile_chunk_info(exr_context_t ctxt, int part_index, int tilex, int tiley, int levelx, int levely, exr_chunk_info_t *cinfo)

Initialize a exr_chunk_info_t structure when encoding tiled data (similar to read but does not do anything with a chunk table).

exr_result_t exr_write_scanline_chunk(exr_context_t ctxt, int part_index, int y, const void *packed_data, uint64_t packed_size)

y must the appropriate starting y for the specified chunk.

exr_result_t exr_write_deep_scanline_chunk(exr_context_t ctxt, int part_index, int y, const void *packed_data, uint64_t packed_size, uint64_t unpacked_size, const void *sample_data, uint64_t sample_data_size)

y must the appropriate starting y for the specified chunk.

exr_result_t exr_write_tile_chunk(exr_context_t ctxt, int part_index, int tilex, int tiley, int levelx, int levely, const void *packed_data, uint64_t packed_size)
exr_result_t exr_write_deep_tile_chunk(exr_context_t ctxt, int part_index, int tilex, int tiley, int levelx, int levely, const void *packed_data, uint64_t packed_size, uint64_t unpacked_size, const void *sample_data, uint64_t sample_data_size)

Open for Read

exr_result_t exr_test_file_header(const char *filename, const exr_context_initializer_t *ctxtdata)

Check the magic number of the file and report EXR_ERR_SUCCESS if the file appears to be a valid file (or at least has the correct magic number and can be read).

exr_result_t exr_start_read(exr_context_t *ctxt, const char *filename, const exr_context_initializer_t *ctxtdata)

Create and initialize a read-only exr read context.

If a custom read function is provided, the filename is for informational purposes only, the system assumes the user has previously opened a stream, file, or whatever and placed relevant data in userdata to access that.

One notable attribute of the context is that once it has been created and returned a successful code, it has parsed all the header data. This is done as one step such that it is easier to provide a safe context for multiple threads to request data from the same context concurrently.

Once finished reading data, use exr_finish() to clean up the context.

If you have custom I/O requirements, see the initializer context documentation exr_context_initializer_t. The ctxtdata parameter is optional, if NULL, default values will be used.

Open for Write

exr_result_t exr_start_write(exr_context_t *ctxt, const char *filename, exr_default_write_mode_t default_mode, const exr_context_initializer_t *ctxtdata)

Create and initialize a write-only context.

If a custom write function is provided, the filename is for informational purposes only, and the default_mode parameter will be ignored. As such, the system assumes the user has previously opened a stream, file, or whatever and placed relevant data in userdata to access that.

Multi-Threading: To avoid issues with creating multi-part EXR files, the library approaches writing as a multi-step process, so the same concurrent guarantees can not be made for writing a file. The steps are:

  1. Context creation (this function)

  2. Part definition (required attributes and additional metadata)

  3. Transition to writing data (this “commits” the part definitions, any changes requested after will result in an error)

  4. Write part data in sequential order of parts (part0 -> partN-1).

  5. Within each part, multiple threads can be encoding and writing data concurrently. For some EXR part definitions, this may be able to write data concurrently when it can predict the chunk sizes, or data is allowed to be padded. For others, it may need to temporarily cache chunks until the data is received to flush in order. The concurrency around this is handled by the library

  6. Once finished writing data, use exr_finish() to clean up the context, which will flush any unwritten data such as the final chunk offset tables, and handle the temporary file flags.

If you have custom I/O requirements, see the initializer context documentation exr_context_initializer_t. The ctxtdata parameter is optional, if NULL, default values will be used.

exr_result_t exr_start_inplace_header_update(exr_context_t *ctxt, const char *filename, const exr_context_initializer_t *ctxtdata)

Create a new context for updating an exr file in place.

This is a custom mode that allows one to modify the value of a metadata entry, although not to change the size of the header, or any of the image data.

If you have custom I/O requirements, see the initializer context documentation exr_context_initializer_t. The ctxtdata parameter is optional, if NULL, default values will be used.

exr_result_t exr_write_header(exr_context_t ctxt)

Write the header data.

Opening a new output file has a small initialization state problem compared to opening for read/update: we need to enable the user to specify an arbitrary set of metadata across an arbitrary number of parts. To avoid having to create the list of parts and entire metadata up front, prior to calling the above exr_start_write(), allow the data to be set, then once this is called, it switches into a mode where the library assumes the data is now valid.

It will recompute the number of chunks that will be written, and reset the chunk offsets. If you modify file attributes or part information after a call to this, it will error.

exr_result_t exr_set_longname_support(exr_context_t ctxt, int onoff)

Enable long name support in the output context.

Close

typedef void (*exr_destroy_stream_func_ptr_t)(exr_const_context_t ctxt, void *userdata, int failed)

Destroy custom stream function pointer.

Generic callback to clean up user data for custom streams. This is called when the file is closed and expected not to error.

Parameters

failed – Indicates the write operation failed, the implementor may wish to cleanup temporary files

exr_result_t exr_finish(exr_context_t *ctxt)

Close and free any internally allocated memory, calling any provided destroy function for custom streams.

If the file was opened for write, first save the chunk offsets or any other unwritten data.

Context

typedef struct _priv_exr_context_t *exr_context_t

Opaque context handle.

The implementation of this is partly opaque to provide better version portability, and all accesses to relevant data should happen using provided functions. This handle serves as a container and identifier for all the metadata and parts associated with a file and/or stream.

typedef const struct _priv_exr_context_t *exr_const_context_t

Warning

doxygenstruct: Cannot find class “_exr_context_initializer” in doxygen xml output for project “OpenEXR” from directory: doxygen/xml

typedef struct _exr_context_initializer_v2 exr_context_initializer_t

Struct used to pass function pointers into the context initialization routines.

This partly exists to avoid the chicken and egg issue around creating the storage needed for the context on systems which want to override the malloc/free routines.

However, it also serves to make a tidier/simpler set of functions to create and start processing exr files.

The size member is required for version portability.

It can be initialized using EXR_DEFAULT_CONTEXT_INITIALIZER.

exr_context_initializer_t myctxtinit = DEFAULT_CONTEXT_INITIALIZER;
myctxtinit.error_cb = &my_super_cool_error_callback_function;
...

exr_result_t exr_get_file_name(exr_const_context_t ctxt, const char **name)

Retrieve the file name the context is for as provided during the start routine.

Do not free the resulting string.

exr_result_t exr_get_user_data(exr_const_context_t ctxt, void **userdata)

Query the user data the context was constructed with.

This is perhaps useful in the error handler callback to jump back into an object the user controls.

exr_result_t exr_register_attr_type_handler(exr_context_t ctxt, const char *type, exr_result_t (*unpack_func_ptr)(exr_context_t ctxt, const void *data, int32_t attrsize, int32_t *outsize, void **outbuffer), exr_result_t (*pack_func_ptr)(exr_context_t ctxt, const void *data, int32_t datasize, int32_t *outsize, void *outbuffer), void (*destroy_unpacked_func_ptr)(exr_context_t ctxt, void *data, int32_t datasize))

Any opaque attribute data entry of the specified type is tagged with these functions enabling downstream users to unpack (or pack) the data.

The library handles the memory packed data internally, but the handler is expected to allocate and manage memory for the unpacked buffer (the library will call the destroy function).

NB: the pack function will be called twice (unless there is a memory failure), the first with a NULL buffer, requesting the maximum size (or exact size if known) for the packed buffer, then the second to fill the output packed buffer, at which point the size can be re-updated to have the final, precise size to put into the file.

Decoding

struct _exr_decode_pipeline

Struct meant to be used on a per-thread basis for reading exr data.

As should be obvious, this structure is NOT thread safe, but rather meant to be used by separate threads, which can all be accessing the same context concurrently.

Public Members

exr_coding_channel_info_t *channels

The output channel information for this chunk.

User is expected to fill the channel pointers for the desired output channels (any that are NULL will be skipped) if you are going to use exr_decoding_choose_default_routines(). If all that is desired is to read and decompress the data, this can be left uninitialized.

Describes the channel information. This information is allocated dynamically during exr_decoding_initialize().

uint16_t decode_flags

Decode flags to control the behavior.

int part_index

Copy of the parameters given to the initialize/update for convenience.

void *decoding_user_data

Can be used by the user to pass custom context data through the decode pipeline.

void *packed_buffer

The (compressed) buffer.

If NULL, will be allocated during the run of the pipeline.

If the caller wishes to take control of the buffer, simple adopt the pointer and set it to NULL here. Be cognizant of any custom allocators.

size_t packed_alloc_size

Used when re-using the same decode pipeline struct to know if chunk is changed size whether current buffer is large enough.

void *unpacked_buffer

The decompressed buffer (unpacked_size from the chunk block info), but still packed into storage order, only needed for compressed files.

If NULL, will be allocated during the run of the pipeline when needed.

If the caller wishes to take control of the buffer, simple adopt the pointer and set it to NULL here. Be cognizant of any custom allocators.

size_t unpacked_alloc_size

Used when re-using the same decode pipeline struct to know if chunk is changed size whether current buffer is large enough.

void *packed_sample_count_table

For deep or other non-image data: packed sample table (compressed, raw on disk representation).

int32_t *sample_count_table

Usable, native sample count table.

Depending on the flag set above, will be decoded to either a cumulative list (n, n+m, n+m+o, …), or an individual table (n, m, o, …). As an optimization, if the latter individual count table is chosen, an extra int32_t will be allocated at the end of the table to contain the total count of samples, so the table will be n+1 samples in size.

void *scratch_buffer_1

A scratch buffer of unpacked_size for intermediate results.

If NULL, will be allocated during the run of the pipeline when needed.

If the caller wishes to take control of the buffer, simple adopt the pointer and set it to NULL here. Be cognizant of any custom allocators.

size_t scratch_alloc_size_1

Used when re-using the same decode pipeline struct to know if chunk is changed size whether current buffer is large enough.

void *scratch_buffer_2

Some decompression routines may need a second scratch buffer (zlib).

If NULL, will be allocated during the run of the pipeline when needed.

If the caller wishes to take control of the buffer, simple adopt the pointer and set it to NULL here. Be cognizant of any custom allocators.

size_t scratch_alloc_size_2

Used when re-using the same decode pipeline struct to know if chunk is changed size whether current buffer is large enough.

void *(*alloc_fn)(exr_transcoding_pipeline_buffer_id_t, size_t)

Enable a custom allocator for the different buffers (if decoding on a GPU).

If NULL, will use the allocator from the context.

void (*free_fn)(exr_transcoding_pipeline_buffer_id_t, void*)

Enable a custom allocator for the different buffers (if decoding on a GPU).

If NULL, will use the allocator from the context.

exr_result_t (*read_fn)(struct _exr_decode_pipeline *pipeline)

Function chosen to read chunk data from the context.

Initialized to a default generic read routine, may be updated based on channel information when exr_decoding_choose_default_routines() is called. This is done such that if the file is uncompressed and the output channel data is planar and the same type, the read function can read straight into the output channels, getting closer to a zero-copy operation. Otherwise a more traditional read, decompress, then unpack pipeline will be used with a default reader.

This is allowed to be overridden, but probably is not necessary in most scenarios.

exr_result_t (*decompress_fn)(struct _exr_decode_pipeline *pipeline)

Function chosen based on the compression type of the part to decompress data.

If the user has a custom decompression method for the compression on this part, this can be changed after initialization.

If only compressed data is desired, then assign this to NULL after initialization.

exr_result_t (*realloc_nonimage_data_fn)(struct _exr_decode_pipeline *pipeline)

Function which can be provided if you have bespoke handling for non-image data and need to re-allocate the data to handle the about-to-be unpacked data.

If left NULL, will assume the memory pointed to by the channel pointers is sufficient.

exr_result_t (*unpack_and_convert_fn)(struct _exr_decode_pipeline *pipeline)

Function chosen based on the output layout of the channels of the part to decompress data.

This will be NULL after initialization, until the user specifies a custom routine, or initializes the channel data and calls exr_decoding_choose_default_routines().

If only compressed data is desired, then leave or assign this to NULL after initialization.

exr_coding_channel_info_t _quick_chan_store[5]

Small stash of channel info values.

This is faster than calling malloc when the channel count in the part is small (RGBAZ), which is super common, however if there are a large number of channels, it will allocate space for that, so do not rely on this being used.

typedef struct _exr_decode_pipeline exr_decode_pipeline_t

Struct meant to be used on a per-thread basis for reading exr data.

As should be obvious, this structure is NOT thread safe, but rather meant to be used by separate threads, which can all be accessing the same context concurrently.

exr_result_t exr_decoding_initialize(exr_const_context_t ctxt, int part_index, const exr_chunk_info_t *cinfo, exr_decode_pipeline_t *decode)

Initialize the decoding pipeline structure with the channel info for the specified part, and the first block to be read.

NB: The decode->unpack_and_convert_fn field will be NULL after this. If that stage is desired, initialize the channel output information and call exr_decoding_choose_default_routines().

exr_result_t exr_decoding_choose_default_routines(exr_const_context_t ctxt, int part_index, exr_decode_pipeline_t *decode)

Given an initialized decode pipeline, find appropriate functions to read and shuffle/convert data into the defined channel outputs.

Calling this is not required if custom routines will be used, or if just the raw compressed data is desired. Although in that scenario, it is probably easier to just read the chunk directly using exr_read_chunk().

exr_result_t exr_decoding_update(exr_const_context_t ctxt, int part_index, const exr_chunk_info_t *cinfo, exr_decode_pipeline_t *decode)

Given a decode pipeline previously initialized, update it for the new chunk to be read.

In this manner, memory buffers can be re-used to avoid continual malloc/free calls. Further, it allows the previous choices for the various functions to be quickly re-used.

exr_result_t exr_decoding_run(exr_const_context_t ctxt, int part_index, exr_decode_pipeline_t *decode)

Execute the decoding pipeline.

exr_result_t exr_decoding_destroy(exr_const_context_t ctxt, exr_decode_pipeline_t *decode)

Free any intermediate memory in the decoding pipeline.

This does not free any pointers referred to in the channel info areas, but rather only the intermediate buffers and memory needed for the structure itself.

Encoding

Warning

doxygenenum: Cannot find enum “transcoding_pipeline_buffer_id” in doxygen xml output for project “OpenEXR” from directory: doxygen/xml

struct _exr_encode_pipeline

Struct meant to be used on a per-thread basis for writing exr data.

As should be obvious, this structure is NOT thread safe, but rather meant to be used by separate threads, which can all be accessing the same context concurrently.

Public Members

exr_coding_channel_info_t *channels

The output channel information for this chunk.

User is expected to fill the channel pointers for the input channels. For writing, all channels must be initialized prior to using exr_encoding_choose_default_routines(). If a custom pack routine is written, that is up to the implementor.

Describes the channel information. This information is allocated dynamically during exr_encoding_initialize().

uint16_t encode_flags

Encode flags to control the behavior.

int part_index

Copy of the parameters given to the initialize/update for convenience.

void *encoding_user_data

Can be used by the user to pass custom context data through the encode pipeline.

void *packed_buffer

The packed buffer where individual channels have been put into here.

If NULL, will be allocated during the run of the pipeline.

If the caller wishes to take control of the buffer, simple adopt the pointer and set it to NULL here. Be cognizant of any custom allocators.

uint64_t packed_bytes

Differing from the allocation size, the number of actual bytes.

size_t packed_alloc_size

Used when re-using the same encode pipeline struct to know if chunk is changed size whether current buffer is large enough.

If NULL, will be allocated during the run of the pipeline.

If the caller wishes to take control of the buffer, simple adopt the pointer and set it to NULL here. Be cognizant of any custom allocators.

int32_t *sample_count_table

For deep data.

NB: the members NOT const because we need to temporarily swap it to xdr order and restore it (to avoid a duplicate buffer allocation).

Depending on the flag set above, will be treated either as a cumulative list (n, n+m, n+m+o, …), or an individual table (n, m, o, …).

size_t sample_count_alloc_size

Allocated table size (to avoid re-allocations).

Number of samples must always be width * height for the chunk.

void *packed_sample_count_table

Packed sample table (compressed, raw on disk representation) for deep or other non-image data.

size_t packed_sample_count_bytes

Number of bytes to write (actual size) for the packed_sample_count_table.

size_t packed_sample_count_alloc_size

Allocated size (to avoid re-allocations) for the packed_sample_count_table.

void *compressed_buffer

The compressed buffer, only needed for compressed files.

If NULL, will be allocated during the run of the pipeline when needed.

If the caller wishes to take control of the buffer, simple adopt the pointer and set it to NULL here. Be cognizant of any custom allocators.

size_t compressed_bytes

Must be filled in as the pipeline runs to inform the writing software about the compressed size of the chunk (if it is an uncompressed file or the compression would make the file larger, it is expected to be the packed_buffer)

If the caller wishes to take control of the buffer, simple adopt the pointer and set it to zero here. Be cognizant of any custom allocators.

size_t compressed_alloc_size

Used when re-using the same encode pipeline struct to know if chunk is changed size whether current buffer is large enough.

If NULL, will be allocated during the run of the pipeline when needed.

If the caller wishes to take control of the buffer, simple adopt the pointer and set it to zero here. Be cognizant of any custom allocators.

void *scratch_buffer_1

A scratch buffer for intermediate results.

If NULL, will be allocated during the run of the pipeline when needed.

If the caller wishes to take control of the buffer, simple adopt the pointer and set it to NULL here. Be cognizant of any custom allocators.

size_t scratch_alloc_size_1

Used when re-using the same encode pipeline struct to know if chunk is changed size whether current buffer is large enough.

If NULL, will be allocated during the run of the pipeline when needed.

If the caller wishes to take control of the buffer, simple adopt the pointer and set it to NULL here. Be cognizant of any custom allocators.

void *scratch_buffer_2

Some compression routines may need a second scratch buffer.

If NULL, will be allocated during the run of the pipeline when needed.

If the caller wishes to take control of the buffer, simple adopt the pointer and set it to NULL here. Be cognizant of any custom allocators.

size_t scratch_alloc_size_2

Used when re-using the same encode pipeline struct to know if chunk is changed size whether current buffer is large enough.

void *(*alloc_fn)(exr_transcoding_pipeline_buffer_id_t, size_t)

Enable a custom allocator for the different buffers (if encoding on a GPU).

If NULL, will use the allocator from the context.

void (*free_fn)(exr_transcoding_pipeline_buffer_id_t, void*)

Enable a custom allocator for the different buffers (if encoding on a GPU).

If NULL, will use the allocator from the context.

exr_result_t (*convert_and_pack_fn)(struct _exr_encode_pipeline *pipeline)

Function chosen based on the output layout of the channels of the part to decompress data.

If the user has a custom method for the compression on this part, this can be changed after initialization.

exr_result_t (*compress_fn)(struct _exr_encode_pipeline *pipeline)

Function chosen based on the compression type of the part to compress data.

If the user has a custom compression method for the compression type on this part, this can be changed after initialization.

exr_result_t (*yield_until_ready_fn)(struct _exr_encode_pipeline *pipeline)

This routine is used when waiting for other threads to finish writing previous chunks such that this thread can write this chunk.

This is used for parts which have a specified chunk ordering (increasing/decreasing y) and the chunks can not be written randomly (as could be true for uncompressed).

This enables the calling application to contribute thread time to other computation as needed, or just use something like pthread_yield().

By default, this routine will be assigned to a function which returns an error, failing the encode immediately. In this way, it assumes that there is only one thread being used for writing.

It is up to the user to provide an appropriate routine if performing multi-threaded writing.

exr_result_t (*write_fn)(struct _exr_encode_pipeline *pipeline)

Function chosen to write chunk data to the context.

This is allowed to be overridden, but probably is not necessary in most scenarios.

exr_coding_channel_info_t _quick_chan_store[5]

Small stash of channel info values.

This is faster than calling malloc when the channel count in the part is small (RGBAZ), which is super common, however if there are a large number of channels, it will allocate space for that, so do not rely on this being used.

typedef struct _exr_encode_pipeline exr_encode_pipeline_t

Struct meant to be used on a per-thread basis for writing exr data.

As should be obvious, this structure is NOT thread safe, but rather meant to be used by separate threads, which can all be accessing the same context concurrently.

struct exr_coding_channel_info_t

Struct for negotiating buffers when decoding/encoding chunks of data.

This is generic and meant to negotiate exr data bi-directionally, in that the same structure is used for both decoding and encoding chunks for read and write, respectively.

The first half of the structure will be filled by the library, and the caller is expected to fill the second half appropriately.

Public Members

const char *channel_name

Channel name.

This is provided as a convenient reference. Do not free, this refers to the internal data structure in the context.

int32_t height

Number of lines for this channel in this chunk.

May be 0 or less than overall image height based on sampling (i.e. when in 4:2:0 type sampling)

int32_t width

Width in pixel count.

May be 0 or less than overall image width based on sampling (i.e. 4:2:2 will have some channels have fewer values).

int32_t x_samples

Horizontal subsampling information.

int32_t y_samples

Vertical subsampling information.

uint8_t p_linear

Linear flag from channel definition (used by b44).

int8_t bytes_per_element

How many bytes per pixel this channel consumes (2 for float16, 4 for float32/uint32).

uint16_t data_type

Small form of exr_pixel_type_t enum (EXR_PIXEL_UINT/HALF/FLOAT).

int16_t user_bytes_per_element

How many bytes per pixel the input is or output should be (2 for float16, 4 for float32/uint32).

Defaults to same size as input.

uint16_t user_data_type

Small form of exr_pixel_type_t enum (EXR_PIXEL_UINT/HALF/FLOAT).

Defaults to same type as input.

int32_t user_pixel_stride

Increment to get to next pixel.

This is in bytes. Must be specified when the decode pointer is specified (and always for encode).

This is useful for implementing transcoding generically of planar or interleaved data. For planar data, where the layout is RRRRRGGGGGBBBBB, you can pass in 1 * bytes per component.

int32_t user_line_stride

When lines > 1 for a chunk, this is the increment used to get from beginning of line to beginning of next line.

This is in bytes. Must be specified when the decode pointer is specified (and always for encode).

uint8_t *decode_to_ptr
const uint8_t *encode_from_ptr
union exr_coding_channel_info_t::[anonymous] [anonymous]

This data member has different requirements reading vs writing.

When reading, if this is left as NULL, the channel will be skipped during read and not filled in. During a write operation, this pointer is considered const and not modified. To make this more clear, a union is used here.

exr_result_t exr_encoding_initialize(exr_const_context_t ctxt, int part_index, const exr_chunk_info_t *cinfo, exr_encode_pipeline_t *encode_pipe)

Initialize the encoding pipeline structure with the channel info for the specified part based on the chunk to be written.

NB: The encode_pipe->pack_and_convert_fn field will be NULL after this. If that stage is desired, initialize the channel output information and call exr_encoding_choose_default_routines().

exr_result_t exr_encoding_choose_default_routines(exr_const_context_t ctxt, int part_index, exr_encode_pipeline_t *encode_pipe)

Given an initialized encode pipeline, find an appropriate function to shuffle and convert data into the defined channel outputs.

Calling this is not required if a custom routine will be used, or if just the raw decompressed data is desired.

exr_result_t exr_encoding_update(exr_const_context_t ctxt, int part_index, const exr_chunk_info_t *cinfo, exr_encode_pipeline_t *encode_pipe)

Given a encode pipeline previously initialized, update it for the new chunk to be written.

In this manner, memory buffers can be re-used to avoid continual malloc/free calls. Further, it allows the previous choices for the various functions to be quickly re-used.

exr_result_t exr_encoding_run(exr_const_context_t ctxt, int part_index, exr_encode_pipeline_t *encode_pipe)

Execute the encoding pipeline.

exr_result_t exr_encoding_destroy(exr_const_context_t ctxt, exr_encode_pipeline_t *encode_pipe)

Free any intermediate memory in the encoding pipeline.

This does NOT free any pointers referred to in the channel info areas, but rather only the intermediate buffers and memory needed for the structure itself.

Attribute Values

struct exr_attr_chromaticities_t

Struct to hold color chromaticities to interpret the tristimulus color values in the image data.

Public Members

float red_x
float red_y
float green_x
float green_y
float blue_x
float blue_y
float white_x
float white_y
struct exr_attr_keycode_t

Struct to hold keycode information.

Public Members

int32_t film_mfc_code
int32_t film_type
int32_t prefix
int32_t count
int32_t perf_offset
int32_t perfs_per_frame
int32_t perfs_per_count
enum exr_attribute_type_t

Built-in/native attribute type enum.

This will enable us to do a tagged type struct to generically store attributes.

Values:

enumerator EXR_ATTR_UNKNOWN

Type indicating an error or uninitialized attribute.

enumerator EXR_ATTR_BOX2I

Integer region definition.

See

exr_attr_box2i_t.

enumerator EXR_ATTR_BOX2F

Float region definition.

See

exr_attr_box2f_t.

enumerator EXR_ATTR_CHLIST

Definition of channels in file.

See

exr_chlist_entry.

enumerator EXR_ATTR_CHROMATICITIES

Values to specify color space of colors in file.

See

exr_attr_chromaticities_t.

enumerator EXR_ATTR_COMPRESSION

uint8_t declaring compression present.

enumerator EXR_ATTR_DOUBLE

Double precision floating point number.

enumerator EXR_ATTR_ENVMAP

uint8_t declaring environment map type.

enumerator EXR_ATTR_FLOAT

Normal (4 byte) precision floating point number.

enumerator EXR_ATTR_FLOAT_VECTOR

List of normal (4 byte) precision floating point numbers.

enumerator EXR_ATTR_INT

32-bit signed integer value.

enumerator EXR_ATTR_KEYCODE

Struct recording keycode.

See

exr_attr_keycode_t.

enumerator EXR_ATTR_LINEORDER

uint8_t declaring scanline ordering.

enumerator EXR_ATTR_M33F

9 32-bit floats representing a 3x3 matrix.

enumerator EXR_ATTR_M33D

9 64-bit floats representing a 3x3 matrix.

enumerator EXR_ATTR_M44F

16 32-bit floats representing a 4x4 matrix.

enumerator EXR_ATTR_M44D

16 64-bit floats representing a 4x4 matrix.

enumerator EXR_ATTR_PREVIEW

2 unsigned ints followed by 4 x w x h uint8_t image.

enumerator EXR_ATTR_RATIONAL

int followed by unsigned int

enumerator EXR_ATTR_STRING

int (length) followed by char string data.

enumerator EXR_ATTR_STRING_VECTOR

0 or more text strings (int + string).

number is based on attribute size.

enumerator EXR_ATTR_TILEDESC

2 unsigned ints xSize, ySize followed by mode.

enumerator EXR_ATTR_TIMECODE

2 unsigned ints time and flags, user data.

enumerator EXR_ATTR_V2I

Pair of 32-bit integers.

enumerator EXR_ATTR_V2F

Pair of 32-bit floats.

enumerator EXR_ATTR_V2D

Pair of 64-bit floats.

enumerator EXR_ATTR_V3I

Set of 3 32-bit integers.

enumerator EXR_ATTR_V3F

Set of 3 32-bit floats.

enumerator EXR_ATTR_V3D

Set of 3 64-bit floats.

enumerator EXR_ATTR_OPAQUE

User/unknown provided type.

enumerator EXR_ATTR_LAST_KNOWN_TYPE
struct exr_attribute_t

Storage, name and type information for an attribute.

Attributes (metadata) for the file cause a surprising amount of overhead. It is not uncommon for a production-grade EXR to have many attributes. As such, the attribute struct is designed in a slightly more complicated manner. It is optimized to have the storage for that attribute: the struct itself, the name, the type, and the data all allocated as one block. Further, the type and standard names may use a static string to avoid allocating space for those as necessary with the pointers pointing to static strings (not to be freed). Finally, small values are optimized for.

Public Members

const char *name

Name of the attribute.

const char *type_name

String type name of the attribute.

uint8_t name_length

Length of name string (short flag is 31 max, long allows 255).

uint8_t type_name_length

Length of type string (short flag is 31 max, long allows 255).

uint8_t pad[2]
exr_attribute_type_t type

Enum of the attribute type.

uint8_t uc
double d
float f
int32_t i
exr_attr_box2i_t *box2i
exr_attr_box2f_t *box2f
exr_attr_chlist_t *chlist
exr_attr_chromaticities_t *chromaticities
exr_attr_keycode_t *keycode
exr_attr_float_vector_t *floatvector
exr_attr_m33f_t *m33f
exr_attr_m33d_t *m33d
exr_attr_m44f_t *m44f
exr_attr_m44d_t *m44d
exr_attr_preview_t *preview
exr_attr_rational_t *rational
exr_attr_string_t *string
exr_attr_string_vector_t *stringvector
exr_attr_tiledesc_t *tiledesc
exr_attr_timecode_t *timecode
exr_attr_v2i_t *v2i
exr_attr_v2f_t *v2f
exr_attr_v2d_t *v2d
exr_attr_v3i_t *v3i
exr_attr_v3f_t *v3f
exr_attr_v3d_t *v3d
exr_attr_opaquedata_t *opaque
uint8_t *rawptr
union exr_attribute_t::[anonymous] [anonymous]

Union of pointers of different types that can be used to type pun to an appropriate type for builtins.

Do note that while this looks like a big thing, it is only the size of a single pointer. These are all pointers into some other data block storing the value you want, with the exception of the pod types which are just put in place (i.e. small value optimization).

The attribute type type should directly correlate to one of these entries.

struct exr_attr_v2i_t

Struct to hold a 2-element integer vector.

Public Members

int32_t x
int32_t y
int32_t arr[2]
union exr_attr_v2i_t::[anonymous] [anonymous]
struct exr_attr_v2f_t

Struct to hold a 2-element 32-bit float vector.

Public Members

float x
float y
float arr[2]
union exr_attr_v2f_t::[anonymous] [anonymous]
struct exr_attr_v2d_t

Struct to hold a 2-element 64-bit float vector.

Public Members

double x
double y
double arr[2]
union exr_attr_v2d_t::[anonymous] [anonymous]
struct exr_attr_v3i_t

Struct to hold a 3-element integer vector.

Public Members

int32_t x
int32_t y
int32_t z
int32_t arr[3]
union exr_attr_v3i_t::[anonymous] [anonymous]
struct exr_attr_v3f_t

Struct to hold a 3-element 32-bit float vector.

Public Members

float x
float y
float z
float arr[3]
union exr_attr_v3f_t::[anonymous] [anonymous]
struct exr_attr_v3d_t

Struct to hold a 3-element 64-bit float vector.

Public Members

double x
double y
double z
double arr[3]
union exr_attr_v3d_t::[anonymous] [anonymous]
struct exr_attr_m33f_t

struct to hold a 32-bit floating-point 3x3 matrix.

Public Members

float m[9]
struct exr_attr_m33d_t

struct to hold a 64-bit floating-point 3x3 matrix.

Public Members

double m[9]
struct exr_attr_m44f_t

Struct to hold a 32-bit floating-point 4x4 matrix.

Public Members

float m[16]
struct exr_attr_m44d_t

Struct to hold a 64-bit floating-point 4x4 matrix.

Public Members

double m[16]
struct exr_attr_box2i_t

Struct to hold an integer box/region definition.

Public Members

exr_attr_v2i_t min
exr_attr_v2i_t max
struct exr_attr_box2f_t

Struct to hold a floating-point box/region definition.

Public Members

exr_attr_v2f_t min
exr_attr_v2f_t max
struct exr_attr_string_t

Storage for a string.

Public Members

int32_t length
int32_t alloc_size

If this is non-zero, the string owns the data, if 0, is a const ref to a static string.

const char *str
struct exr_attr_string_vector_t

Storage for a string vector.

Public Members

int32_t n_strings
int32_t alloc_size

If this is non-zero, the string vector owns the data, if 0, is a const ref.

const exr_attr_string_t *strings
struct exr_attr_float_vector_t

Float vector storage struct.

Public Members

int32_t length
int32_t alloc_size

If this is non-zero, the float vector owns the data, if 0, is a const ref.

const float *arr
struct exr_attr_chlist_entry_t

Individual channel information.

Public Members

exr_attr_string_t name
exr_pixel_type_t pixel_type

Data representation for these pixels: uint, half, float.

uint8_t p_linear

Possible values are 0 and 1 per docs exr_perceptual_treatment_t.

uint8_t reserved[3]
int32_t x_sampling
int32_t y_sampling
struct exr_attr_chlist_t

List of channel information (sorted alphabetically).

Public Members

int num_channels
int num_alloced
const exr_attr_chlist_entry_t *entries
struct exr_attr_preview_t

Struct to define attributes of an embedded preview image.

Public Members

uint32_t width
uint32_t height
size_t alloc_size

If this is non-zero, the preview owns the data, if 0, is a const ref.

const uint8_t *rgba
struct exr_attr_tiledesc_t

Struct holding base tiledesc attribute type defined in spec.

NB: This is in a tightly packed area so it can be read directly, be careful it doesn’t become padded to the next uint32_t boundary.

Public Members

uint32_t x_size
uint32_t y_size
uint8_t level_and_round
struct exr_attr_timecode_t

Struct to hold timecode information.

Public Members

uint32_t time_and_flags
uint32_t user_data
struct exr_attr_opaquedata_t

Custom storage structure for opaque data.

Handlers for opaque types can be registered, then when a non-builtin type is encountered with a registered handler, the function pointers to unpack/pack it will be set up.

See

exr_register_attr_type_handler

Public Members

int32_t size
int32_t unpacked_size
int32_t packed_alloc_size

If this is non-zero, the struct owns the data, if 0, is a const ref.

uint8_t pad[4]
void *packed_data
void *unpacked_data

When an application wants to have custom data, they can store an unpacked form here which will be requested to be destroyed upon destruction of the attribute.

exr_result_t (*unpack_func_ptr)(exr_context_t ctxt, const void *data, int32_t attrsize, int32_t *outsize, void **outbuffer)

An application can register an attribute handler which then fills in these function pointers.

This allows a user to delay the expansion of the custom type until access is desired, and similarly, to delay the packing of the data until write time.

exr_result_t (*pack_func_ptr)(exr_context_t ctxt, const void *data, int32_t datasize, int32_t *outsize, void *outbuffer)
void (*destroy_unpacked_func_ptr)(exr_context_t ctxt, void *data, int32_t attrsize)

Reading

typedef int64_t (*exr_read_func_ptr_t)(exr_const_context_t ctxt, void *userdata, void *buffer, uint64_t sz, uint64_t offset, exr_stream_error_func_ptr_t error_cb)

Read custom function pointer.

Used to read data from a custom output. Expects similar semantics to pread or ReadFile with overlapped data under win32.

It is required that this provides thread-safe concurrent access to the same file. If the stream/input layer you are providing does not have this guarantee, your are responsible for providing appropriate serialization of requests.

A file should be expected to be accessed in the following pattern:

  • upon open, the header and part information attributes will be read

  • upon the first image read request, the offset tables will be read multiple threads accessing this concurrently may actually read these values at the same time

  • chunks can then be read in any order as preferred by the application

While this should mean that the header will be read in ‘stream’ order (no seeks required), no guarantee is made beyond that to retrieve image/deep data in order. So if the backing file is truly a stream, it is up to the provider to implement appropriate caching of data to give the appearance of being able to seek/read atomically.

typedef int64_t (*exr_query_size_func_ptr_t)(exr_const_context_t ctxt, void *userdata)

Query stream size function pointer.

Used to query the size of the file, or amount of data representing the openexr file in the data stream.

This is used to validate requests against the file. If the size is unavailable, return -1, which will disable these validation steps for this file, although appropriate memory safeguards must be in place in the calling application.

exr_result_t exr_get_count(exr_const_context_t ctxt, int *count)

Query how many parts are in the file.

exr_result_t exr_get_name(exr_const_context_t ctxt, int part_index, const char **out)

Query the part name for the specified part.

NB: If this file is a single part file and name has not been set, this will return NULL.

exr_result_t exr_get_storage(exr_const_context_t ctxt, int part_index, exr_storage_t *out)

Query the storage type for the specified part.

exr_result_t exr_get_tile_levels(exr_const_context_t ctxt, int part_index, int32_t *levelsx, int32_t *levelsy)

Query how many levels are in the specified part.

If the part is a tiled part, fill in how many tile levels are present.

Return ERR_SUCCESS on success, an error otherwise (i.e. if the part is not tiled).

It is valid to pass NULL to either of the levelsx or levelsy arguments, which enables testing if this part is a tiled part, or if you don’t need both (i.e. in the case of a mip-level tiled image)

exr_result_t exr_get_tile_sizes(exr_const_context_t ctxt, int part_index, int levelx, int levely, int32_t *tilew, int32_t *tileh)

Query the tile size for a particular level in the specified part.

If the part is a tiled part, fill in the tile size for the specified part/level.

Return ERR_SUCCESS on success, an error otherwise (i.e. if the part is not tiled).

It is valid to pass NULL to either of the tilew or tileh arguments, which enables testing if this part is a tiled part, or if you don’t need both (i.e. in the case of a mip-level tiled image)

exr_result_t exr_get_level_sizes(exr_const_context_t ctxt, int part_index, int levelx, int levely, int32_t *levw, int32_t *levh)

Query the data sizes for a particular level in the specified part.

If the part is a tiled part, fill in the width/height for the specified levels.

Return ERR_SUCCESS on success, an error otherwise (i.e. if the part is not tiled).

It is valid to pass NULL to either of the levw or levh arguments, which enables testing if this part is a tiled part, or if you don’t need both for some reason.

exr_result_t exr_get_chunk_count(exr_const_context_t ctxt, int part_index, int32_t *out)

Return the number of chunks contained in this part of the file.

As in the technical documentation for OpenEXR, the chunk is the generic term for a pixel data block. This is the atomic unit that this library uses to negotiate data to and from a context.

This should be used as a basis for splitting up how a file is processed. Depending on the compression, a different number of scanlines are encoded in each chunk, and since those need to be encoded/decoded as a block, the chunk should be the basis for I/O as well.

exr_result_t exr_get_scanlines_per_chunk(exr_const_context_t ctxt, int part_index, int32_t *out)

Return the number of scanlines chunks for this file part.

When iterating over a scanline file, this may be an easier metric for multi-threading or other access than only negotiating chunk counts, and so is provided as a utility.

exr_result_t exr_get_chunk_unpacked_size(exr_const_context_t ctxt, int part_index, uint64_t *out)

Return the maximum unpacked size of a chunk for the file part.

This may be used ahead of any actual reading of data, so can be used to pre-allocate buffers for multiple threads in one block or whatever your application may require.

exr_result_t exr_get_attribute_count(exr_const_context_t ctxt, int part_index, int32_t *count)

Query the count of attributes in a part.

exr_result_t exr_get_attribute_by_index(exr_const_context_t ctxt, int part_index, exr_attr_list_access_mode_t mode, int32_t idx, const exr_attribute_t **outattr)

Query a particular attribute by index.

exr_result_t exr_get_attribute_by_name(exr_const_context_t ctxt, int part_index, const char *name, const exr_attribute_t **outattr)

Query a particular attribute by name.

exr_result_t exr_get_attribute_list(exr_const_context_t ctxt, int part_index, exr_attr_list_access_mode_t mode, int32_t *count, const exr_attribute_t **outlist)

Query the list of attributes in a part.

This retrieves a list of attributes currently defined in a part.

If outlist is NULL, this function still succeeds, filling only the count. In this manner, the user can allocate memory for the list of attributes, then re-call this function to get the full list.

exr_result_t exr_attr_declare_by_type(exr_context_t ctxt, int part_index, const char *name, const char *type, exr_attribute_t **newattr)

Declare an attribute within the specified part.

Only valid when a file is opened for write.

exr_result_t exr_attr_declare(exr_context_t ctxt, int part_index, const char *name, exr_attribute_type_t type, exr_attribute_t **newattr)

Declare an attribute within the specified part.

Only valid when a file is opened for write.

exr_result_t exr_initialize_required_attr(exr_context_t ctxt, int part_index, const exr_attr_box2i_t *displayWindow, const exr_attr_box2i_t *dataWindow, float pixelaspectratio, const exr_attr_v2f_t *screenWindowCenter, float screenWindowWidth, exr_lineorder_t lineorder, exr_compression_t ctype)

Initialize all required attributes for all files.

NB: other file types do require other attributes, such as the tile description for a tiled file.

exr_result_t exr_initialize_required_attr_simple(exr_context_t ctxt, int part_index, int32_t width, int32_t height, exr_compression_t ctype)

Initialize all required attributes to default values:

  • displayWindow is set to (0, 0 -> width - 1, height - 1)

  • dataWindow is set to (0, 0 -> width - 1, height - 1)

  • pixelAspectRatio is set to 1.0

  • screenWindowCenter is set to 0.f, 0.f

  • screenWindowWidth is set to 1.f

  • lineorder is set to INCREASING_Y

  • compression is set to ctype

exr_result_t exr_copy_unset_attributes(exr_context_t ctxt, int part_index, exr_const_context_t source, int src_part_index)

Copy the attributes from one part to another.

This allows one to quickly unassigned attributes from one source to another.

If an attribute in the source part has not been yet set in the destination part, the item will be copied over.

For example, when you add a part, the storage type and name attributes are required arguments to the definition of a new part, but channels has not yet been assigned. So by calling this with an input file as the source, you can copy the channel definitions (and any other unassigned attributes from the source).

exr_result_t exr_get_channels(exr_const_context_t ctxt, int part_index, const exr_attr_chlist_t **chlist)

Retrieve the list of channels.

exr_result_t exr_get_compression(exr_const_context_t ctxt, int part_index, exr_compression_t *compression)

Retrieve the compression method used for the specified part.

exr_result_t exr_get_data_window(exr_const_context_t ctxt, int part_index, exr_attr_box2i_t *out)

Retrieve the data window for the specified part.

exr_result_t exr_get_display_window(exr_const_context_t ctxt, int part_index, exr_attr_box2i_t *out)

Retrieve the display window for the specified part.

exr_result_t exr_get_lineorder(exr_const_context_t ctxt, int part_index, exr_lineorder_t *out)

Retrieve the line order for storing data in the specified part (use 0 for single part images).

exr_result_t exr_get_pixel_aspect_ratio(exr_const_context_t ctxt, int part_index, float *par)

Retrieve the pixel aspect ratio for the specified part (use 0 for single part images).

exr_result_t exr_get_screen_window_center(exr_const_context_t ctxt, int part_index, exr_attr_v2f_t *wc)

Retrieve the screen oriented window center for the specified part (use 0 for single part images).

exr_result_t exr_get_screen_window_width(exr_const_context_t ctxt, int part_index, float *out)

Retrieve the screen oriented window width for the specified part (use 0 for single part images).

exr_result_t exr_get_tile_descriptor(exr_const_context_t ctxt, int part_index, uint32_t *xsize, uint32_t *ysize, exr_tile_level_mode_t *level, exr_tile_round_mode_t *round)

Retrieve the tiling info for a tiled part (use 0 for single part images).

exr_result_t exr_get_version(exr_const_context_t ctxt, int part_index, int32_t *out)
exr_result_t exr_attr_get_box2i(exr_const_context_t ctxt, int part_index, const char *name, exr_attr_box2i_t *outval)
exr_result_t exr_attr_get_box2f(exr_const_context_t ctxt, int part_index, const char *name, exr_attr_box2f_t *outval)
exr_result_t exr_attr_get_channels(exr_const_context_t ctxt, int part_index, const char *name, const exr_attr_chlist_t **chlist)

Zero-copy query of channel data.

Do not free or manipulate the chlist data, or use after the lifetime of the context.

exr_result_t exr_attr_get_chromaticities(exr_const_context_t ctxt, int part_index, const char *name, exr_attr_chromaticities_t *chroma)
exr_result_t exr_attr_get_compression(exr_const_context_t ctxt, int part_index, const char *name, exr_compression_t *out)
exr_result_t exr_attr_get_double(exr_const_context_t ctxt, int part_index, const char *name, double *out)
exr_result_t exr_attr_get_envmap(exr_const_context_t ctxt, int part_index, const char *name, exr_envmap_t *out)
exr_result_t exr_attr_get_float(exr_const_context_t ctxt, int part_index, const char *name, float *out)
exr_result_t exr_attr_get_float_vector(exr_const_context_t ctxt, int part_index, const char *name, int32_t *sz, const float **out)

Zero-copy query of float data.

Do not free or manipulate the out data, or use after the lifetime of the context.

exr_result_t exr_attr_get_int(exr_const_context_t ctxt, int part_index, const char *name, int32_t *out)
exr_result_t exr_attr_get_keycode(exr_const_context_t ctxt, int part_index, const char *name, exr_attr_keycode_t *out)
exr_result_t exr_attr_get_lineorder(exr_const_context_t ctxt, int part_index, const char *name, exr_lineorder_t *out)
exr_result_t exr_attr_get_m33f(exr_const_context_t ctxt, int part_index, const char *name, exr_attr_m33f_t *out)
exr_result_t exr_attr_get_m33d(exr_const_context_t ctxt, int part_index, const char *name, exr_attr_m33d_t *out)
exr_result_t exr_attr_get_m44f(exr_const_context_t ctxt, int part_index, const char *name, exr_attr_m44f_t *out)
exr_result_t exr_attr_get_m44d(exr_const_context_t ctxt, int part_index, const char *name, exr_attr_m44d_t *out)
exr_result_t exr_attr_get_preview(exr_const_context_t ctxt, int part_index, const char *name, exr_attr_preview_t *out)
exr_result_t exr_attr_get_rational(exr_const_context_t ctxt, int part_index, const char *name, exr_attr_rational_t *out)
exr_result_t exr_attr_get_string(exr_const_context_t ctxt, int part_index, const char *name, int32_t *length, const char **out)

Zero-copy query of string value.

Do not modify the string pointed to by out, and do not use after the lifetime of the context.

exr_result_t exr_attr_get_string_vector(exr_const_context_t ctxt, int part_index, const char *name, int32_t *size, const char **out)

Zero-copy query of string data.

Do not free the strings pointed to by the array.

Must provide size.

out must be a const char** array large enough to hold the string pointers for the string vector when provided.

exr_result_t exr_attr_get_tiledesc(exr_const_context_t ctxt, int part_index, const char *name, exr_attr_tiledesc_t *out)
exr_result_t exr_attr_get_timecode(exr_const_context_t ctxt, int part_index, const char *name, exr_attr_timecode_t *out)
exr_result_t exr_attr_get_v2i(exr_const_context_t ctxt, int part_index, const char *name, exr_attr_v2i_t *out)
exr_result_t exr_attr_get_v2f(exr_const_context_t ctxt, int part_index, const char *name, exr_attr_v2f_t *out)
exr_result_t exr_attr_get_v2d(exr_const_context_t ctxt, int part_index, const char *name, exr_attr_v2d_t *out)
exr_result_t exr_attr_get_v3i(exr_const_context_t ctxt, int part_index, const char *name, exr_attr_v3i_t *out)
exr_result_t exr_attr_get_v3f(exr_const_context_t ctxt, int part_index, const char *name, exr_attr_v3f_t *out)
exr_result_t exr_attr_get_v3d(exr_const_context_t ctxt, int part_index, const char *name, exr_attr_v3d_t *out)
exr_result_t exr_attr_get_user(exr_const_context_t ctxt, int part_index, const char *name, const char **type, int32_t *size, const void **out)

Writing

typedef int64_t (*exr_write_func_ptr_t)(exr_const_context_t ctxt, void *userdata, const void *buffer, uint64_t sz, uint64_t offset, exr_stream_error_func_ptr_t error_cb)

Write custom function pointer.

Used to write data to a custom output. Expects similar semantics to pwrite or WriteFile with overlapped data under win32.

It is required that this provides thread-safe concurrent access to the same file. While it is unlikely that multiple threads will be used to write data for compressed forms, it is possible.

A file should be expected to be accessed in the following pattern:

  • upon open, the header and part information attributes is constructed.

  • when the write_header routine is called, the header becomes immutable and is written to the file. This computes the space to store the chunk offsets, but does not yet write the values.

  • Image chunks are written to the file, and appear in the order they are written, not in the ordering that is required by the chunk offset table (unless written in that order). This may vary slightly if the size of the chunks is not directly known and tight packing of data is necessary.

  • at file close, the chunk offset tables are written to the file.

exr_result_t exr_add_part(exr_context_t ctxt, const char *partname, exr_storage_t type, int *new_index)

Define a new part in the file.

int exr_add_channel(exr_context_t ctxt, int part_index, const char *name, exr_pixel_type_t ptype, exr_perceptual_treatment_t percept, int32_t xsamp, int32_t ysamp)

Define a new channel to the output file part.

The percept parameter is used for lossy compression techniques to indicate that the value represented is closer to linear (1) or closer to logarithmic (0). For r, g, b, luminance, this is normally 0.

exr_result_t exr_set_channels(exr_context_t ctxt, int part_index, const exr_attr_chlist_t *channels)

Copy the channels from another source.

Useful if you are manually constructing the list or simply copying from an input file.

exr_result_t exr_set_compression(exr_context_t ctxt, int part_index, exr_compression_t ctype)

Set the compression method used for the specified part.

int exr_set_data_window(exr_context_t ctxt, int part_index, const exr_attr_box2i_t *dw)

Set the data window for the specified part.

int exr_set_display_window(exr_context_t ctxt, int part_index, const exr_attr_box2i_t *dw)

Set the display window for the specified part.

exr_result_t exr_set_lineorder(exr_context_t ctxt, int part_index, exr_lineorder_t lo)

Set the line order for storing data in the specified part (use 0 for single part images).

exr_result_t exr_set_pixel_aspect_ratio(exr_context_t ctxt, int part_index, float par)

Set the pixel aspect ratio for the specified part (use 0 for single part images).

int exr_set_screen_window_center(exr_context_t ctxt, int part_index, const exr_attr_v2f_t *wc)

Set the screen oriented window center for the specified part (use 0 for single part images).

exr_result_t exr_set_screen_window_width(exr_context_t ctxt, int part_index, float ssw)

Set the screen oriented window width for the specified part (use 0 for single part images).

exr_result_t exr_set_tile_descriptor(exr_context_t ctxt, int part_index, uint32_t x_size, uint32_t y_size, exr_tile_level_mode_t level_mode, exr_tile_round_mode_t round_mode)

Set the tiling info for a tiled part (use 0 for single part images).

exr_result_t exr_set_name(exr_context_t ctxt, int part_index, const char *val)
exr_result_t exr_set_version(exr_context_t ctxt, int part_index, int32_t val)
exr_result_t exr_set_chunk_count(exr_context_t ctxt, int part_index, int32_t val)
exr_result_t exr_attr_set_box2i(exr_context_t ctxt, int part_index, const char *name, const exr_attr_box2i_t *val)
exr_result_t exr_attr_set_box2f(exr_context_t ctxt, int part_index, const char *name, const exr_attr_box2f_t *val)
exr_result_t exr_attr_set_channels(exr_context_t ctxt, int part_index, const char *name, const exr_attr_chlist_t *channels)

This allows one to quickly copy the channels from one file to another.

exr_result_t exr_attr_set_chromaticities(exr_context_t ctxt, int part_index, const char *name, const exr_attr_chromaticities_t *chroma)
exr_result_t exr_attr_set_compression(exr_context_t ctxt, int part_index, const char *name, exr_compression_t comp)
exr_result_t exr_attr_set_double(exr_context_t ctxt, int part_index, const char *name, double val)
exr_result_t exr_attr_set_envmap(exr_context_t ctxt, int part_index, const char *name, exr_envmap_t emap)
exr_result_t exr_attr_set_float(exr_context_t ctxt, int part_index, const char *name, float val)
exr_result_t exr_attr_set_float_vector(exr_context_t ctxt, int part_index, const char *name, int32_t sz, const float *vals)
exr_result_t exr_attr_set_int(exr_context_t ctxt, int part_index, const char *name, int32_t val)
exr_result_t exr_attr_set_keycode(exr_context_t ctxt, int part_index, const char *name, const exr_attr_keycode_t *kc)
exr_result_t exr_attr_set_lineorder(exr_context_t ctxt, int part_index, const char *name, exr_lineorder_t lo)
exr_result_t exr_attr_set_m33f(exr_context_t ctxt, int part_index, const char *name, const exr_attr_m33f_t *m)
exr_result_t exr_attr_set_m33d(exr_context_t ctxt, int part_index, const char *name, const exr_attr_m33d_t *m)
exr_result_t exr_attr_set_m44f(exr_context_t ctxt, int part_index, const char *name, const exr_attr_m44f_t *m)
exr_result_t exr_attr_set_m44d(exr_context_t ctxt, int part_index, const char *name, const exr_attr_m44d_t *m)
exr_result_t exr_attr_set_preview(exr_context_t ctxt, int part_index, const char *name, const exr_attr_preview_t *p)
exr_result_t exr_attr_set_rational(exr_context_t ctxt, int part_index, const char *name, const exr_attr_rational_t *r)
exr_result_t exr_attr_set_string(exr_context_t ctxt, int part_index, const char *name, const char *s)
exr_result_t exr_attr_set_string_vector(exr_context_t ctxt, int part_index, const char *name, int32_t size, const char **sv)
exr_result_t exr_attr_set_tiledesc(exr_context_t ctxt, int part_index, const char *name, const exr_attr_tiledesc_t *td)
exr_result_t exr_attr_set_timecode(exr_context_t ctxt, int part_index, const char *name, const exr_attr_timecode_t *tc)
exr_result_t exr_attr_set_v2i(exr_context_t ctxt, int part_index, const char *name, const exr_attr_v2i_t *v)
exr_result_t exr_attr_set_v2f(exr_context_t ctxt, int part_index, const char *name, const exr_attr_v2f_t *v)
exr_result_t exr_attr_set_v2d(exr_context_t ctxt, int part_index, const char *name, const exr_attr_v2d_t *v)
exr_result_t exr_attr_set_v3i(exr_context_t ctxt, int part_index, const char *name, const exr_attr_v3i_t *v)
exr_result_t exr_attr_set_v3f(exr_context_t ctxt, int part_index, const char *name, const exr_attr_v3f_t *v)
exr_result_t exr_attr_set_v3d(exr_context_t ctxt, int part_index, const char *name, const exr_attr_v3d_t *v)
exr_result_t exr_attr_set_user(exr_context_t ctxt, int part_index, const char *name, const char *type, int32_t size, const void *out)

Error Handling

enum exr_error_code_t

Error codes that may be returned by various functions.

Values:

enumerator EXR_ERR_SUCCESS
enumerator EXR_ERR_OUT_OF_MEMORY
enumerator EXR_ERR_MISSING_CONTEXT_ARG
enumerator EXR_ERR_INVALID_ARGUMENT
enumerator EXR_ERR_ARGUMENT_OUT_OF_RANGE
enumerator EXR_ERR_FILE_ACCESS
enumerator EXR_ERR_FILE_BAD_HEADER
enumerator EXR_ERR_NOT_OPEN_READ
enumerator EXR_ERR_NOT_OPEN_WRITE
enumerator EXR_ERR_HEADER_NOT_WRITTEN
enumerator EXR_ERR_READ_IO
enumerator EXR_ERR_WRITE_IO
enumerator EXR_ERR_NAME_TOO_LONG
enumerator EXR_ERR_MISSING_REQ_ATTR
enumerator EXR_ERR_INVALID_ATTR
enumerator EXR_ERR_NO_ATTR_BY_NAME
enumerator EXR_ERR_ATTR_TYPE_MISMATCH
enumerator EXR_ERR_ATTR_SIZE_MISMATCH
enumerator EXR_ERR_SCAN_TILE_MIXEDAPI
enumerator EXR_ERR_TILE_SCAN_MIXEDAPI
enumerator EXR_ERR_MODIFY_SIZE_CHANGE
enumerator EXR_ERR_ALREADY_WROTE_ATTRS
enumerator EXR_ERR_BAD_CHUNK_LEADER
enumerator EXR_ERR_CORRUPT_CHUNK
enumerator EXR_ERR_INCORRECT_PART
enumerator EXR_ERR_INCORRECT_CHUNK
enumerator EXR_ERR_USE_SCAN_DEEP_WRITE
enumerator EXR_ERR_USE_TILE_DEEP_WRITE
enumerator EXR_ERR_USE_SCAN_NONDEEP_WRITE
enumerator EXR_ERR_USE_TILE_NONDEEP_WRITE
enumerator EXR_ERR_INVALID_SAMPLE_DATA
enumerator EXR_ERR_FEATURE_NOT_IMPLEMENTED
enumerator EXR_ERR_UNKNOWN
typedef void (*exr_error_handler_cb_t)(exr_const_context_t ctxt, exr_result_t code, const char *msg)

Error callback function.

Because a file can be read from using many threads at once, it is difficult to store an error message for later retrieval. As such, when a file is constructed, a callback function can be provided which delivers an error message for the calling application to handle. This will then be delivered on the same thread causing the error.

const char *exr_get_default_error_message(exr_result_t code)

Return a static string corresponding to the specified error code.

The string should not be freed (it is compiled into the binary).

const char *exr_get_error_code_as_string(exr_result_t code)

Return a static string corresponding to the specified error code.

The string should not be freed (it is compiled into the binary).

Debugging

exr_result_t exr_print_context_info(exr_const_context_t c, int verbose)

Debug function: print to stdout the parts and attributes of the context c.