///
///  @header AppleEXR.h
///  @library  /usr/lib/libAppleEXR.dylib
///  @dependency -stdlib=libc++ -lobjc
///  @copyright Copyright © 2019-2020 Apple Inc. All rights reserved.
///  @discussion Apple OpenEXR decoder / encoder
///
///     Created by Ian Ollmann on 8/19/19.
///

/*  ************************************************
 *  ***  SAVE YOURSELF SOME TIME -- Please Read  ***
 *  ************************************************
 *
 *  This is the low level API to a software OpenEXR encoder / decoder,
 *  intended to support the subset of operations needed by ImageIO in a
 *  lightweight, secure and performant manner. The same code is also usable
 *  through the higher level ImageIO.framework CGImageSource / CGImageDestination
 *  APIs and more generally through CGImageRef and associated AppKit / UIKit
 *  NSImage/UIImage interfaces. In most cases, those interfaces will prove
 *  simpler to use. This low level API is provided for those applications that
 *  need more direct access to the unique feature set of the OpenEXR file format
 *  that might otherwise be obscured by more general APIs that must service
 *  numerous other compressed image formats. If there is sufficient interest,
 *  the feature set may be expanded in the future.
 *
 *  While this library is intended to be file format compatible with the ILM / Academy
 *  Software Foundation OpenEXR implementation (www.openexr.com), it is not API
 *  compatible with that work. Software written to use those interfaces will need to
 *  be modified in order to use these interfaces instead. No effort has been made to
 *  ensure that all operations in other OpenEXR implementations have a counterpart here.
 *
 *  Except for the _rgba interfaces, AppleEXR does not provide pixel type conversion services.
 *  Image data is provided as stored (after decompression) at the same precision found in the
 *  file. Most OpenEXR content is extended range linear gamma and care must be exercised when
 *  converting to other formats, especially traditional unorm based formats to avoid losing
 *  highlights. Please see vImageConvert_AnyToAny() or MPSImageConversion.
 *
 *  Feature requests and bug reports may be made through to the AppleEXR Radar component.
 *  See https://feedbackassistant.apple.com.
 *
 *  Overview of interfaces:
 *    Library versioning:
 *      axr_get_version                     major.minor.bugfix
 *
 *    Informational / Utility:
 *      axr_channel_type_get_name           const char[] representation of enum axr_channel_type_t
 *      axr_compression_get_name            const char[] representation of enum axr_compression_t
 *      axr_error_get_name                  const char[] representation of enum axr_error_t
 *      axr_flags_get_name                  const char[] representation of enum axr_flags_t
 *      axr_line_order_get_name             const char[] representation of enum axr_line_order_t
 *      axr_property_flags_get_name         const char[] representation of enum axr_property_flags_t
 *      axr_type_get_name                   const char[] representation of enum axr_type_t
 *      axr_type_get_size                   sizeof type named by axr_type_t in bytes
 *
 *    File Data container creation / metadata
 *      axr_introspect_data                 Quick check to see if file is EXR
 *      axr_data_create                     The axr_data_t is a container that holds a EXR file loaded into memory
 *      axr_data_get_channel_count          Report how many channels are in a part
 *      axr_data_get_channel_info           Report metadata about a channel
 *      axr_data_get_layer_count            Number of groups of channels with the same name prefix
 *      axr_data_get_layer_info             Metadata about a group of channels with the same name prefix
 *      axr_data_get_level_count            Number of mip- or ripmap levels for the image
 *      axr_data_get_level_size             Size in pixels of a layer level
 *      axr_data_get_part_count             Report how many parts are in a v2 OpenEXR file. Otherwise 1.
 *      axr_data_get_part_info              Report metadata for a part
 *      axr_data_get_property_count         Number of raw metadata items in a part
 *      axr_data_get_property               Raw meta data access for a part. Provides access to all attributes.
 *
 *    Decoding
 *      axr_decoder_create                  create a new decoder for a axr_data_t.
 *      axr_decoder_create_rgba             Convenience method to create a decoder for RGBA output.
 *                                              See also: axr_decoder_read_rgba_pixels, axr_decoder_create_rgba_colorspace.
 *      axr_decoder_copy                    create a copy of a axr_data_t
 *      axr_decoder_append_channel          Add a new channel to the list to be decoded
 *      axr_decoder_append_constant_value_channel   Add a new channel to the output. The channel doesn't occur in the EXR file. A constant value (e.g. 1.0) will be used.
 *      axr_decoder_clear_channel_list      Delete all of the channels in the list of channels to be decoded
 *      axr_decoder_create_colorspace       Create a CGColorSpaceRef to represent the colorspace of the decoded channels
 *      axr_decoder_create_rgba_colorspace  Like axr_decoder_create_colorspace, but intended for use with axr_decoder_read_rgba_pixels
 *      axr_decoder_get_channel_count       Get the number of channels to be decoded. (See also axr_decoder_append_channel)
 *      axr_decoder_get_channel_info        Get metadata about the decoded channel
 *      axr_decoder_get_data                Get the axr_data_t that contains the data for the decoder
 *      axr_decoder_get_info                Get the current decoder settings
 *      axr_decoder_translate_channel_index Get the axr_data_t part index for the channels to be decoded
 *      axr_decoder_set_subregion           Set the X,Y spatial subregion of the image to be decoded
 *      axr_decoder_read_pixels             Read the pixel data as either 1, 2 or 4 interleaved channels
 *      axr_decoder_read_rgba_pixels        Read the pixel data as 4 channel RGBA content, even if the channels are grayscale, XYZ, or YCbCr (YCC, YUV)
 *      axr_decoder_read_planes             Read the pixel data as separate planes
 *
 *   Encoding
 *      axr_encoder_append_part             Add a part to a axr_encoder_t
 *      axr_encoder_compress                Compress the parts encapsulated by axr_encoder_t to serial file
 *      axr_encoder_create                  Create a axr_encoder_t object. This can be used to compress a OpenEXR image
 *      axr_part_append_attribute           Add an attribute to a part
 *      axr_part_append_channels            add 1,2 or 4 channels to a part
 *      axr_part_create                     create a new part
 *      axr_part_set_chromaticities         Configure the color space of the part
 *      axr_part_set_preview                Add a flat RGBA unorm8 preview image to the part
 *      axr_part_create_colorspace          Get a CGColorSpaceRef for the part.  Useful for converting pixels to values suitable for encoding with OpenEXR
 *
 *
 *  Conceptual organization of files:
 *      The hierarchy of subdivision of a EXR file, is as follows:  EXR file > part > layer > channel > level
 *      At each subdivision, there is a many to one relationship with the subdivision above it. Each file may
 *      have many parts. Each part may have many layers. Each layer may have many channels, etc. A rough
 *      description of what each is follows:
 *
 *          EXR file:   A serialized file container that starts with little endian int32_t 20000630
 *          part:       A set of layers with shared metadata like a unified set of coordinates, color space,
 *                      and time stamp. Only channels in the same part can be decoded together.
 *                      Since generally all channels in a part must be at least partially decoded to read
 *                      any channel in the part, the part serves to provide some file segmentation
 *                      to reduce the amount of unnecessary work in files that contain a lot of data. Generally
 *                      only channels read together should appear in the same part. Except where adjusted by
 *                      a x- or y- downsampling rate (e.g. YCbCr 420), all channels in a part are the same height and width.
 *          layer:      A set of channels with the same name prefix, e.g. "camera1.R", "camera1.G", "camera1.B".  These
 *                      typically belong to the same conceptual data set, such as a RGB image shot from a particular
 *                      position in space. Do not mix unrelated data into the same layer.
 *          channel:    A sequence of float, uint32_t or half precision floating point values that encode a single
 *                      data channel in a file such as the alpha information for a layer. Channels may be anything,
 *                      and may include color channels, alpha channels, zbuffer channels, raw data, etc.
 *          level:      A channel may be available in a series of variants that have been down sampled by
 *                      a factor of a power of two. The full size image is available as level 0.  The downsampled
 *                      variants grow progressively smaller until the image is 1x1 in size.  Each such level is a rectangular
 *                      data plane belonging to a single channel.
 *
 *      Channels that have the same part and mip-/ripmap level can be read together as a interleaved (chunky) image,
 *      provided that their x/y downsampling rate is also the same. This is more efficient than reading data planes one at a time.
 *
 *  C++ sample code for quickly obtaining RGBA images with AppleEXR:
 *
 *      void * fileData = ...;  // pointer to contents of an EXR file in memory
 *      size_t fileSize = ...;  // number of bytes in the file
 *      axr_error_t axrErr = axr_error_success;
 *
 *      // create a axr_data_t to represent the file. See also axr_introspect_data()
 *      axr_data_t axrData = axr_data_create( fileData, fileSize, &axrErr); 
 *      if( NULL == axrData )
 *          return axrErr;
 *
 *      // Make a decoder for the part of the file you want to read
 *      //    default parameters will read the default layer from the first part
 *      axr_decoder_t d = axr_decoder_create_rgba(axrData);
 *      if( NULL == d )
 *      {
 *          os_release(axrData);
 *          return axr_error_insufficient_memory;
 *      }
 *
 *      // Figure out how big to make the buffer to receive the pixels
 *      axr_type_t sampleType = axr_decoder_get_channel_info(d, 0).sampleType;
 *      uint32_t channelSize = (uint32_t) axr_type_get_size(sampleType);
 *      uint32_t pixelSize = 4 * channelSize;       // RGBA = 4 channels
 *      axr_decoder_info_t info = axr_decoder_get_info(d);
 *      size_t rowBytes = info.subregion.size.width * pixelSize;
 *      void * buf = malloc( info.subregion.size.height * rowBytes );
 *             // ...handle malloc failure ...
 *
 *      // read the desired content from the file into buf as RGBA pixels
 *      if( axr_error_success == (axrErr = axr_decoder_read_rgba_pixels( d, buf, rowBytes )))
 *      {
 *          // Do something with the pixels here
 *      }
 *
 *      // Clean up. Note that if Automatic Reference Counting (ARC) is on,
 *      // this part isn't necessary
 *      os_release(d);
 *      os_release(axrData);
 *
 *      free(buf);      // ARC doesn't clean this up
 *
 *      return axrErr;
 */

#ifndef AppleEXR_h
#define AppleEXR_h  1

#include <os/object.h>
#include <math.h>
#include <stdint.h>
#include <unistd.h>
#include <stdbool.h>
#include <Availability.h>

/* As a C++ convenience, some argments can have default values. */
#if defined __cplusplus && ! defined(DOXYGEN)
    extern "C" {
#   define _AXR_DEFAULT(_a)            = _a
#   ifndef _AXR_DEFAULT_FLAGS
#       if DEBUG
#          define _AXR_DEFAULT_FLAGS   = axr_flags_print_debug_info
#       else
#          define _AXR_DEFAULT_FLAGS   = axr_flags_default
#       endif
#   endif
#else
#   define _AXR_DEFAULT(_a)
#   ifndef _AXR_DEFAULT_FLAGS
#       define _AXR_DEFAULT_FLAGS
#   endif
#endif
    
#ifndef AXR_RETURNS_RETAINED
#   define AXR_RETURNS_RETAINED         OS_OBJECT_RETURNS_RETAINED
#   define AXR_RETURNS_INNER_POINTER   
#endif


/*  Availability information for AppleEXR (Apple OpenEXR)
 *  version 2:
 *      Version 1 (and 2) requires that your application provide
 *      image data in full tile sized chunks even when the image
 *      was not a multiple of the tile size when compressing EXR
 *      images. It did not however provide a way to find out what
 *      the tile size is.  To remedy this problem axr_part_get_tile_size()
 *      is added.
 *  version 1:
 *      Initial feature set.
 *         Decodes OpenEXR 1.7 and 2.0 images
 *      Unsupported features:
 *         Deep Tile and Deep scanline images
 *         features introduced after file format 2.0
 */

#ifdef AXR_SKIP_AVAILABILITY
#   define AXR_AVAILABILITY_v1
#   define AXR_ENUM_AVAILABILITY_v1
#   define AXR_AVAILABILITY_v2
#   define AXR_ENUM_AVAILABILITY_v2
#   define AXR_AVAILABILITY_v3
#   define AXR_ENUM_AVAILABILITY_v3
#else
#   define AXR_AVAILABILITY_v1          __API_AVAILABLE(macos(11.0), ios(14.0), tvos(14.0), watchos(7.0))
#   define AXR_ENUM_AVAILABILITY_v1     __API_AVAILABLE(macos(11.0), ios(14.0), tvos(14.0), watchos(7.0))
#   define AXR_AVAILABILITY_v2          __API_AVAILABLE(macos(12.3),  ios(15.3), tvos(15.3), watchos(8.3))
#   define AXR_ENUM_AVAILABILITY_v2     __API_AVAILABLE(macos(12.3),  ios(15.3), tvos(15.3), watchos(8.3))
#   define AXR_AVAILABILITY_v3          __API_AVAILABLE(macos(13.0),  ios(16.1), tvos(16.1), watchos(9.1))
#   define AXR_ENUM_AVAILABILITY_v3     __API_AVAILABLE(macos(13.0),  ios(16.1), tvos(16.1), watchos(9.1))
#endif

    
/*! @abstract   Get AppleEXR dylib.current_version
 *  @return     A uint32_t encoding library version as major.minor.bugfix  (16.8.8)
 *              The major field of the build version corresponds with the compatibility
 *              version (e.g. AXR_AVAILABILITY_v1) */
uint32_t  axr_get_version(void)  AXR_AVAILABILITY_v1;

    
#pragma mark constants

/*! @enum       axr_error_t
 *  @abstract   Error codes for axr_data_t
 *  @constant   axr_error_success                             The operation completed successfully
 *  @constant   axr_error_invalid_parameter              One of the parameters was incorrect.
 *                                             Try axr_flags_print_debug_info for more information
 *  @constant   axr_error_unknown_file_type             This is not a recognized OpenEXR file variant
 *  @constant   axr_error_unsupported_EXR_type     This OpenEXR file variant is not supported, or for serialized data, file too new
 *  @constant   axr_error_data_truncated                   The file is smaller than it should be.
 *  @constant   axr_error_insufficient_memory           Malloc failure
 *  @constant   axr_error_data_corrupted                   Data within the file is internally inconsistent, or illegal values found
 *  @constant   axr_error_null_data                             The axr_data_t parameter may not be NULL
 *  @constant   axr_error_compression_method_not_supported  This version of AppleEXR does not support the compression options  provided or found within the file
 *  @constant   axr_error_reserved_attribute_name    The attribute is already saved as a standard part of saving the axr_part_t. It can not be added again.
 *  @constant   axr_error_encoding_suspended         Your application's axr_data_provider_read_t returned 0.
 *  @constant   axr_error_encoding_insufficient_data Your application's axr_data_provider_read_t returned -1.
 *  @constant   axr_error_encoding_read_error           Your application's axr_data_provider_read_t returned -2.
 *  @constant   axr_error_encoding_write_error          The internal compressor failed.  Pass axr_flags_print_debug_info to see more.  This shouldn't happen. File a Radar.
 *  @constant   axr_error_image_too_large                 The image has tile sizes larger than 4 GB. Possibly, the tiles attribute is corrupted in the file, if decode.
 *
 *  @memberof   axr_data_t
 */
#if defined(DOXYGEN)
typedef enum axr_error_t
{
    axr_error_success                       AXR_ENUM_AVAILABILITY_v1  =     0,
    
    /* Please see axr_error_get_name below before needlessly duplicating this list */

    /* All failures are negative */
    axr_error_invalid_parameter             AXR_ENUM_AVAILABILITY_v1  =    -1,
    axr_error_unknown_file_type             AXR_ENUM_AVAILABILITY_v1  =    -2,
    axr_error_unsupported_EXR_type          AXR_ENUM_AVAILABILITY_v1  =    -3,
    axr_error_data_truncated                AXR_ENUM_AVAILABILITY_v1  =    -4,
    axr_error_insufficient_memory           AXR_ENUM_AVAILABILITY_v1  =    -5,
    axr_error_data_corrupted                AXR_ENUM_AVAILABILITY_v1  =    -6,
    axr_error_null_data                     AXR_ENUM_AVAILABILITY_v1  =    -7,
    axr_error_compression_method_not_supported AXR_ENUM_AVAILABILITY_v1  = -8,
    axr_error_reserved_attribute_name       AXR_ENUM_AVAILABILITY_v1  =    -9,
    axr_error_encoding_suspended            AXR_ENUM_AVAILABILITY_v1  =   -10,
    axr_error_encoding_insufficient_data    AXR_ENUM_AVAILABILITY_v1  =   -11,
    axr_error_encoding_read_error           AXR_ENUM_AVAILABILITY_v1  =   -12,
    axr_error_encoding_write_error          AXR_ENUM_AVAILABILITY_v1  =   -13,
    axr_error_image_too_large               AXR_ENUM_AVAILABILITY_v2  =   -14,

    /* other error codes are reserved */
};
#else
OS_ENUM( axr_error, long,
    axr_error_success                       AXR_ENUM_AVAILABILITY_v1  =     0,
    
    /* Please see axr_error_get_name below before needlessly duplicating this list */
    
    /* All failures are negative */
    axr_error_invalid_parameter             AXR_ENUM_AVAILABILITY_v1  =    -1,
    axr_error_unknown_file_type             AXR_ENUM_AVAILABILITY_v1  =    -2,
    axr_error_unsupported_EXR_type          AXR_ENUM_AVAILABILITY_v1  =    -3,
    axr_error_data_truncated                AXR_ENUM_AVAILABILITY_v1  =    -4,
    axr_error_insufficient_memory           AXR_ENUM_AVAILABILITY_v1  =    -5,
    axr_error_data_corrupted                AXR_ENUM_AVAILABILITY_v1  =    -6,
    axr_error_null_data                     AXR_ENUM_AVAILABILITY_v1  =    -7,
    axr_error_compression_method_not_supported AXR_ENUM_AVAILABILITY_v1  = -8,
    axr_error_reserved_attribute_name       AXR_ENUM_AVAILABILITY_v1  =    -9,
    axr_error_encoding_suspended            AXR_ENUM_AVAILABILITY_v1  =   -10,
    axr_error_encoding_insufficient_data    AXR_ENUM_AVAILABILITY_v1  =   -11,
    axr_error_encoding_read_error           AXR_ENUM_AVAILABILITY_v1  =   -12,
    axr_error_encoding_write_error          AXR_ENUM_AVAILABILITY_v1  =   -13,
    axr_error_image_too_large               AXR_ENUM_AVAILABILITY_v2  =   -14,

    /* other error codes are reserved */
);
#endif /* DOXYGEN */
    
/*! @function axr_error_get_name
 *  @abstract Get a human readable string for a axr_error_t
 *  @memberof axr_error_t */
const char * __nonnull axr_error_get_name( axr_error_t error ) AXR_AVAILABILITY_v1;

/*! @enum       axr_flags_t
 *  @discussion These are flags to control aspects of operation of various AppleEXR interfaces. If multiple flags are needed, bitwise-OR them together.
 *
 *              Skip Parameter Checking
 *                  Some methods may do extensive parameter checking. If this is a performance problem, you may try axr_flags_skip_parameter_checking
 *                  to disable this activity.  Note that as a consequence, the library may not catch or report usage that leads to undefined behavior and
 *                  undefined behavior will be the result in such cases.
 *
 *              Print Debug Info:
 *                  if something goes wrong, or there is something that AppleEXR wants to nag you about, human readable text will be
 *                  sent to the Apple System Logger to help explain the problem when axr_flags_print_debug_info is on. These messages
 *                  should show up on your debugger console. See also behavior of _AXR_DEFAULT_FLAGS for C++ automatic parameters.
 *                  Defining AXR_PRINT_DEBUG_INFO in the environment is another way to turn this on.
 *
 *              Disable multithreading:
 *                 By default, expensive routines such as axr_decoder_read_pixels and axr_decoder_read_planes are likely to
 *                 use separate threads to read tiles from the EXR image so as to better take advantage of multiple processors on
 *                 your system.  If your application is reading multiple EXR files in parallel or otherwise is likely to be executing
 *                 multithreaded workloads at the time, it may not be advantageous for AppleEXR to spawn yet more GCD tasks to
 *                 do its work, since the machine is already saturated. It would just add unnecessary synchronization overhead and
 *                 pollute more of the caches.  In such cases, you may pass axr_flags_disable_multithreading to force serial operation
 *                 on the calling thread.
 *
 *              Disable automatic line order handling:
 *                 OpenEXR -- "When a scan-line based image is written, application code must set the lineOrder attribute in the header to INCREASING_Y
 *                           order (top scan line first) or DECREASING_Y order (bottom scan line first). Application code must then write the scan lines
 *                           in the specified order"   https://www.openexr.com/documentation/TechnicalIntroduction.pdf
 *
 *                 We interpret this to mean that if DECREASING_Y line order is set, you are responsible for vertically flipping the image yourself when reading
 *                 or writing the image when using ILM OpenEXR. AppleEXR, however, will by default attempt to automatically flip the image for you when it
 *                 detects axr_line_order_decreasing_y.    While better for users, this approach might be undesired if we misunderstand the situation, encounter
 *                 malformed images, or your application, having recently been ported from ILM OpenEXR, is not expecting this behavior.  Your application
 *                 may use axr_flags_disable_automatic_line_order_handling to cause the scanlines to be read or written in the order they appear in the file.
 *                 AppleEXR will still attempt to reorder tiles in the case that the order is axr_line_order_random_y.
 *
 *                 In the event your application needs to reverse the order of scanlines itself, this is most easily done by changing the pointer to your pixel buffer to point
 *                 to the bottom left corner of the region of interest that you wish to read/write, and negate the rowBytes parameter.  For example, when working
 *                 with axr_decoder_read_planes, you can adjust each plane as follows:
 *
 *                  // defeat C operator+ increment by sizeof(ptr[0]).  Increment by byte instead.  Defeat asan assertion for pointer overflow.
 *                  #define  ADVANCE_PTR( _ptr, _bytes )   ((__typeof__(_ptr)) (((uintptr_t) (_ptr)) + (_bytes)) )
 *
 *                  if( axr_part_info_t.lineOrder == axr_line_order_decreasing_y)
 *                  {
 *                      // move data to point to bottom left of image instead
 *                      axr_pixel_data_t.data = ADVANCE_PTR( axr_pixel_data_t.data,  (roiHeight - 1) * axr_pixel_data_t.rowBytes);      // roiHeight: see axr_decoder_set_subregion(... ySize). For entire image, see axr_data_get_level_size().height.
 *
 *                      //walk bottom to top through image
 *                      axr_pixel_data_t.rowBytes = -axr_pixel_data_t.rowBytes
 *                  }
 *
 *
 *  @abstract   Flags to influence the operation of Apple EXR
 *  @constant   axr_flags_default                                                       Default operation
 *  @constant   axr_flags_skip_parameter_checking                         Assume parameters are sane and proceed blindly forward without parameter error checking.
 *  @constant   axr_flags_print_debug_info                                       Print additional debug info to stderr in case of error
 *  @constant   axr_flags_disable_multithreading                              Run entirely in the calling thread. May be multithreaded otherwise.
 *  @constant   axr_flags_disable_automatic_line_order_handling   Turn off automatic image flipping for axr_line_order_decreasing_y cases  (See discussion)
 *
 *  @memberof   axr_data_t
 */
#if defined(DOXYGEN)
typedef enum axr_flags_t
{
    axr_flags_default                       AXR_ENUM_AVAILABILITY_v1  =   0,
    axr_flags_skip_parameter_checking       AXR_ENUM_AVAILABILITY_v1  =   1U << 0,          //*<  Do not spend time checking input parameters */
    axr_flags_print_debug_info              AXR_ENUM_AVAILABILITY_v1  =   1U << 1,          //*<  Print additional debug info to stderr */
    axr_flags_disable_multithreading        AXR_ENUM_AVAILABILITY_v1  =   1U << 2,          //*<  Run entirely in this thread */
    axr_flags_disable_automatic_line_order_handling AXR_ENUM_AVAILABILITY_v1  =   1U << 3,  //*<  Turn off image flipping for axr_line_order_decreasing_y */

    /* other bits are reserved */
};
#else
OS_ENUM( axr_flags, uint64_t,
    axr_flags_default                               AXR_ENUM_AVAILABILITY_v1  =   0,
    axr_flags_skip_parameter_checking               AXR_ENUM_AVAILABILITY_v1  =   1U << 0,    //*<  Do not spend time checking input parameters */
    axr_flags_print_debug_info                      AXR_ENUM_AVAILABILITY_v1  =   1U << 1,    //*<  Print additional debug info to stderr */
    axr_flags_disable_multithreading                AXR_ENUM_AVAILABILITY_v1  =   1U << 2,    //*<  Run entirely in this thread */
    axr_flags_disable_automatic_line_order_handling AXR_ENUM_AVAILABILITY_v1  =   1U << 3,    //*<  Turn off image flipping for axr_line_order_decreasing_y */

    /* other bits are reserved */
);
#endif
    
/*! @function   axr_flags_get_name
 *  @abstract   Get a user readable string that describes the contents of  a flags bitfield
 *  @param      buf     The memory location to write the string
 *  @param      len     The size of the memory location
 *  @param      flags   The flags value to print
 *  @return     The number of bytes consumed in buf
 *  @memberof  axr_flags_t*/
size_t axr_flags_get_name( char * __nonnull buf,
                           size_t len,
                           axr_flags_t flags) AXR_AVAILABILITY_v1;


/*! @enum       axr_type_t
 *  @abstract   The data type of each individual sample, e.g. half, float or uint32_t
 *  @constant   axr_type_uint  uint32_t
 *  @constant   axr_type_half  IEEE-754-2008 16-bit floating-point  (1 bit sign, 5 bit exponent (bias 15), 10 bit significand)
 *  @constant   axr_type_float  IEEE-754 32-bit floating-point (e.g. "float" in C)
 *
 *  @memberof   axr_data_t
 */
#if defined(DOXYGEN)
typedef enum axr_type_t
{
    axr_type_uint                    AXR_ENUM_AVAILABILITY_v1  =   0,          //*<  uint32_t   */
    axr_type_half                    AXR_ENUM_AVAILABILITY_v1  =   1,          //*<  __fp16     */
    axr_type_float                   AXR_ENUM_AVAILABILITY_v1  =   2,          //*<  float      */
    
    /* other values are reserved */
};
#else
OS_ENUM( axr_type, uint32_t,
    axr_type_uint                    AXR_ENUM_AVAILABILITY_v1  =   0,          //*<  uint32_t   */
    axr_type_half                    AXR_ENUM_AVAILABILITY_v1  =   1,          //*<  __fp16     */
    axr_type_float                   AXR_ENUM_AVAILABILITY_v1  =   2,          //*<  float      */

    /* other bits are reserved */
);
#endif

/*! @function axr_type_get_name
    @abstract Return a human readable name for the type
    @memberof axr_type_t */
const char * __nonnull axr_type_get_name( axr_type_t type ) AXR_AVAILABILITY_v1;


/*! @enum       axr_level_mode_t
 *  @abstract   Indicates what sort of lower resolution versions are available for anisotropic filtering
 *  @constant   axr_level_mode_one_level      A simple flat image
 *  @constant   axr_level_mode_mipmap        Images are stored as a series of power-of-two downsampled images
 *  @constant   axr_level_mode_ripmap          Images are stored as a series of power-of-two downsampled images, with x and y downsampling rates varying independently
 *  @constant   axr_level_mode_round_down When downsampling, round the size of odd dimensions down
 *  @constant   axr_level_mode_round_up      When downsampling, round the size of odd dimensions up
 *
 *  @memberof   axr_type_t
 */
#if defined(DOXYGEN)
typedef enum axr_level_mode_t
{
    axr_level_mode_one_level         AXR_ENUM_AVAILABILITY_v1  =   0U,         //*<  flat image */
    axr_level_mode_mipmap            AXR_ENUM_AVAILABILITY_v1  =   1U,         //*<  mipmap */
    axr_level_mode_ripmap            AXR_ENUM_AVAILABILITY_v1  =   2U,         //*<  ripmap */
    axr_level_mode_format_mask       AXR_ENUM_AVAILABILITY_v1  = 0xfU,         //*<  bits encoding for level mode */

    axr_level_mode_round_down        AXR_ENUM_AVAILABILITY_v1  =   0U << 4,    //*<  round odd dimensions down when downsampling */
    axr_level_mode_round_up          AXR_ENUM_AVAILABILITY_v1  =   1U << 4,    //*<  round odd dimensions up when downsampling */
    axr_level_mode_round_mask        AXR_ENUM_AVAILABILITY_v1  = 0xfU << 4,    //*<  bits encoding for level rounding mode */
    
    /* other values are reserved */
};
#else
OS_ENUM( axr_level_mode, uint32_t,
    axr_level_mode_one_level         AXR_ENUM_AVAILABILITY_v1  =   0U,         //*<  flat image */
    axr_level_mode_mipmap            AXR_ENUM_AVAILABILITY_v1  =   1U,         //*<  mipmap */
    axr_level_mode_ripmap            AXR_ENUM_AVAILABILITY_v1  =   2U,         //*<  ripmap */
    axr_level_mode_format_mask       AXR_ENUM_AVAILABILITY_v1  = 0xfU,         //*<  bits encoding for level mode */

    axr_level_mode_round_down        AXR_ENUM_AVAILABILITY_v1  =   0U << 4,    //*<  round odd dimensions down when downsampling */
    axr_level_mode_round_up          AXR_ENUM_AVAILABILITY_v1  =   1U << 4,    //*<  round odd dimensions up when downsampling */
    axr_level_mode_round_mask        AXR_ENUM_AVAILABILITY_v1  = 0xfU << 4,    //*<  bits encoding for level rounding mode */

    /* other bits are reserved */
);
#endif

/*! @function axr_level_mode_get_name
    @abstract Return a human readable name for the axr_level_mode
    @memberof axr_level_mode_t */
const char * __nonnull axr_level_mode_get_name( axr_level_mode_t mode ) AXR_AVAILABILITY_v1;


/*! @abstract Return the number of bytes needed to store the axr_type_t */
size_t axr_type_get_size( axr_type_t )  AXR_AVAILABILITY_v1;

/*! @enum       axr_channel_type_t
 *  @abstract   A color channel classification,  e.g.  red, green or blue
 *  @constant   axr_channel_type_unknown      unknown channel type
 *  @constant   axr_channel_type_red      red
 *  @constant   axr_channel_type_green  green
 *  @constant   axr_channel_type_blue    blue
 *  @constant   axr_channel_type_alpha        a shared alpha channel
 *  @constant   axr_channel_type_alpha_red an alpha channel specific to the red channel
 *  @constant   axr_channel_type_alpha_green an alpha channel specific to the green channel
 *  @constant   axr_channel_type_alpha_blue an alpha channel specific to the blue channel
 *  @constant   axr_channel_type_alpha_cb  The blue correction channel in YCbCr
 *  @constant   axr_channel_type_alpha_cr  The red correction channel in YCbCr
 *  @constant   axr_channel_type_alpha_y    The luminance correction channel in YCbCr, grayscale or Y in XYZ
 *  @constant   axr_channel_type_alpha_x    X in XYZ
 *  @constant   axr_channel_type_alpha_z    Z in XYZ
 *  @constant   axr_channel_type_distance  Distance of the front of a sample from the viewer.
 *  @constant   axr_channel_type_distance_back    Distance of the back of a sample from the viewer.
 *  @constant   axr_channel_type_id                      a numerical identifier for an object
 *  @memberof   axr_type_t
 */
#if defined(DOXYGEN)
typedef enum axr_channel_type_t
{
    axr_channel_type_unknown                AXR_ENUM_AVAILABILITY_v1  =   0,
    axr_channel_type_red                    AXR_ENUM_AVAILABILITY_v1,
    axr_channel_type_green                  AXR_ENUM_AVAILABILITY_v1,
    axr_channel_type_blue                   AXR_ENUM_AVAILABILITY_v1,
    
    axr_channel_type_alpha                  AXR_ENUM_AVAILABILITY_v1,      /// a shared alpha channel
    axr_channel_type_alpha_red              AXR_ENUM_AVAILABILITY_v1,      /// a alpha channel specific to the red channel
    axr_channel_type_alpha_green            AXR_ENUM_AVAILABILITY_v1,      /// a alpha channel specific to the green channel
    axr_channel_type_alpha_blue             AXR_ENUM_AVAILABILITY_v1,      /// a alpha channel specific to the blue channel

    axr_channel_type_cb                     AXR_ENUM_AVAILABILITY_v1,      /// YCbCr blue correction
    axr_channel_type_cr                     AXR_ENUM_AVAILABILITY_v1,      /// YCbCr red correction
    axr_channel_type_y                      AXR_ENUM_AVAILABILITY_v1,      /// Grayscale or luminance 
    axr_channel_type_x                      AXR_ENUM_AVAILABILITY_v1,      /// XYZ X channel
    axr_channel_type_z                      AXR_ENUM_AVAILABILITY_v1,      /// XYZ Z channel
    
    axr_channel_type_distance               AXR_ENUM_AVAILABILITY_v1,      /// Distance of the front of a sample from the viewer.
    axr_channel_type_distance_back          AXR_ENUM_AVAILABILITY_v1,      /// Distance of the back of a sample from the viewer.
    axr_channel_type_id                     AXR_ENUM_AVAILABILITY_v1,      /// a numerical identifier for an object

    /* other values are reserved */
};
#else
OS_ENUM( axr_channel_type, uint32_t,
    axr_channel_type_unknown                AXR_ENUM_AVAILABILITY_v1  =   0,
    axr_channel_type_red                    AXR_ENUM_AVAILABILITY_v1,
    axr_channel_type_green                  AXR_ENUM_AVAILABILITY_v1,
    axr_channel_type_blue                   AXR_ENUM_AVAILABILITY_v1,
    
    axr_channel_type_alpha                  AXR_ENUM_AVAILABILITY_v1,      /// a shared alpha channel
    axr_channel_type_alpha_red              AXR_ENUM_AVAILABILITY_v1,      /// a alpha channel specific to the red channel
    axr_channel_type_alpha_green            AXR_ENUM_AVAILABILITY_v1,      /// a alpha channel specific to the green channel
    axr_channel_type_alpha_blue             AXR_ENUM_AVAILABILITY_v1,      /// a alpha channel specific to the blue channel

    axr_channel_type_cr                     AXR_ENUM_AVAILABILITY_v1,      /// YCbCr red correction
    axr_channel_type_cb                     AXR_ENUM_AVAILABILITY_v1,      /// YCbCr blue correction
    axr_channel_type_y                      AXR_ENUM_AVAILABILITY_v1,      /// Grayscale, luminance or Y in XYZ
    axr_channel_type_x                      AXR_ENUM_AVAILABILITY_v1,      /// XYZ X channel
    axr_channel_type_z                      AXR_ENUM_AVAILABILITY_v1,      /// XYZ Z channel
    
    axr_channel_type_distance               AXR_ENUM_AVAILABILITY_v1,      /// Distance of the front of a sample from the viewer.
    axr_channel_type_distance_back          AXR_ENUM_AVAILABILITY_v1,      /// Distance of the back of a sample from the viewer.
    axr_channel_type_id                     AXR_ENUM_AVAILABILITY_v1,      /// a numerical identifier for an object

    axr_channel_type_u                      AXR_ENUM_AVAILABILITY_v1,      /// U channel, from channel pair
    axr_channel_type_v                      AXR_ENUM_AVAILABILITY_v1,      /// V channel, from channel pair

    /* other values are reserved */
);
#endif
    
/*! @abstract Return a human readable string for a axr_channel_type_t value
 *  @memberof axr_channel_type_t */
const char * __nonnull axr_channel_type_get_name( axr_channel_type_t type ) AXR_AVAILABILITY_v1;
    
    
/*! @enum       axr_compression_t
 *  @abstract   Different compression methods used by EXR
 *  @constant   axr_compression_none   uncompressed
 *  @constant   axr_compression_rle       split into even and odd bytes and run length encoded. (l)ossless)
 *  @constant   axr_compression_zips    split into even and odd bytes and compressed with zlib  (lossless)
 *  @constant   axr_compression_zip      split into even and odd bytes and compressed with zlib in larger tiles (lossless)
 *  @constant   axr_compression_piz      wavelet transform, ordinal transform, Huffman encoded (lossy?)
 *  @constant   axr_compression_pxr24  each channel byte split into a separate stream and compressed with zlib. last 8 LSBs of float32s rounded off. lossless for fp16 and uint32. lossy for fp32
 *  @constant   axr_compression_b44     each 4x4 block is stored as difference vs. top left pixel with some truncation  (lossy) Excellent for random access into image subregions
 *  @constant   axr_compression_b44a   Same as B44 but an additional smaller encoding for constant color blocks/alpha is added  (lossy)  Not as good for random access into image
 *
 *  @memberof   axr_data_t
 */
#if  defined(DOXYGEN)
typedef enum axr_compression_t
{
    axr_compression_none        AXR_ENUM_AVAILABILITY_v1  =   0,    //*<  NO_COMPRESSION
    axr_compression_rle         AXR_ENUM_AVAILABILITY_v1  =   1,    //*<  RLE_COMPRESSION
    axr_compression_zips        AXR_ENUM_AVAILABILITY_v1  =   2,    //*<  ZIPS_COMPRESSION
    axr_compression_zip         AXR_ENUM_AVAILABILITY_v1  =   3,    //*<  ZIP_COMPRESSION
    axr_compression_piz         AXR_ENUM_AVAILABILITY_v1  =   4,    //*<  PIZ_COMPRESSION
    axr_compression_pxr24       AXR_ENUM_AVAILABILITY_v1  =   5,    //*<  PXR24_COMPRESSION
    axr_compression_b44         AXR_ENUM_AVAILABILITY_v1  =   6,    //*<  B44_COMPRESSION
    axr_compression_b44a        AXR_ENUM_AVAILABILITY_v1  =   7,    //*<  B44A_COMPRESSION

    /* other values are reserved */
};
#else
OS_ENUM( axr_compression, uint32_t,
    axr_compression_none        AXR_ENUM_AVAILABILITY_v1  =   0,    //*<  NO_COMPRESSION
    axr_compression_rle         AXR_ENUM_AVAILABILITY_v1  =   1,    //*<  RLE_COMPRESSION
    axr_compression_zips        AXR_ENUM_AVAILABILITY_v1  =   2,    //*<  ZIPS_COMPRESSION
    axr_compression_zip         AXR_ENUM_AVAILABILITY_v1  =   3,    //*<  ZIP_COMPRESSION
    axr_compression_piz         AXR_ENUM_AVAILABILITY_v1  =   4,    //*<  PIZ_COMPRESSION
    axr_compression_pxr24       AXR_ENUM_AVAILABILITY_v1  =   5,    //*<  PXR24_COMPRESSION
    axr_compression_b44         AXR_ENUM_AVAILABILITY_v1  =   6,    //*<  B44_COMPRESSION
    axr_compression_b44a        AXR_ENUM_AVAILABILITY_v1  =   7,    //*<  B44A_COMPRESSION

    /* other values are reserved */
);
#endif

/*! @function axr_compression_get_name
    @abstract Return a human readable name for the type
    @memberof axr_compression_t */
const char * __nonnull axr_compression_get_name( axr_compression_t compressionType ) AXR_AVAILABILITY_v1;

    
/*! @enum       axr_line_order_t
 *  @abstract   Different orderings used used by EXR
 *  @discussion See https://www.openexr.com/documentation/TechnicalIntroduction.pdf, sections on scan lines and tiles.
 *              Application code must read / write the scanlines in the specified order.
 *  @constant   axr_line_order_increasing_y         top scanline first
 *  @constant   axr_line_order_decreasing_y       bottom scanline first
 *  @constant   axr_line_order_random_y            tiles are not sorted
 *
 *  @memberof   axr_data_t
 */
#if  defined(DOXYGEN)
typedef enum axr_line_order_t
{
    axr_line_order_increasing_y     AXR_ENUM_AVAILABILITY_v1  =   0,    //*<  top scanline first.     Read: Origin at top-left.
    axr_line_order_decreasing_y     AXR_ENUM_AVAILABILITY_v1  =   1,    //*<  bottom scanline first.  Read: Origin at bottom-left.
    axr_line_order_random_y         AXR_ENUM_AVAILABILITY_v1  =   2,    //*<  tiles are not sorted.   Read: Origin at top-left

    /* other values are reserved */
};
#else
OS_ENUM( axr_line_order, uint32_t,
    axr_line_order_increasing_y     AXR_ENUM_AVAILABILITY_v1  =   0,    //*<  top scanline first.  Origin at top-left.
    axr_line_order_decreasing_y     AXR_ENUM_AVAILABILITY_v1  =   1,    //*<  bottom scanline first.  Origin at bottom-left.
    axr_line_order_random_y         AXR_ENUM_AVAILABILITY_v1  =   2,    //*<  tiles are not sorted. Origin at top-left

    /* other values are reserved */
);
#endif

/*! @function axr_line_order_get_name
    @abstract Return a human readable name for the type
    @memberof axr_line_order_t */
const char * __nonnull axr_line_order_get_name( axr_line_order_t line_orderType ) AXR_AVAILABILITY_v1;
    
    
/*! @enum       axr_property_flags_t
 *  @abstract   Comments on AppleEXR file properties
 *  @discussion  Depending on circumstance, your application may want to walk the complete list of EXR properties or
 *               just those that AppleEXR did not handle for you. For example, when debugging the complete
 *               list, it is nice to avoid missing any information.  However, when printing out the metadata associated with the
 *               file it would be redundant to print out the contents of the axr_part_info_t and the complete set of
 *               properties because much of the information in the properties is already in the axr_part_info_t. Likewise,
 *               being able to quickly determine which properties that AppleEXR already handled for you is a useful shortcut.
 *               For this reason (and future expansion) the axr_property_flags are included to better help classify information.
 *  @constant   axr_property_unhandled   AppleEXR did not understand this property and did not attempt to handle it
 *  @constant   axr_property_flags_part_info_v1   Information in this property is redundant with the information presented in the axr_part_info_t
 *  @constant   axr_property_flags_channel_info_v1 Information in this property is redundant with the information presented in the axr_channel_info_t
 *
 *  @memberof   axr_property_flags_t
 */
#if defined(DOXYGEN)
typedef enum axr_property_flags_t
{
     axr_property_flags_unhandled           AXR_ENUM_AVAILABILITY_v1  =   0,  //*< Information is not understood or otherwise reported by AppleEXR
     axr_property_flags_part_info_v1        AXR_ENUM_AVAILABILITY_v1  =   1,  //*< Information is redundant with axr_part_info_t
     axr_property_flags_part_mask           AXR_ENUM_AVAILABILITY_v1  = 0xff,
         
     axr_property_flags_channel_info_v1     AXR_ENUM_AVAILABILITY_v1  = (1<<8),  //*< Information is redundant with axr_channel_info_t
     axr_property_flags_channel_mask        AXR_ENUM_AVAILABILITY_v1  = 0xff00,

    /* other bits are reserved */
};
#else
OS_ENUM( axr_property_flags, uint64_t,
    axr_property_flags_unhandled           AXR_ENUM_AVAILABILITY_v1  =   0,  //*< Information is not understood or otherwise reported by AppleEXR
    axr_property_flags_part_info_v1        AXR_ENUM_AVAILABILITY_v1  =   1,  //*< Information is redundant with axr_part_info_t
    axr_property_flags_part_mask           AXR_ENUM_AVAILABILITY_v1  = 0xff,
        
    axr_property_flags_channel_info_v1     AXR_ENUM_AVAILABILITY_v1  = (1<<8),  //*< Information is redundant with axr_channel_info_t
    axr_property_flags_channel_mask        AXR_ENUM_AVAILABILITY_v1  = 0xff00,

    /* other bits are reserved */
);
#endif

/*! @abstract Return a human readable name for the properties flags
 *  @memberof axr_property_flags_t  */
const char * __nonnull axr_property_flags_get_name( axr_property_flags_t flags) AXR_AVAILABILITY_v1;
    
typedef struct axr_box2i
{
    int32_t     xMin, xMax, yMin, yMax;
}axr_box2i_t;

#pragma mark file classification

/*! @function   axr_introspect_data
 *  @abstract   Examine if the indicated data to see if it is a OpenEXR file likely to be readable by this library
 *  @discussion Use to determine whether or not a byte stream represents a decodable by this library
 *  @param fileData     A raw pointer to the first byte of memory containing data from an OpenEXR file
 *  @param fileSize     The number of valid bytes in the region of memory pointed to by fileData
 *  @param flags            Flags to control the decoding process. Valid flags: axr_flags_print_debug_info
 *  @return          Returns axr_error_success if the file is a readable OpenEXR file.
 *                   Returns a non-zero error code  otherwise that describes the failure.  */
axr_error_t axr_introspect_data(
    const void * __nonnull fileData,
    size_t fileSize,
    axr_flags_t flags   _AXR_DEFAULT_FLAGS
) AXR_AVAILABILITY_v1;

/*! @abstract   Block to release or free the file data. It is called when the axr_data_t is destroyed  */
typedef void (^axr_data_destructor_t)( void * __nonnull fileData, size_t fileSize ) AXR_AVAILABILITY_v1;
        
#pragma mark axr_data_t
    
/*!
 *  @class      axr_data_t
 *  A OpenEXR file representation
 *  The axr_data_t defines an interface for an opaque object that can
 *  compress or decompress an OpenEXR image.
 *
 *  The axr_data_t is an os_object.  If your application is not using ARC,
 *  it should be released with os_release when you are done using it.
 */
#if OS_OBJECT_USE_OBJC && ! defined(DOXYGEN)
    OS_OBJECT_DECL(axr_data);
#else
    typedef struct axr_data *  axr_data_t;
#endif
        
/*! @function   axr_data_create
 *  @abstract   Create a axr_data_t object
 *  @param      fileBytes    A pointer to the raw bytes comprising the OpenEXR file
 *  @param      fileSize    The size of the block of memory pointed to by fileData
 *  @param      flags           Options to govern the operation of axr_data_create
 *  @param      outError    If non-NULL, an error code will be written here
 *  @param      dataDestructor    If non-NULL, this method is called when the axr_data_t
 *                              is destroyed. It is intended to help manage the release of the
 *                              memory referenced by fileData.
 *  @return     On success, a valid axr_data_t will be returned capable of decoding the
 *              file.  On failure, NULL is returned.
 *  @memberof   axr_data_t */
__nullable axr_data_t axr_data_create(
                                        const void * __nonnull fileBytes,
                                        size_t fileSize,
                                        axr_error_t * __nonnull outError,
                                        axr_flags_t flags _AXR_DEFAULT_FLAGS,
                                        __nullable axr_data_destructor_t dataDestructor _AXR_DEFAULT(NULL)
                                    ) AXR_RETURNS_RETAINED AXR_AVAILABILITY_v1;
    
/*! @abstract   Get the number of parts contained within the EXR file
 *  @memberof axr_data_ */
unsigned long axr_data_get_part_count( __nullable axr_data_t ) AXR_AVAILABILITY_v1;

/*! @abstract   A height and width of a rectangular region of pixels */
typedef struct{ uint32_t width, height;} axr_size_t;

/*! @abstract   A horizontal and vertical offset into a rectangular region of pixels */
typedef struct{ uint32_t x, y;} axr_origin_t;

/*! @abstract   A rectangular subregion  of a region of pixels*/
typedef struct{ axr_origin_t origin, axr_size_t;} axr_rect_t;

    
/*! @struct axr_part_info_t
    @abstract information about a EXR  part */
typedef struct
{
    axr_box2i_t                                    dataWindow;          /// actual size of encoded data
    axr_box2i_t                                    displayWindow;       /// region within dataWindow intended to be drawn. Note: individual mip- or rip-levels may be smaller.
    axr_size_t                                     tileSize;            /// size of natural encoding block. It is more efficient to structure decode in chunks of these.
    axr_compression_t                              compression;         /// compression format used for the part
    axr_line_order_t                               lineOrder;           /// line order for the part
    float                                          pixelAspectRatio;    /// EXR pixel aspect ratio
    struct{ float       x, y;}                     screenWindowCenter;  /// EXR screen window center
    float                                          screenWindowWidth;   /// EXR screen window width
    union
    {
        struct{
            struct{ float red, green, blue, white;} x;
            struct{ float red, green, blue, white;} y;
        }                                          chromaticities;   // Chromaticities for color space. Default: Rec. ITU-R BT.709-3  TF: Pixel data is by convention linear. See also axr_decoder_create_colorspace
    };
    struct{
        axr_size_t                  size;
        const uint8_t * __nullable  rgbaData;
    }                                              preview;     // A rgba unorm8 preview image. If not present, rgbaData is NULL.
    const char * __nullable                        name;        // owned by axr_data_t. Do not free.
    const char * __nullable                        view;        // owned by axr_data_t. Do not free.
    axr_level_mode_t                               levelMode;   // {single level, mipmap, ripmap} and rounding direction as bitfield
    float                                          xDensity;    // horizontal pixel density in Pixels Per Inch (PPI, DPI).  The vertical DPI is calculated using the pixelAspectRatio.
}axr_part_info_t;
typedef enum  { axr_part_info_current = 1 } axr_part_info_struct_version_t;

/*! @function   axr_data_get_part_info
 *  @abstract   Get characteristics for the part at index
 *  @discussion Some help understanding OpenEXR deep file formats may be found
 *              here: https://www.openexr.com/documentation/InterpretingDeepPixels.pdf
 *              and https://www.openexr.com/documentation/TechnicalIntroduction.pdf
 *  @param      fileData     The EXR file data
 *  @param      partIndex   An EXR file may have multiple parts. This is the index of the part.
 *  @param      version     Pass axr_part_info_current here
 *  @return     a valid axr_part_info_t describing the part
 *  @memberof   axr_data_t */
axr_part_info_t axr_data_get_part_info( __nullable axr_data_t fileData,
                                        unsigned long partIndex,
                                        axr_part_info_struct_version_t version _AXR_DEFAULT(axr_part_info_current))
                                        AXR_AVAILABILITY_v1;

/*! @abstract   Get the number of channels for the part at partIndex */
unsigned long axr_data_get_channel_count( __nullable axr_data_t fileData, unsigned long partIndex) AXR_AVAILABILITY_v1;
    
    
/*! @struct axr_layer_info_t
    @abstract information about a EXR image layer
    @discussion  Especially in EXR v1 files (before multipart files were introduced) it may be common to have
                 dozens of channels in a part. While it is possible that these all encode different
                 types of data channel for the same part, it may also be that the multiplicity of channels
                 encodes a series of sub-images packaged together in the same file. For example, for a stereo
                 view image, there may be a left eye view and a right eye view. By convention these channels
                 might have names like:
 
                    "left.R", "left.G", "left.B"
                    "right.R", "right.G", "right.B"
 
                 The EXR layer abstraction groups these into "left" and "right" image layers, each with a red, green
                 and blue channel.
 
                 Why do we need layers if we we already have parts?  Version 1 of the file format did not
                 allow for multipart files. These files will be reported with a part count of 1. If multiple images
                 needed to be put in the same file, then one would have to use layers to describe how to
                 group the channels. A problem with this approach is that for most compression formats,
                 (not B44) it may be necessary to fully decode all of the channels in the file in order to get the
                 subset you are asking for in the image layer. When the file is a multipart file, then each part
                 has its own table of tile offsets and we only need to decode the part to get those channels
                 out.  It is possible that a version 2 file may also include layers within each part.
 *  @memberof    axr_data_t */
typedef struct
{
    const char * __nullable         name;           /// the layer name
    const uint32_t * __nullable     channels;       /// an array of channelCount channel indicies into the list of image channels, which indicate the channels present in the layer
    unsigned long                   channelCount;   /// the number of channels in the layer
}axr_layer_info_t;
typedef enum  { axr_layer_info_current = 1 } axr_layer_info_struct_version_t;

/*! @abstract   Get the number of layers in the part
 *  @discussion Layers are derived from the file channel names. The channels may have names of
 *              the format  <layername>.<channel name>.  It is not required that you use layers. They
 *              help disambiguate cases when there are multiple channels of the same type (e.g. red)
 *  @param      fileData     A valid AppleEXR file reference
 *  @param      partIndex         The part index to query. See axr_data_get_part_count()
 *  @return     The number of layers found in the part.
 *  @memberof   axr_data_t */
unsigned long axr_data_get_layer_count( __nullable axr_data_t fileData,
                                        unsigned long partIndex )
                                        AXR_AVAILABILITY_v1;

/*! @abstract Get  information about a specific image layer
 *  @param      fileData     A valid AppleEXR file reference
 *  @param      partIndex  The part index to query. See axr_data_get_part_count()
 *  @param      layerIndex  The index of the layer within the part.
 *  @param      version   You must pass axr_layer_info_current here.
 *  @memberof   axr_data_t */
axr_layer_info_t axr_data_get_layer_info(__nullable axr_data_t fileData,
                                         unsigned long partIndex,
                                         unsigned long layerIndex,
                                         axr_layer_info_struct_version_t version _AXR_DEFAULT(axr_layer_info_current))
                                         AXR_AVAILABILITY_v1;

/*! @struct     axr_channel_info_t
 *  @discussion Summary information about a particular channel in a EXR part
 *  @var        sampleType Data type of the value stored for each channel
 *  @var        sampleBits sizeof(dataType)*8: 16 bits for half, 32 bits for UInt and float
 *  @var        xSampling The sampling rate in horizontal dimension. If > 1, samples are left aligned. Subsampled width is (width + xSampling - 1) / xSampling
 *  @var        ySampling The sampling rate in vertical dimension. If > 1, samples are top aligned. Subsampled height is (height + ySampling - 1) / ySampling
 *  @var        pLinear  0 or 1
 *  @var        name  For conventions on naming, see: https://www.openexr.com/documentation/TechnicalIntroduction.pdf
 *  @memberof   axr_data_t */
typedef struct
{
    axr_type_t          sampleType;     /// Data type of the value stored for each channel
    axr_channel_type_t  channelType;    /// AppleEXR attempts to classify the string in name[] into a well known channel type. on Failure: axr_channel_type_unknown
    uint32_t            sampleBits;     /// sizeof(sampleType)*8: 16 bits for half, 32 bits for UInt and float
    uint32_t            xSampling;      /// The sampling rate in horizontal dimension. If > 1, samples are left aligned. Subsampled width is (width + xSampling - 1) / xSampling
    uint32_t            ySampling;      /// The sampling rate in vertical dimension. If > 1, samples are top aligned. Subsampled height is (height + ySampling - 1) / ySampling
    uint8_t             pLinear;        /// 0 or 1
    uint8_t             _reserved[3];
    char                name[256];	    /// For conventions on naming, see: https://www.openexr.com/documentation/TechnicalIntroduction.pdf
}axr_channel_info_t;
typedef enum  { axr_channel_info_current = 1 } axr_channel_info_struct_version_t;

/*! @function axr_data_get_channel_info
 *  @abstract get information corresponding to an individual channel in a part
 *  @param      fileData                    The EXR file reference
 *  @param      partIndex       An EXR file may have multiple parts. This is the index of the part.
 *  @param      channelIndex  The index of the channel within the part
 *  @param      version              Pass axr_channel_info_current here
 *  @return     A valid axr_channel_info_t
 *  @memberof   axr_data_t */
axr_channel_info_t axr_data_get_channel_info( __nullable axr_data_t fileData,
                                              unsigned long partIndex,
                                              unsigned long channelIndex,
                                              axr_channel_info_struct_version_t version _AXR_DEFAULT(axr_channel_info_current))
                                              AXR_AVAILABILITY_v1;

    
/*! @function   axr_data_get_property_count
    @abstract   Get the number of  properties associated with the part at partIndex.
    @discussion Information in common properties that are understood by the AppleEXR
                are also returned by axr_data_get_channel_info() or axr_data_get_part_info()
    @param      fileData     The Apple EXR part file reference
    @param      partIndex  EXR files may have multiple parts. Each part is described as an part here.
    @return     The number of properties in the EXR part
    @memberof   axr_data_t */
unsigned long axr_data_get_property_count( __nullable axr_data_t fileData,
                                           unsigned long partIndex ) AXR_AVAILABILITY_v1;

/*! @abstract A property found within the OpenEXR metadata  */
typedef struct
{
    const char * __nonnull  name;      /// the name of the property as a '\0' terminated string
    const char * __nonnull  type;      /// the data type  of the property as a '\0' terminated string. https://www.openexr.com/documentation/openexrfilelayout.pdf
    size_t                  size;      /// the size of the data pointed to by value in bytes
    const void * __nonnull  value;     /// a raw pointer to the data contained in the property
    axr_property_flags_t    flags;     /// additional information about the property, including where else in the AppleEXR API the information is available
}axr_property_t;
typedef enum  { axr_property_current AXR_ENUM_AVAILABILITY_v1 = 1 } axr_property_struct_version_t;

/*! @function axr_data_get_property
 *  @abstract Get additional information about an image
 *  @discussion OpenEXR files contain many metadata tags. Commonly used information can often
 *              be obtained from axr_data_get_channel_info() or axr_data_get_part_info().
 *              However, for completeness, the full set of tags are available here.
 *  @param      fileData                       The AppleEXR file reference
 *  @param      partIndex          The index of the part in the file
 *  @param      propertyIndex    The index of the property in the file
 *  @param      version                 Pass axr_property_current here.
 *  @return     Property info corresponding with the part and property. The pointers point directly to file data
 *              and will become invalid if the file data is freed, unmapped, etc. The data should be copied, not cached.
 *  @memberof   axr_data_t */
axr_property_t axr_data_get_property( __nullable axr_data_t fileData,
                                      unsigned long partIndex,
                                      unsigned long propertyIndex,
                                      axr_property_struct_version_t version _AXR_DEFAULT(axr_property_current) )
                                      AXR_AVAILABILITY_v1;
    
/*! @abstract return the number of mip- or ripmap levels for a part
 *  @memberof axr_data_t
 *  @param      fileData              The AppleEXR file reference
 *  @param      partIndex          The index of the part in the file
 *  @return   The number of mip- or ripmap levels in a part. If axr_level_mode_one_level, 1 is returned. */
unsigned long axr_data_get_level_count( __nullable axr_data_t fileData,
                                        unsigned long partIndex )
                                        AXR_AVAILABILITY_v1;
    
/*! @abstract return the number of mip- or ripmap levels for an part
 *  @memberof axr_data_t
 *  @param      fileData              The AppleEXR file reference
 *  @param      partIndex          The index of the part in the file
 *  @param      levelIndex          The index of the level in the index
 *  @return   The number of pixels in each dimension for the part level  */
axr_size_t axr_data_get_level_size( __nullable axr_data_t fileData,
                                    unsigned long partIndex,
                                    unsigned long levelIndex _AXR_DEFAULT(0) )
                                    AXR_AVAILABILITY_v1;

#pragma mark axr_decoder_t
/*!
 *  @class      axr_decoder_t
 *  @abstract   A decoder for a EXR file
 *  @discussion The axr_decoder_t defines an interface for an opaque object that
 *              allows OpenEXR pixel content to be decoded.
 *
 *              The axr_decoder_t is an os_object.  If your application is not using ARC,
 *              it should be released with os_release when you are done using it.
 */

#if OS_OBJECT_USE_OBJC && ! defined(DOXYGEN)
    OS_OBJECT_DECL(axr_decoder);
#else
    typedef struct axr_decoder *  axr_decoder_t;
#endif


    
/*! @function   axr_decoder_create
 *  @abstract   create a new decoder
 *  @discussion This is an opaque object that describes the subregion of the input image to read, and the
 *              channel order and contents of the results.  At creation:
 *
 *              - The subregion of interest is the entire dataWindow.
 *              - The starting output channel list is empty.
 *                  More channels can be added  to the output stream with axr_decoder_append_result_channel()
 *                  or axr_decoder_append_constant_value_result_channel.
 *
 *              The object is a subclass of os_object and must be reference counted with os_retain / os_release
 *              if automatic reference counting (ARC) is not enabled.  Other NSObject methods such as -description and
 *              -debugDecription are available for debugging purposes. When used with axr_decoder_read_pixels,
 *              the order the channels appear in memory in the destination matches the  the order they are appended
 *              to the decoder.
 *
 *              For the purpose of reading pixels from an axr_data_t, each axr_decoder_t is internally multithreaded on
 *              the chunk (tile) level. If your application desires to itself manage the threading, the internal multithreading
 *              may be disabled using the axr_flags_disable_multithreading flag with axr_decoder_read_pixels/planes().
 *              To avoid redundant work, the threading granularity should align with and be a multiple of the
 *              axr_part_info_t.tileSize. Your application may create many axr_decoder_t objects for each axr_file_t.
 *              Behavior is undefined if the axr_decoder_t properties (e.g. subregion, channel lists) are modified concurrently
 *              with calls to axr_decoder_read_pixels/planes. Your application is responsible for creating appropriate locks
 *              or queues as  necessary to prevent this from happening.  axr_decoder_t objects are light weight.
 *              Significant memory may be transiently allocated to support the operation of axr_decoder_read_pixels/planes()
 *              for compression formats that are not B44, B44a and uncompressed.
 *
 *
 *  @memberof axr_decoder_t
 *  @param      fileData    The file reference that will be used with the decoder. Retained by read decoder.
 *                        Used to provide sensible default values for decoder.
 *  @param      partIndex The index of the part within the OpenEXR file to decode
 *  @param      levelIndex   The index of the mip- or ripmap level to use
 *  @result     On success, a valid axr_decoder_t object.  */
axr_decoder_t __nonnull axr_decoder_create( __nonnull axr_data_t fileData,
                                            unsigned long partIndex _AXR_DEFAULT(0),
                                            unsigned long levelIndex _AXR_DEFAULT(0),
                                            axr_flags_t   flags _AXR_DEFAULT_FLAGS
                                           ) AXR_RETURNS_RETAINED AXR_AVAILABILITY_v1;

/*! @function   axr_decoder_create_rgba
 *  @abstract   convenience method to create a new decoder for RGBA output
 *  @discussion This method combines the function of axr_decoder_create, axr_decoder_append_channels, and various
 *              information getters to attempt to select appropriate channels for RGBA output for a particular layer and part.
 *              The decoder created is intended to be suitable for use with axr_decoder_read_rgba_pixels, though it is not
 *              necessary to use axr_decoder_create_rgba to create a decoder appropriate for axr_decoder_read_rgba_pixels.
 *
 *              The function will attempt to select a subset of channels from the part / layer that best match the RGBA color
 *              model. The channels selected are in order of priority:
 *
 *                  R, G, B, (A)
 *                  X, Y, Z, (A)
 *                  Y, BY, RY, (A)
 *                  Y (A)
 *
 *              If the channels have unusual names (i.e. axr_channel_info_t.channelType doesn't identify them) then it is likely
 *              your application will need to fall back to selecting the channels by hand.
 *
 *  @memberof axr_decoder_t
 *  @param      fileData    The file reference that will be used with the decoder. Retained by read decoder.
 *                        Used to provide sensible default values for decoder.
 *  @param      layerName  If not NULL, the algorithm will restrict its search for channels in the indicated layer.
 *                        Use NULL to search the entire part. Use "" to search the default layer only.
 *  @param      partIndex The index of the part within the OpenEXR file to decode
 *  @param      levelIndex   The index of the mip- or ripmap level to use
 *  @param      flags           Options to govern the operation of axr_decoder_create_rgba
 *  @return     On failure, NULL will be returned.  Otherwise, a valid axr_decoder_t will be returned, suitable for use with
 *              axr_decoder_read_rgba_pixels and axr_decoder_create_rgba_colorspace. */
axr_decoder_t __nullable axr_decoder_create_rgba( __nonnull axr_data_t fileData,
                                                  const char * __nullable layerName _AXR_DEFAULT(NULL),
                                                  unsigned long partIndex _AXR_DEFAULT(0),
                                                  unsigned long levelIndex _AXR_DEFAULT(0),
                                                  axr_flags_t   flags _AXR_DEFAULT_FLAGS) AXR_RETURNS_RETAINED AXR_AVAILABILITY_v1;

/*! @function axr_decoder_copy
 *  @abstract Create a copy of the axr_decoder_t
 *  @discussion A copy may be necessary for managing multiple decoders on the same axr_data_t, particularly
 *              achieving thread safety for changes to decoder properties while the decoder is reading pixels or planes
 *  @memberof axr_decoder_t
 *  @return   A copy of the decoder */
axr_decoder_t __nonnull axr_decoder_copy( axr_decoder_t __nonnull decoder ) AXR_RETURNS_RETAINED AXR_AVAILABILITY_v1;
    
/*! @function   axr_decoder_set_subregion
 *  @abstract   Change the subregion of the image decoded by the read operation
 *  @discussion Coordinates are given within the dataWindow reference frame. {0,0}
 *              is the top left corner of the dataWindow, the first encoded byte in the image
 *              Example: to read the entire data window, set origins to 0, xSize to
 *              dataWindow.xMax - dataWindow.xMin +1, and ySize to
 *              dataWindow.yMax - dataWindow.yMin + 1.
 *
 *              Caution:  OpenEXR is generally not a randomly addressable image format. The tiles
 *              are randomly addressable, but the regions within the tiles are not. If you want to read
 *              1 pixel from a tile, the entire tile may need to be decoded. Consequently, if your
 *              application reads data 1 scanline at a time, it is entirely likely that by the time you
 *              have finished decoding the image, you will have actually decoded it 16 or 32 times
 *              over.  To avoid this problem, read data from the image in multiples of the tile size.
 *              Tile alignment starts aligned with the top left corner of the image {0,0} and proceeds
 *              in a packed fashion to the right and downward from there.
 *              See: axr_part_info_t.tileSize
 *
 *  @memberof axr_decoder_t
 *  @param      xOrigin     The horizontal position of the left edge of the region of interest in the dataWindow coordinate space.
 *  @param      yOrigin     The vertical position of the top edge of the subregion of interest in the dataWindow coordinate space.
 *  @param      xSize          The number of pixels to read in the horizontal dimension. Should be tile aligned for best performance.
 *  @param      ySize           The number of pixels to read in the vertical dimension. Should be tile aligned for best performance.
 *  @param      flags           flags to control the behavior of the operation
 *  @return     An axr_error_t indicating the success or failure of the operation.
 *  @memberof   axr_decoder_t */
axr_error_t axr_decoder_set_subregion( __nonnull axr_decoder_t d,
                                       uint32_t xOrigin,
                                       uint32_t yOrigin,
                                       uint32_t xSize,
                                       uint32_t ySize,
                                       axr_flags_t flags _AXR_DEFAULT_FLAGS);
    
/*! @function axr_decoder_append_channel
 *  @abstract Append a channel to the output stream
 *  @discussion Not atomic
 *          The correct choice of channel can sometimes be complicated by the presence of multiple
 *          views in the same part each with seemingly redundant color content. For example
 *          a part might reasonably contain 9 channels:
 *
 *              "red",         "green",        "blue",
 *              "left.red",   "left.green",  "left.blue",       // left stereo image
 *              "right.red", "right.green" "right.blue      // right stereo image
 *
 *          in any order. A simple heuristic such as pick the first few channels
 *          isn't likely to be robust.
 *
 *          In OpenEXR channel nomenclature, the channels are grouped into layers. Make sure
 *          that all channels that appear together in a part share the same layer. For the above set,
 *          the layers would be "", "left" and "right". Layers may themselves have multiple levels of
 *          hierarchy, such as "camera1.left.<channelname>" and do not need to share a consistent
 *          layer depth. As a general guideline for picking the default layer to show in the absence of
 *          other identifying information, prefer layers that have fewer prefixes over those with more.
 *          If two layers have the same number of prefixes, prefer the one that appears first in the file.
 *          Only channels in the same part may be read concurrently in the same call to
 *          axr_decoder_read_planes / axr_decoder_read_pixels. Layers with the same name in
 *          in different parts should be considered different layers. They may be associated
 *          with one another through some other dimension like time while still being different frames.
 *
 *  @memberof axr_decoder_t
 *  @param  decoder        The decoder
 *  @param  channelIndex    The index of the new channel to be appended to the result list.
 *                        If the channel will be used with axr_decoder_read_pixels, the
 *                        axr_type_t of the channel must match that of the previous channels.
 *  @return axr_error_success on success, a non-zero error otherwise
 *  @memberof   axr_decoder_t */
axr_error_t axr_decoder_append_channel( __nonnull axr_decoder_t decoder,
                                        uint32_t channelIndex,
                                        axr_flags_t flags _AXR_DEFAULT_FLAGS);

/*! @function axr_decoder_append_constant_value_channel
 *  @abstract Append a constant value channel to the  output stream
 *  @discussion A constant value channel is a channel filled entirely by a single value.
 *              For example, if the part contains only RGB data but you need RGBA with alpha 1.0
 *              everywhere, then append a constant value channel after red, green, blue with value 1.0.
 *              The x- and y-sampling rates are inferred from the other channels present in the part.
 *              It is an error to use constant value channels with axr_decoder_read_planes.
 *  @memberof axr_decoder_t
 *  @param  decoder              The axr_decoder_t
 *  @param  value                   The value held by the channel. All pixels in the image will contain this value.
 *                        A IEEE-754 double precision value can represent all uint32_t, float and __fp16
 *                        values without loss of precision.
 *  @param  sampleType        The data type used by the additional channel. I attempted to infer this value
 *                        from context but found too many corner cases in which the missing information
 *                        is needed resulting  in expense reanalyzing the decoder for context and
 *                        too many error conditions. So as to simplify error handling for the application,
 *                        the type is passed in explicitly. When used with axr_decoder_read_pixels,
 *                        the axr_type_t for all channels in the pixel must match or undefined behavior will occur.
 *                        axr_decoder_read_planes will function as desired for cases with heterogeneous
 *                        axr_types.
 *  @param  channelType      The type of channel to be added. Used for colorspace inference in axr_decoder_create_colorspace
 *                        and axr_decoder_create_rgba_colorspace .
 *  @param  flags                   Flags to control the operation of axr_decoder_append_constant_value_channel
 *  @return axr_error_success on success, a non-zero error otherwise
 *  @memberof   axr_decoder_t */
axr_error_t axr_decoder_append_constant_value_channel( __nonnull axr_decoder_t decoder,
                                                       double value,
                                                       axr_type_t sampleType,
                                                       axr_channel_type_t channelType,
                                                       axr_flags_t flags _AXR_DEFAULT_FLAGS );

/*! @function  axr_decoder_get_channel_count
 *  @abstract  Get the number of  output channels appended so far
 *  @param     decoder   A valid axr_decoder_t
 *  @memberof  axr_decoder_t */
uint32_t axr_decoder_get_channel_count( __nonnull axr_decoder_t decoder);
    
/*! @function axr_decoder_translate_channel_index
 *  @abstract Get the part index of an individual decoder channel
 *  @memberof axr_decoder_t
 *  @discussion  A decoder may reference channels {1, 2, 5, 9} as the set of channels
 *               to be decoded. When your application calls axr_decoder_get_channel_info()
 *               it will refer to these as channel indices {0,1,2,3}. However, it may be necessary
 *               at times to lookup the original indices of those channels in the axr_data_t.
 *               This function will map [0,3] back to, for example, {1,2,5,9}.
 *  @param      decoder             The axr_decoder_t
 *  @param      channelIndex  The index of the channel within the decoder.
 *                           The index[0, axr_decoder_get_channel_count()-1]
 *  @return     The index of the channel within the part or -1 if the channelIndex is too large or the channel is
 *              a constant value channel (and therefore not in the part)
 *  @memberof   axr_decoder_t */
long axr_decoder_translate_channel_index( __nonnull axr_decoder_t decoder,
                                          unsigned long channelIndex)
                                          AXR_AVAILABILITY_v1;

/*! @function axr_decoder_get_channel_info
 *  @abstract Get axr_channel_info_t for a channel described by a decoder
 *  @param decoder       A valid axr_decoder_t
 *  @param channelIndex  The index of the channel in the decoder. (Not the same index as that used in axr_data_get_channel_info!)
 *                      If there are N channels described in the decoder, this has value [0,N-1]
 *  @param version             You must pass axr_channel_info_current here.
 *  @result A axr_channel_info_t with information about the channel.  Do not free any pointers passed in the struct.
 *  @memberof   axr_decoder_t */
axr_channel_info_t axr_decoder_get_channel_info(__nonnull axr_decoder_t decoder,
                                                unsigned long channelIndex,
                                                axr_channel_info_struct_version_t version _AXR_DEFAULT(axr_channel_info_current) )
                                                AXR_AVAILABILITY_v1;

/*! @function  axr_decoder_clear_channel_list
 *  @abstract  Remove all result channels from the output channel list
 *  @param     decoder   A valid axr_decoder_t
 *  @memberof  axr_decoder_t */
axr_error_t axr_decoder_clear_channel_list( __nonnull axr_decoder_t decoder ) AXR_AVAILABILITY_v1;

/*! @struct axr_decoder_info_t
    @abstract information about the segment of a EXR part referenced by the decoder  */
typedef struct
{
    struct axr_data * __nonnull                    data;            /// the axr_data_t used to create the decoder
    uint32_t                                       partIndex;       /// part index used  to create decoder
    axr_level_mode_t                               levelMode;       /// the axr_level_mode_t for the image
    uint32_t                                       levelIndex;      /// the index of the mip level
    axr_size_t                                     levelSize;       /// the size in pixels of this mip- or  rip-level
    struct
    {
        struct{ uint32_t x, y;}                    origin;          /// The offset to the top left corner of the subregion from the the top left corner of the data window
        axr_size_t                                 size;            /// the size of the sub region
    }subregion;                                                     /// the spatial subregion to decode. See axr_decoder_set_subregion
}axr_decoder_info_t;
typedef enum  { axr_decoder_info_current = 1 } axr_decoder_info_struct_version_t;

axr_decoder_info_t axr_decoder_get_info( __nonnull axr_decoder_t decoder,
                                         axr_decoder_info_struct_version_t version _AXR_DEFAULT(axr_decoder_info_current))
                                         AXR_AVAILABILITY_v1;

typedef struct
{
    axr_size_t          size;                       /// the size of the image
    size_t              minimumRowBytes;            /// The minimum byte stride from a pixel to a pixel at the same position, one row below.
    uint32_t            bitsPerChannel;             /// The number of bits per channel. (16 or 32)
    uint32_t            bitsPerPixel;               /// The number of bits per encoded pixel. If a plane, then equal to bitsPerChannel
    axr_type_t          type;                       /// The encoding type of the data. Half, float, uint32_t
}axr_decoder_read_info_t;
typedef enum  { axr_decoder_read_info_current = 1 } axr_decoder_read_info_struct_version_t;

/*! @function axr_decoder_get_data
 *  @abstract Get the axr_data_t used to create the decoder
 *  @memberof axr_decoder_t */
__nonnull axr_data_t axr_decoder_get_data( __nonnull axr_decoder_t data) AXR_AVAILABILITY_v1;

/*! @function axr_decoder_get_data_window_size
 *  @abstract returns the size of the data window for the decoder mipmap level */

#pragma mark Read the image
        
/*! @abstract  interleave two or four channels from a subregion of an part into a single buffer
 *  @discussion Each pixel is a interleaved set of channels, e.g. RGBA, RGBA, RGBA...
 *              The channels must all be the same size. The number of channels must be 1, 2, or 4
 *              The x & y subsampling for the channels must match. The mip/rip levels must match.
 *              The position and size of the region of interest in the part must match.
 *              This method should be  faster than reading out planes separately then interleaving them.
 *              This method is not suitable for mixed sampling rates like 422, 420, etc. though it may
 *              be used to create the {Cb,Cr,Cb,Cr...} plane in 420.
 *
 *              Performance Advisory:  Most OpenEXR compression formats are best described as a
 *              2-pass decode process. Typically, the first pass decodes a lossless bitstream
 *              compression scheme like Zip or Huffman. The second pass extracts out the requested
 *              channels from the others, often performing some data manipulations along the way such
 *              as interleaving bytes from different points in the compressed stream, wavelet
 *              reconstruction or the B44 transform. Importantly, the first pass is generally very
 *              serial and not dynamically addressable due to variable code word size and lack of
 *              restart markers or other context clues. The second pass is typically very parallel, easily
 *              vectorizable and trivial to calculate the location of data leading to any particular pixel.
 *
 *              Because prior to 1st pass decompression the file contents are generally not dynamically addressable
 *              below part and chunk granularity, this library may have to do 1st pass decompression on much
 *              more data than is ultimately needed to fulfil the request. For this purpose, image subregions are
 *              rounded outward to the chunks (tiles) that contain them, and there is no dynamic addressability
 *              for channels at all. So, if one channel is needed from a part with 20 channels, the library might be
 *              forced to do 1st pass decoding on all 20 channels to retrieve the one requested.  Since the first pass
 *              may dominate the total decompression time -- recall this stage is also usually not vectorizable or
 *              trivially multithreaded -- and may have to work on much more data than is needed and AppleEXR
 *              does not currently cache the first pass decoding results for use by later calls to axr_decoder_read_planes
 *              or axr_decoder_read_pixels -- they are big -- it is important that your application acts to minimize the 1st
 *              pass workload. An application that does not take this file format limitation into consideration may find
 *              itself redundantly running 1st pass decompression on the same data over and over, triggering a needless
 *              performance regression of possibly an order of magnitude or more.
 *
 *              There are several things your application can do to mitigate this problem:
 *
 *              1) When possible, encode your images taking advantage of OpenEXR multipart (OpenEXR 2.0
 *              and later) file format, taking care to bundle channels that are used together into the same part.
 *              Move channels that are not used together or are rarely used off to other parts. This is most
 *              important when there are many channels in the part, only some of which are typically used.
 *
 *              2) If your application needs to read multiple channels from a part, be sure to read them all at once
 *              with a single call to axr_decoder_read_pixels or axr_decoder_read_planes. This will ensure the
 *              1st pass decoding stage happens only once per part.
 *
 *              3) The axr_compression_none and axr_compression_b44 methods do not involve a 1st pass
 *              and consequently do not suffer this limitation. axr_compression_b44a has a variable
 *              block encoding size and does not enjoy the same benefit as axr_compression_b44.
 * *
 *              Terms: An OpenEXR "part" is a OpenEXR file segment containing one or more channels. Prior to
 *              OpenEXR 2.0, all files were single part files, meaning that all channels were encoded together.
 *              Please see the OpenEXR File Layout specification for a more details. The number of parts returned by
 *              axr_data_get_part_count() is the number of parts in the file. Chunks are spatial subregions
 *              of each part. They are described as tiles in the AppleEXR interface. See axr_part_info_t.tileSize.
 *
 *              The above information is provided for software tuning purposes only. Implementation details
 *              are subject to change in future versions of the library.
 * *
 *  @param  decoder      The decoder object that specifies which part of the data to read
 *  @param  destination     A pointer to the top left corner of the place to write the output data. See discussion
 *                       above for special treatment of axr_line_order_decreasing_y.
 *                       Note that addressing here is different from OpenEXR. The OpenEXR setFrameBuffer( ptr, ...)
 *                       call expects ptr to point to {0,0} in the dataWindow coordinate space. Whereas,
 *                       AppleEXR, the destination pointer points to {dataWindow.min.x, dataWindow.min.y}
 *  @param  destinationRowBytes  The distance in bytes from a sample to the next vertically adjacent sample in the destination buffer. Supports negative rowBytes. See  discussion above for special treatment of axr_line_order_decreasing_y
 *  @param  flags        Options to control the decode process
 *  @return axr_error_success if the operation succeeds.  Another axr_error code otherwise.
 *  @memberof axr_decoder_t */
axr_error_t axr_decoder_read_pixels( __nonnull axr_decoder_t decoder,
                                     void * __nonnull destination,
                                     unsigned long destinationRowBytes,
                                     axr_flags_t flags _AXR_DEFAULT_FLAGS ) AXR_AVAILABILITY_v1;
    
/*! @abstract returns the expected size of the data written by axr_decoder_read_pixels
 *  @discussion This is intended to simplify allocation and handling of the result buffer
 *  @param          decoder     The decoder your application intends to use with axr_decoder_read_pixels
 *  @param          flags           The flags your application intends to use with axr_decoder_read_pixels
 *  @result         The size, shape and encoding of the content written by axr_decoder_read_pixels */
axr_decoder_read_info_t axr_decoder_get_read_pixels_info( __nonnull axr_decoder_t decoder,
                                                          axr_flags_t flags _AXR_DEFAULT_FLAGS,
                                                          axr_decoder_read_info_struct_version_t version _AXR_DEFAULT(axr_decoder_read_info_current))    AXR_AVAILABILITY_v3;
    

/*! @abstract  Read the channels described by the decoder as RGBA content
 *  @discussion  Behavior varies according to available channels:
 *
 *                  RGB, RGBA:                 Decoded as is and interleaved to RGBA
 *                  Y RY BY, Y RY BY A:    Decoded and converted to RGBA  (YUV / YCC / YCbCr content)
 *                  XYZ, XYZA                    Decoded to XYZA
 *                  Y, YA                             Decoded to Y,Y,Y,A    (grayscale content as RGBA)
 *
 *                  Only these channel combinations are supported. All channels must have the same axr_type_t.
 *
 *              In each case, the colorspace returned by axr_decoder_create_rgba_colorspace will be appropriate
 *              to the content returned by this function.
 *
 *          The  size of the destination buffer is given by axr_decoder_info_t.subregion.size.height * rowBytes.
 *          The minimum rowBytes is given by 4 * width * sampleBytes.   If the rowBytes is larger than that, the extra
 *          memory in the row will not be modified. SampleBytes is 2 for half, and 4 for float. Uint32_t is not supported
 *          by this function.
 *
 *  @memberof axr_decoder_t
 *  @param  decoder      The decoder object that specifies which part of the data to read
 *  @param  destination     A pointer to the top left corner of the place to write the output data. See discussion above for special treatment of axr_line_order_decreasing_y
 *  @param  destinationRowBytes  The distance in bytes from a sample to the next vertically adjacent sample in the destination buffer. Supports negative rowBytes. See  discussion above for special treatment of axr_line_order_decreasing_y
 *  @param  alphaVal    If no alpha channel is present, the value in alphaVal is used instead
 *  @param  flags        Options to control the decode process
 *  @return axr_error_success if the operation succeeds.  Another axr_error code otherwise.  Use axr_decoder_create_rgba_colorspace to
 *          obtain a CGColorSpaceRef to represent this content.
 *                  */
axr_error_t axr_decoder_read_rgba_pixels(__nonnull axr_decoder_t decoder,
                                         void * __nonnull destination,
                                         unsigned long destinationRowBytes,
                                         double alphaVal _AXR_DEFAULT(1.0),
                                         axr_flags_t flags _AXR_DEFAULT_FLAGS ) AXR_AVAILABILITY_v1;

/*! @abstract returns the expected size of the data written by axr_decoder_read_rgba_pixels
 *  @discussion This is intended to simplify allocation and handling of the result buffer
 *  @param          decoder     The decoder your application intends to use with axr_decoder_read_rgba_pixels
 *  @param          alphaVal     The alpha value to be passed into axr_decoder_read_rgba_pixels
 *  @param          flags           The flags your application intends to use with axr_decoder_read_rgba_pixels
 *  @result         The size, shape and encoding of the content written by axr_decoder_read_rgba_pixels */
axr_decoder_read_info_t axr_decoder_get_read_rgba_pixels_info( __nonnull axr_decoder_t decoder,
                                                               double alphaVal _AXR_DEFAULT(1.0),
                                                               axr_flags_t flags _AXR_DEFAULT_FLAGS,
                                                               axr_decoder_read_info_struct_version_t version _AXR_DEFAULT(axr_decoder_read_info_current)) AXR_AVAILABILITY_v3;

    
/*! @struct axr_pixel_data_t
 *  @abstract A structure that describes each plane written to by axr_decoder_read_planes */
typedef struct axr_pixel_data_t
{
    void * __nonnull        data;       /// pointer to top left corner of region to write to
    size_t                  rowBytes;   /// the distance in bytes from the start of a pixel and the same position one row below
}axr_pixel_data_t;
typedef enum  { axr_pixel_data_current AXR_ENUM_AVAILABILITY_v1 = 0 } axr_pixel_data_struct_version_t;

/*! @function axr_decoder_read_planes
 *  @abstract read one or more channels from a subregion of an image as separate image planes
 *  @discussion Each plane contains a single channel. It is much more efficient to read multiple planes at once than one at a time.
 *              The planes may not be the result of axr_decoder_append_constant_value_channel(). They must be real
 *              data in the file.
 *
 *              Performance Advisory:  Most OpenEXR compression formats are best described as a
 *              2-pass decode process. Typically, the first pass decodes a lossless bitstream
 *              compression scheme like Zip or Huffman. The second pass extracts out the requested
 *              channels from the others, often performing some data manipulations along the way such
 *              as interleaving bytes from different points in the compressed stream, wavelet
 *              reconstruction or the B44 transform. Importantly, the first pass is generally very
 *              serial and not dynamically addressable due to variable code word size and lack of
 *              restart markers or other context clues. The second pass is typically very parallel, easily
 *              vectorizable and trivial to calculate the location of data leading to any particular pixel.
 *
 *              Because prior to 1st pass decompression the file contents are generally not dynamically addressable
 *              below part and chunk granularity, this library may have to do 1st pass decompression on much
 *              more data than is ultimately needed to fulfill the request. For this purpose, image subregions are
 *              rounded outward to the chunks (tiles) that contain them, and there is no dynamic addressability
 *              for channels at all. So, if one channel is needed from a part with 20 channels, the library might be
 *              forced to do 1st pass decoding on all 20 channels to retrieve the one requested.  Since the first pass
 *              may dominate the total decompression time -- recall this stage is also usually not vectorizable or
 *              trivially multithreaded -- and may have to work on much more data than is needed and AppleEXR
 *              does not currently cache the first pass decoding results for use by later calls to axr_decoder_read_planes
 *              or axr_decoder_read_pixels -- they are big -- it is important that your application acts to minimize the 1st
 *              pass workload. An application that does not take this file format limitation into consideration may find
 *              itself redundantly running 1st pass decompression on the same data over and over, triggering a needless
 *              performance regression of possibly an order of magnitude or more.
 *
 *              There are several things your application can do to mitigate this problem:
 *
 *              1) When possible, encode your images taking advantage of OpenEXR multipart (OpenEXR 2.0
 *              and later) file format, taking care to bundle channels that are used together into the same part.
 *              Move channels that are not used together or are rarely used off to other parts. This is most
 *              important when there are many channels in the part, only some of which are typically used.
 *
 *              2) If your application needs to read multiple channels from a part, be sure to read them all at once
 *              with a single call to axr_decoder_read_pixels or axr_decoder_read_planes. This will ensure the
 *              1st pass decoding stage happens only once per part.
 *
 *              3) The axr_compression_none and axr_compression_b44 methods do not involve a 1st pass
 *              and consequently do not suffer this limitation. axr_compression_b44a has a variable
 *              block encoding size and does not enjoy the same benefit as axr_compression_b44.
 * *
 *              Terms: An OpenEXR "part" is a OpenEXR file segment containing one or more channels. Prior to
 *              OpenEXR 2.0, all files were single part files, meaning that all channels were encoded together.
 *              Please see the OpenEXR File Layout specification for a more details. The number of images returned by
 *              axr_data_get_part_count() is the number of parts in the file. Chunks are spatial subregions
 *              of each part. They are described as tiles in the AppleEXR interface. See axr_part_info_t.tileSize.
 *
 *              The above information is provided for software tuning purposes only. Implementation details
 *              are subject to change in future versions of the library.
 * *
 *  @param  decoder     The list of channels to read
 *  @param  destinationPlaneList     A pointer to an array of axr_pixel_data_t structures describing where to put each plane.
 *                                The number of such is given by axr_decoder_get_channel_count(decoder)
 *                                The size of each is given by the decoder. The order matches the order the channels
 *                                were added to the decoder.
 *  @param  flags                        Options to control the decode process
 *  @param  structVersion       Pass axr_pixel_data_current here.
 *  @return axr_error_success if the operation succeeds.  Another axr_error code otherwise.
 *  @memberof axr_decoder_t */
axr_error_t axr_decoder_read_planes( __nonnull axr_decoder_t decoder,
                                     axr_pixel_data_t * __nonnull destinationPlaneList,  // Address of first struct in axr_pixel_data_t[ axr_decoder_get_channel_count(decoder) ]
                                     axr_flags_t flags _AXR_DEFAULT_FLAGS,
                                     axr_pixel_data_struct_version_t structVersion _AXR_DEFAULT(axr_pixel_data_current) ) AXR_AVAILABILITY_v1;

/*! @abstract returns the expected size of the data written by axr_decoder_read_planes
 *  @discussion This is intended to simplify allocation and handling of the result buffer
 *  @param          decoder     The decoder your application intends to use with axr_decoder_read_planes
 *  @param          planeIndex     The alpha value to be passed into axr_decoder_read_planes
 *  @param          flags           The flags your application intends to use with axr_decoder_read_planes
 *  @result         The size, shape and encoding of the content written by axr_decoder_read_planes */
axr_decoder_read_info_t axr_decoder_get_read_planes_info( __nonnull axr_decoder_t decoder,
                                                          unsigned long planeIndex,
                                                          axr_flags_t flags _AXR_DEFAULT_FLAGS,
                                                          axr_decoder_read_info_struct_version_t version _AXR_DEFAULT(axr_decoder_read_info_current))    AXR_AVAILABILITY_v3;

    
/*! @function axr_decoder_create_colorspace
 *  @abstract Create a CGColorSpaceRef to represent the EXR image
 *  @discussion Use this to create a CGColorSpaceRef suitable for use to represent the data with CoreGraphics
 *              in a CGImageRef, CGColorConversionInfoRef or a CGContextRef.  Note that if the data is YCbCr,
 *              the CGColorSpaceRef for the corresponding RGB colorspace is returned.
 *  @memberof axr_decoder_t
 *  @param      decoder   A valid axr_decoder_t
 *  @param      options         An options dictionary to used to provide additional configuration at the CGColorSpaceRef creation step.
 *                   Available options:
 *                      <none>      AXR_AVAILABILITY_v1
 *  @param      flags           flags to control the operation of the function. axr_flags_print_debug_info may provide additional information for failure cases.
 *  @return If the color model could be inferred from the channel names and corresponds to a representable CGColorSpaceRef, a valid CGColorSpaceRef will
 *          be returned. Otherwise, NULL will be returned. For the purpose of color model identification, alpha is ignored, but non-color components such as
 *          unknown, distance, distance_back, id, etc. will trigger failure. (CG doesn't well tolerate  non-alpha data interleaved with color data, even when
 *          CGImageAlphaNoneSkip{First/Last} is used to describe those channels.)
 */
void * __nullable /*CGColorSpaceRef*/
    axr_decoder_create_colorspace( __nonnull axr_decoder_t decoder,
                                   axr_flags_t flags,
                                   void * __nullable /* CFDictionaryRef */ options _AXR_DEFAULT(NULL)) AXR_AVAILABILITY_v1;
    
    /*! @function axr_decoder_create_rgba_colorspace
     *  @abstract Create a CGColorSpaceRef to represent the EXR image as read by axr_decoder_read_rgba_pixels
     *  @discussion Use this to create a CGColorSpaceRef suitable for use to represent the data with CoreGraphics
     *              in a CGImageRef, CGColorConversionInfoRef or a CGContextRef for data produced by axr_decoder_read_rgba_pixels.
     *              The difference in behavior between this interface and axr_decoder_create_colorspace is that grayscale
     *              content will return a RGB colorspace. axr_decoder_read_rgba_pixels returns YYYA for grayscale content
     *              and should be associated with a RGB colorspace.
     *
     *              CAUTION: The colorspace returned by this function is not guaranteed to be a RGBA colorspace. If the
     *              part chromaticities indicate XYZ, the colorspace will be XYZ instead.
     *
     *  @memberof axr_decoder_t
     *  @param      decoder   A valid axr_decoder_t
     *  @param      options         An options dictionary to used to provide additional configuration at the CGColorSpaceRef creation step.
     *                   Available options:
     *                      <none>      AXR_AVAILABILITY_v1
     *  @param      flags           flags to control the operation of the function. axr_flags_print_debug_info may provide additional information for failure cases.
     *  @return     Decoder channels         Output colorspace                                         axr_decoder_create_colorspace behavor
     *              ==============        ==============                                        ==============================
     *                R, G, B, (A)              RGB linear extended colorspace                    RGB linear extended colorspace
     *                X, Y, Z, (A)                XYZ linear extended colorspace                    XYZ linear extended colorspace
     *                Y BY RY (A)             RGB linear extended colorspace                   RGB linear extended colorspace
     *                Y (A)                        RGB linear extended colorspace                    Grayscale linear extended colorspace
     *                other                       NULL
     */
void * __nullable /*CGColorSpaceRef*/
    axr_decoder_create_rgba_colorspace( __nonnull axr_decoder_t decoder,
                                        axr_flags_t flags,
                                        void * __nullable /* CFDictionaryRef */ options _AXR_DEFAULT(NULL)) AXR_AVAILABILITY_v1;

#pragma mark -
#pragma mark axr_encoder_t
/*!
 *  @class      axr_encoder_t
 *  @abstract   A encoder for a EXR file
 *  @discussion The axr_encoder_t defines an interface for an opaque object that
 *              allows  pixel content to be encoded to an OpenEXR file.
 *
 *              The axr_encoder_t is an os_object.  If your application is not using ARC,
 *              it should be released with os_release when you are done using it.
 */

#if OS_OBJECT_USE_OBJC && ! defined(DOXYGEN)
    OS_OBJECT_DECL(axr_encoder);
#else
    typedef struct axr_encoder *  axr_encoder_t;
#endif




/*! @abstract  Create a mutable axr_encoder_t object
 *  @discussion In version 1, deep data is not supported.
 *  @param      flags               A default set of flags to use with the object, when  other flags are not available
 *  @return     On success, a pointer to a axr_encoder_t
 *  @memberof   axr_encoder_t */
__nonnull axr_encoder_t axr_encoder_create( axr_flags_t flags ) AXR_RETURNS_RETAINED AXR_AVAILABILITY_v1;
    

/*!
 *  @class     axr_part_t
 *  @abstract   An description for a part of a EXR file
 *  @discussion The axr_part_t defines an interface for an opaque object that
 *              allows OpenEXR  part to be encoded.  The object belongs to
 *              the axr_encoder_t and should not be freed or released by you. */
#if OS_OBJECT_USE_OBJC && ! defined(DOXYGEN)
    OS_OBJECT_DECL(axr_part);
#else
    typedef struct axr_part *  axr_part_t;
#endif

/*! @abstract Append a new part for encoding an EXR file
 *  @discussion  The following default values are also initialized:
 *                  chromaticities:  ITU-BT.709
 *                  whiteLuminance: 1
 *                  preview: <none>
 *
 *               axr_compression_piz  should use a tile height that is a power of two such as 32
 *               tile dimensions do not need to evenly divide into data window dimensions
 *
 *               The parts are saved in the order they are created.
 *
 *  @memberof axr_encoder_t
 *  @param  viewName  An optional human readable name for the part  (attribute: view)
 *  @param  compression     The compression style to use. ZIP is a good choice. PIZ (also lossless) can be better for size
 *                        for large images. B44 (lossy) is better for speedy random access into image subregions, and
 *                        for parts with lots of channels, only some of which are typically read.
 *                        Note: version 1 supports only axr_compression_zip. Other choices will be mapped to zip.
 *  @param  dataWindow       The pixel coordinates for the data window
 *  @param  displayWindow  The pixel coordinates for the display window
 *  @param  lineOrder            The ordering of the scanlines within the part. axr_line_order_increasing_y is typical
 *  
 *  @param  pixelAspectRatio  The aspect ratio of the pixels. Typically 1
 *  @param  screenWindowCenterX  The center of the screen window in the x axis
 *  @param  screenWindowCenterY  The center of the screen window in the y axis
 *  @param  screenWindowWidth       The width of the screen window
 *  @param  xDensity                           The intended resolution of the image in pixels per inch. The vertical density is xDensity * pixelAspectRatio
 *  @param  tileWidth       The width of the tile. Will be ignored for axr_compression_none/zips/rle, in which case it is the rip width. If 0, AppleEXR will pick a value.
 *  @param  tileHeight     The height of the tile. Will be ignored for axr_compression_none/zips/rle, in which case it is 1. If 0, AppleEXR will pick a value.
 *  @param  levelMode       {ripmap/mipmap/one_level} | {round_up / round_down}.   Must be axr_level_mode_one_level for axr_compression_none/zips/rle
 *  @return  on success, a valid axr_part_t owned by the encoder. You should not release this object unless you have also retained it. */
__nonnull axr_part_t axr_encoder_append_part(__nonnull axr_encoder_t encoder,
                                             const char * __nonnull  partName,      // "name" attribute
                                             const char * __nullable viewName,      // "view" attribute
                                             axr_compression_t compression,
                                             axr_box2i_t dataWindow,
                                             axr_box2i_t displayWindow,
                                             axr_line_order_t lineOrder,
                                             float pixelAspectRatio,
                                             float screenWindowCenterX,
                                             float screenWindowCenterY,
                                             float screenWindowWidth,
                                             float xDensity _AXR_DEFAULT(72.0f),
                                             uint32_t tileWidth _AXR_DEFAULT(0),
                                             uint32_t tileHeight _AXR_DEFAULT(0),
                                             axr_level_mode_t levelMode  _AXR_DEFAULT(axr_level_mode_one_level)
                                            ) AXR_RETURNS_INNER_POINTER AXR_AVAILABILITY_v1;

/*! @abstract Adjust the primaries used by the part
 *  @discussion  If parameters x or y are NULL, the chromaticities are inferred from the CGColorSpaceModel.
 *  Channel names are in part inferred from the colorspace model. The ColorSpace model must be set before
 *
 *  @memberof axr_part_t
 *  @param   part      The part
 *  @param   cgColorSpaceModel    The CGColorSpaceModel for the colorspace:
 *                                  kCGColorSpaceModelUnknown        Suitable when the image has only  non-color data
 *                                  kCGColorSpaceModelMonochrome  Grayscale only
 *                                  kCGColorSpaceModelRGB                RGB and YCbCr  {use corresponding RGB x, y}
 *                                  kCGColorSpaceModelXYZ                 XYZ      (pass NULL for x, y}
 *                              For this purpose, ignore alpha and other non-color channels.
 *  @param   whiteLuminance           1.0f is the default
 *  @param   x              {red, green, blue, white}   "little x" components of the chromaticities
 *  @param   y              {red, green, blue, white}   "little y" components of the chromaticities  */
void axr_part_set_chromaticities( __nonnull axr_part_t part,
                                  int32_t  cgColorSpaceModel,
                                  float whiteLuminance  _AXR_DEFAULT(1.0f),
                                  float x[__nullable 4] _AXR_DEFAULT(NULL),
                                  float y[__nullable 4] _AXR_DEFAULT(NULL)) AXR_AVAILABILITY_v1;

/*! @abstract Callback to free your data when AppleEXR is done with it
 *  @discussion  This callback is an opportunity for you to dispose of memory when AppleEXR is done with it.
 *               You may ignore it, release or free objects, and kick off another GCD task to the same.
 *               It is possible that your data destructor might be called reentrantly, if it is handed to
 *               AppleEXR many times. Use appropriate synchronization primitives to ensure your code in the
 *               destructor is thread safe and run on the main thread if necessary.  This destructor will be
 *               called even if data is NULL. Destructors are not guaranteed to be called in any particular order.
 *  @memberof axr_part_t
 *  @param  data            A pointer to your data to be freed / released
 *  @param  userInfo   This is extra information you can hand to yourself to help with the destruction of data.
 *                    It might be a type code, a size, or a pointer to some other data structure of yours, for example.
 *                    The definition of what this is is up to you. AppleEXR merely collects the value at the
 *                    time you hand it the destructor and hands it back to you when the destructor is called.*/
typedef void (*axr_user_data_destructor_t)(const void * __nullable data, uintptr_t userInfo);
    
/*! @abstract add a preview image to the axr_part_t
 *  @discussion  There can only be 1 preview image.
 *  @memberof axr_part_t
 *  @param  part  The part
 *  @param  data    A pointer to the top left corner of a RGBA unorm8 image
 *  @param  width  The number of pixel columns in the preview image
 *  @param  height The number of pixel rows in the preview image
 *  @param  rowBytes The number of bytes from one row to the next in the same column of the preview image
 *  @param  userData   This is handed back to you as the second parameter of dataDestructor.  AppleEXR doesn't know what it is.
 *  @param  dataDestructor  An optional callback you can use to free the data pointed to by the data parameter.  Will be called even if data is NULL.
 *                         Note that dataDestructor may be called before axr_part_set_preview returns.  */
void axr_part_set_preview( __nonnull axr_part_t part,
                            const uint8_t * __nullable data,
                            uint32_t width,
                            uint32_t height,
                            size_t rowBytes,
                            uintptr_t userData,
                            __nullable axr_user_data_destructor_t dataDestructor) AXR_AVAILABILITY_v1;
    
/*! @abstract   Add an arbitrary EXR attribute to an part
 *  @discussion All EXR attributes have a attribute name and a type code  See "Predefined Attribute Types" https://www.openexr.com/documentation/openexrfilelayout.pdf
 *              EXR uses little endian byte order for types larger than one byte. Your application needs to apply this permutation before handing the data to AppleEXR.
 *              All data is packed, with no byte padding for alignment.  No effort is made to avoid creating duplicate attributes. This interface is not intended as a method
 *              to change data passed to axr_part_create()
 *  @memberof axr_part_t
 *  @param  part   The part to add the attribute
 *  @param  attributeName   A '\0' terminated C-string. See URL in discussion for attribute names.
 *  @param  attributeType   A '\0' terminated C string. See URL in discussion for type names.
 *  @param  attributeSize   The number of bytes in the buffer pointed to by attributeBytes
 *  @param  attributeBytes  A pointer to the data to be store in the EXR metadata. Data should be byte-swapped to little endian order. */
axr_error_t axr_part_append_attribute( __nonnull axr_part_t part,
                                       const char * __nonnull attributeName,
                                       const char * __nonnull attributeType,
                                       size_t attributeSize,
                                       const uint8_t * __nonnull attributeBytes,
                                       uintptr_t userData,
                                       __nullable axr_user_data_destructor_t dataDestructor ) AXR_AVAILABILITY_v1;

/*! @abstract   Create a CGColorSpaceRef to match the image content to be written to a axr_part_t
 *  @discussion Your application is responsible for converting  image pixels to this colorspace before handing
 *              it to AppleEXR.  Please see vImageConvert_AnyToAny for one way to do this.
 *  @memberof axr_part_t
 *  @param      part  The part to contain the data
 *  @param      flags  Flags to control the operation
 *  @param      options  A set of optional options to add to the colorspace you get back
 *  @return     A CGColorSpaceRef. Make sure to call CFRelease() on it when you no longer need it.     */
void * __nullable /*CGColorSpaceRef*/
axr_part_create_colorspace( __nonnull axr_part_t part,
                            axr_flags_t flags,
                            void * __nullable /* CFDictionaryRef */ options _AXR_DEFAULT(NULL)) AXR_AVAILABILITY_v1;
    
/*! @abstract  Get the number of rip levels in X and Y dimensions */
axr_size_t axr_part_get_rip_level_counts( __nonnull axr_part_t part );

/*! @abstract Get the tile size used by the part.
 *  @discussion This information may be needed to correctly size the pixel information passed to the axr_data_provider_read_t.
 *  @memberof axr_part_t
 *  @part            The part
 *  @return   The tile size used for encoding the part.   */
axr_size_t axr_part_get_tile_size( __nonnull axr_part_t part ) AXR_AVAILABILITY_v2;
    
/*! @abstract called to read more pixel data from your data store
 *  @discussion  The callback will not be called until after your application calls axr_encoder_compress
 *               When possible, the data copied to buf should be materialized  lazily to avoid using too much memory.
 *               The encoder will always ask for a full tile sized piece of data, even when a full tile is not available,
 *               This can happen when the image is not a multiple of the tile size. Your application should fill the
 *               right and bottom edges of the image, as needed, with extra padding to fill out these regions.
 *               The data will actually be encoded into the file, so it would be best to either zero the extra regions
 *               or fill them with the nearest valid pixel value.  Please see axr_part_get_tile_size to get
 *               the expected tile size for the part.
 *  @param buf    The location to copy the data to
 *  @param size   The number of bytes to copy
 *  @param levelIndex   This is the mip level. If it is a rip level, then ripLevelX = levelIndex % ripLevelCountX.  ripLevelY = levelIndex / ripLevelCountX.
 *  @param levelSize     The size of the current mip level. For mip level index 0, this is the full image size.
 *  @param userInfo  The userInfo pointer passed to axr_data_provider_create. This is your object. AppleEXR doesn't know what it is.
 *  @return  The number of bytes copied in.   If there is an error, return one of the following error codes:
 *              0:   The read operation is suspended. Do no further encoding.
 *              -1:  There is no more data
 *              -2:  An unrecoverable error occurred. */
typedef long (*axr_data_provider_read_t)( void * __nonnull buf,
                                          size_t size,
                                          unsigned long levelIndex,
                                          axr_size_t levelSize,
                                          uintptr_t userInfo ) AXR_AVAILABILITY_v1;

/*! @abstract   Called when the encode operation described by axr_part_append_channels is finished.
 *  @discussion This provides you with an opportunity to destroy or release storage used during encode operations.
 *              The callback will not be called until sometime after your application calls axr_encoder_compress
 *  @param userInfo  The userInfo pointer passed to axr_data_provider_create. This is your object. AppleEXR doesn't know what it is. */
typedef void (*axr_data_provider_release_t)( uintptr_t userInfo ) AXR_AVAILABILITY_v1;
    

    
/*! @abstract Append channels to the part
 *  @discussion You may append channels in sets of 1, 2 or 4.  Channels added together will appear
 *              consecutively in the file.  Note that EXR files with too many channels per part are
 *              inefficient to read.  Consider making a file with just a few channels per part and many parts.
 *              Channels that are read together must be stored together in the same part and should
 *              share the same layerName.  If adding more than one channel at a time with axr_part_append_channels
 *              your application will later be asked to produce the channels together as a single pixel buffer,
 *              with channels interleaved as {RGBArgbaRGBArgba...} rather than {RRRRR...gggggg...BBBBBB...aaaaa...}
 *              If your content is planar in nature, add the channels one at a time.
 *
 *              If multiple calls to axr_part_append_channels are used to encode a part, or there are multiple parts,
 *              the encoder may work on multiple parts or channel groups concurrently on different threads at the same
 *              time. Your readFunc/releaseFunc callbacks must take appropriate steps to ensure this does
 *              not cause problems for your internal application state. If this can not be accomplished, you may
 *              pass axr_flags_disable_multithreading to axr_encoder_compress to force single threaded operation.
 *              A significant performance penalty may apply in such cases, so it is preferred that your application strive
 *              toward thread safety.  Data access within a particular channel group (as delineated by each separate call to
 *              axr_part_append_channels) is serialized.
 *
 *  @memberof axr_part_t
 *  @param   part  The part to append the channels
 *  @param   layerName  A name prefix to prepend to the channels to help group them.  The
 *                     name may be reused for multiple calls to axr_part_append_channels
 *                     with the same part. The channels will all appear to be part of the same
 *                     layer.
 *  @param   channelType  The data type shared by the channels appended by this call
 *  @param   xSampling      The sampling rate in the x dimension.        Values > 1 indicate downsampled channels
 *  @param   ySampling      The sampling rate in the y dimension.        Values > 1 indicate downsampled channels
 *  @param   perceptuallyLinear   True indicates the data is perceptually linear, such as L*a*b* / YCbCr chroma or hue.  Unless you are sure, false is recommended.
 *  @param   channelList   A pointer to a flat array of axr_channel_type_t. The number of elements in the array is given by channel count
 *                       The ordering of the channels should match the ordering of the channels in the interleaved pixel buffer in memory.
 *  @param   channelNames An optional list of optional names. If the name is unavailable, AppleEXR will use the default name for the channel type
 *                        given by the channelList parameter. Channel names are required for  axr_channel_type_unknown channels.
 *  @param   channelCount  The number of channels to be added together. Must be 1, 2, or 4
 *  @param   readFunc        This is called to get the next N bytes of the input source.  It should be a 1, 2, or 4 channel interleaved image with
 *                       properties conforming to those above
 *  @param   releaseFunc  This is called when the encoder is done with the pixel data in the image. Your application should release
 *                       storage associated with the pixel buffer and the write operation at this time.
 *  @param   userInfo         an optional extra piece of application state. It is passed unmodified to readFunc and doneFunc. This can be a selector,
 *                       an opaque pointer to your object, a file handle, etc.  AppleEXR doesn't know this integer is and does not try to interact
 *                       with it.  If the same object can be used by multiple encoders concurrently, your application must take steps to make sure
 *                       its operation is thread safe. For example, there should likely be a separate copy of the buffer position offset implicitly
 *                       used by readFunc per instance. */
void axr_part_append_channels( __nonnull axr_part_t part,
                               const char * __nullable layerName,
                               axr_type_t channelType,
                               uint32_t xSampling,
                               uint32_t ySampling,
                               bool perceptuallyLinear,
                               const axr_channel_type_t channelList[__nonnull],
                               const char * __nullable channelNames[__nullable],
                               unsigned long channelCount,
                               axr_data_provider_read_t __nonnull readFunc,
                               axr_data_provider_release_t __nullable releaseFunc,
                               uintptr_t userInfo );
                             
    
/*! @abstract   AppleEXR doesn't know what this is.  This is a pointer to your storage object that will be handed back to you as needed */
typedef void * axr_encoder_storage_t;
    
/*! @abstract Move the current file position to byte_position bytes from the start of the file
 *  @discussion This will be called with offset 0 once before any writes are performed, and then as needed after that.
 *  @param storage                Your storage object
 *  @param bytePosition     This is the byte offset into the storage. If axr_encoder_seek_end is passed, the byte after the last byte written to the file is desired (e.g. fseek(file, 0, SEEK_END))
 *  @return return 0 for success, non-zero for failure  */
typedef long (*axr_encoder_seek_proc_t)( __nullable axr_encoder_storage_t storage, unsigned long bytePosition );
static const unsigned long axr_encoder_seek_end = -1UL;

/*! @abstract write bytes to the storage starting at the current file location and extending size bytes
 *  @param storage  A pointer to your storage object
 *  @param bytes      A pointer to the start of the byte stream to write
 *  @param  size     The number of bytes in the region of memory pointed to by bytes
 *  @return  The number of bytes written, or a negative number if an error occurred*/
typedef long (*axr_encoder_write_proc_t)( __nullable axr_encoder_storage_t storage, const uint8_t * __nonnull bytes, size_t size  );
    
/*! @abstract Called once  when the encoding is done.
 *  @param storage      A pointer to your storage object.  It should be freed or released as appropriate. */
typedef void (*axr_encoder_storage_destroy_proc_t)( __nullable axr_encoder_storage_t storage );

/*! @abstract Serialize the axr_encoder_t to a stream of bytes decodable as a OpenEXR file
 *  @discussion  if no seekProc is provided, AppleEXR will have to compress the entire file before any of it can be written.  This will
 *               result in much, much more memory usage.
 *
 *               Each tile EXR file contains all planes of the part. As AppleEXR encodes the tile, it will read a tile row sized
 *               chunk of pixels from each of your layer data providers in turn. This will cause all of your layer data providers
 *               to be open concurrently. They should attempt to minimize the amount of persistent storage they keep around
 *               between calls, if possible. 
 *
 *               *** Once axr_encoder_compress() returns, the object can not be reused. It should be released. ***
 *               This is because the axr_user_data_destructor_ts have been called and your resources no longer available.
 *               Since the encoder consumes itself as part of the compression operation (releases your resources to save
 *               memory), it should not be called reentrantly. You may call it from multiple threads as long as care is taken
 *               to protect the object with appropriate locks. While the object is likely to be internally multithreaded, calls to
 *               your applications storage object will be serialized.
 *
 *               Flags:
 *                  axr_flags_disable_multithreading — Pass this flag to force the compressor to run synchronously.
 *  @memberof axr_encoder_t
 *  @param encoder           A valid AppleEXR encoder
 *  @param storage           A pointer to your storage object.  AppleEXR doesn't know what this object is and will interact with it only through writeProc, seekProc, and destroyProc
 *  @param writeProc       A pointer to a function to write bytes to the storage
 *  @param seekProc         A optional pointer to a function to move the storage index around. If NULL, encoding will consume much more system memory.
 *  @param destroyProc  An optional pointer to a function to destroy or release the storage object
 *  @param flags            Flags to control the operation of axr_encoder_compress
 *  @return  On success, axr_error_success.  Otherwise, an error code is returned */
axr_error_t axr_encoder_compress( __nonnull axr_encoder_t encoder,
                                  __nonnull axr_encoder_storage_t storage,
                                  __nonnull axr_encoder_write_proc_t writeProc,
                                  __nullable axr_encoder_seek_proc_t seekProc,
                                  __nullable axr_encoder_storage_destroy_proc_t destroyProc,
                                  axr_flags_t flags ) AXR_AVAILABILITY_v1;
    
#pragma mark -
    
    /*!
     *  @class      axr_logical_image_list_t
     *  A representation of channel groupings into images in a axr_data_t
     *  The axr_logical_images_t defines an interface for an opaque object that can
     *  automatically associate EXR channels together into logical images, according
     *  to known EXR naming conventions.  Note: this method preferentially attempts to
     *  decode images belonging to tristimulus colorspaces such as RGB, XYZ, YCbCr
     *  and as a fallback grayscale.  There are other methods of associating channels
     *  such as that described in Fichet, Pacanowski, Wilkie, J. Computer Graphics
     *  Techniques 10(3), 2021, for spectral images which are not intended to be supported
     *  by this convenience method. Channels that do not conform to a CGColorSpaceModel
     *  will be reported as false color monochrome images, tagged as kCGColorSpaceModelUnknown.
     *  The kCGColorSpaceExtendedLinearGray colorspace is appropriate for these, though
     *  in some cases the content may well exceed the gamut that typically appears on screan,
     *  leading to oversaturated or very dark false color images.
     *
     *  This is a subclass of os_object_t and supports Automatic Reference Counting (ARC).
     *  If this feature is not enabled in your build, you must use os_retain() and os_release()
     *  explicitly to avoid leaking memory.
     *
     *  Note: The naming attached to logical images is intended to be human readable.
     *  A variety of fallback mechanisms are at play to try to make sure images have
     *  names, even when the name is missing or the EXR channel naming conventions
     *  (where the names come from) specifically prescribe the omission of image names
     *  for default images.  Because of the heterogeneity of methods used, it may be difficult
     *  to reliably interpret the names presented in a way that makes sense to straight-forward
     *  code in a programming language.  For machine use, the names attached to axr_channel_info_t,
     *  may be somewhat more regular.  There will also remain cases in which the logical image
     *  has no name. In such cases, the name might be inferred fro the view name (if only
     *  image in the view) , the colorspace or the file name. EXR docs specifically disrecommend
     *  trying to infer the name from the file name, but there are times when provenance is
     *  paramount..
     *
     *  Conforms to NSSecureCoding, -isEqual, -debugDescription -description.
     *  The object is immutable once created and is thread safe. */
    #if OS_OBJECT_USE_OBJC && ! defined(DOXYGEN)
        OS_OBJECT_DECL(axr_logical_image_list);
    #else
        typedef struct axr_logical_image_list *  axr_logical_image_list_t;
    #endif
    
    /*! @abstract Create a logical image list from a axr_data container */
    __nonnull axr_logical_image_list_t axr_data_create_logical_image_list( __nonnull axr_data_t data) AXR_AVAILABILITY_v3;
    
    /*! @abstract Get the number of images in a logical image list */
    unsigned long axr_logical_image_list_get_view_count( __nullable axr_logical_image_list_t list ) AXR_AVAILABILITY_v3;

    /*! @abstract Get the index of the default view
     *  @return  The index of the default view, or -1 if there are no images */
    long axr_logical_image_list_get_default_view_index( __nullable axr_logical_image_list_t list ) AXR_AVAILABILITY_v3;

    /*! @abstract Get the name of the view
     *  @return   A NUL terminated C string. The default view is likely to be called "".  "left" and "right" are common.
     *            The storage for the string is owned by the logical image list and will be destroyed when it is freed. */
    const char * __nonnull axr_logical_image_list_get_view_name( __nullable axr_logical_image_list_t list,
                                                                 unsigned long viewIndex ) AXR_RETURNS_INNER_POINTER AXR_AVAILABILITY_v3;
    
    
    /*! @abstract Get the number of images in a logical image view
     *  @param list                 The logical image list to use
     *  @param viewIndex       The index of the view. See axr_logical_image_list_get_view_count/name, axr_logical_image_list_get_default_view_index
     *  @return  The number of images in the view */
    unsigned long axr_logical_image_list_get_image_count( __nullable axr_logical_image_list_t list,
                                                          unsigned long viewIndex ) AXR_AVAILABILITY_v3;

    /*! @abstract Get the index of the default image
     *  @param list                  The logical image list to use
     *  @param viewIndex       The index of the view. See axr_logical_image_list_get_view_count/name, axr_logical_image_list_get_default_view_index
     *  @return  The index of the default image in the view */
    unsigned long axr_logical_image_list_get_default_image_index( __nullable axr_logical_image_list_t list,
                                                                  unsigned long viewIndex ) AXR_AVAILABILITY_v3;

    /*! @abstract Get the human readable name of the logical image (not localized)
     *  @discussion  The name of an image may commonly be "" for the default image. In such cases,
     *               the layer, channel, part name or view attribute may be substituted if appropriate,
     *               though "" is still a possible result. For deeply hierarchical files, there may be multiple
     *               names as name1.name2.name3...  that may imply some higher level of organization
     *               between images. (OpenEXR allows unlimited freedom here making this task challenging.
     *               The raw name for each channel is given by the axr channel info as amended by part attributes.
     *  @param list                  The logical image list to use
     *  @param viewIndex       The index of the view. See axr_logical_image_list_get_view_count/name, axr_logical_image_list_get_default_view_index
     *  @param imageIndex     The index of the image within the view to use
     *  @return   A NUL terminated C string. The default image is likely to be called "".
     *            The storage for the string is owned by the logical image list and will be destroyed when it is freed. */
    const char * __nonnull axr_logical_image_list_get_image_name( __nullable axr_logical_image_list_t list,
                                                                  unsigned long viewIndex,
                                                                  unsigned long imageIndex ) AXR_RETURNS_INNER_POINTER AXR_AVAILABILITY_v3;

    /*! @abstract Commonly used information about logical images
     *  @discussion The lion's share of the metadata for a image may be
     *              contained in the attributes for the part that contains the image.
     *              There may for example be a rgba image preview, timestamp information,
     *              geolocation data, etc.
     *  @seealso  axr_data_get_part_info and axr_data_get_property */
    typedef struct axr_logical_image_info_t
    {
        uint64_t               channelMask;             /// A mask of channel types present:  (1ULL << axr_channel_type_t)
        axr_size_t             size;                    /// The size of the image data window. Note: actual size of some channels may be smaller due to chroma subsampling. 
        axr_type_t             type;                    /// The data type for the image
        uint32_t               partIndex;               /// The index of the EXR part that the image belongs to. See also axr_data_get_part_info
        uint32_t               layerIndex;              /// The index of the layer in the part.
        int32_t                cgColorSpaceModel;       /// The CGColorSpaceModel
        uint32_t               xChromaSampling;         /// The sampling rate in horizontal dimension for CbCr channels. 0 if no CbCr. Subsampled width is (size.width + xChromaSampling - 1) / xChromaSampling
        uint32_t               yChromaSampling;         /// The sampling rate in vertical dimension for CbCr channels.  0 if no CbCr. Subsampled height is (size.height + yChromaSampling - 1) / yChromaSampling
        uint32_t               channelCount;            /// number of channels encoded in the file for image
        uint32_t               channels[4];             /// Channel indices. Channels are in color model order (or YCbCr) or alphabetical if no color model. Alpha, if any, is last.
    }axr_logical_image_info_t;
    typedef enum  { axr_logical_image_info_current = 1 } axr_logical_image_info_struct_version_t;

    /*! @abstract Get information about a logical image
     *  @param list               The logical image list from which to get info
     *  @param viewIndex    The index of the view containing the logical image.
     *  @param imageIndex  The index of the image within the view.
     *  @param version         Pass  axr_logical_image_info_current here if required.
     *  @return A axr_logical_image_info_t for the image. */
    axr_logical_image_info_t axr_logical_image_list_get_image_info( __nullable axr_logical_image_list_t list,
                                                                    unsigned long viewIndex,
                                                                    unsigned long imageIndex,
                                                                    axr_logical_image_info_struct_version_t version _AXR_DEFAULT(axr_logical_image_info_current) ) AXR_AVAILABILITY_v3;


    /*! @abstract Convenience method to make a decoder for a  logical image
     *  @seealso  To make one by hand see axr logical image list get image info
     *  @discussion  This is a convenience function intended to produce interleaved
     *               data for use with common image formats and color spaces.
     *               Behavior is as follows:
     *
     *               image channelCount           alpha         Behavior
     *               ===================================
     *                      0                           any            There should be no axr_logical_image_list_t image like this. File a bug.
     *                      1                           NaN          1 channel decoder
     *                      1                         numeric       2 channel decoder with constant alpha, first or last
     *                      2                           NaN           2 channel decoder
     *                      2                          numeric      4 channel decoder, a false color rgba image as either Arg0 or rg0A with constant alpha and constant blue channel with value 0
     *                      3                           NaN           3 channel decoder      (CAUTION: not supported by axr_decoder_read_pixels)
     *                      3                           numeric     4 channel decoder, with constant alpha, first or last
     *                      4                            NaN          4 channel decoder
     *                      4                           numeric     4 channel decoder, attempt to append constant alpha ignored
     *                      >4                          any          There should be no axr_logical_image_list_t image like this. File a bug.
     *
     *                This function does not well support some downsampled output cases like 422 or 420 split biplanar YCbCr.
     *                In such cases, your application should make its own decoders using the channel indices in the
     *                 axr_logical_image_info_t.channels_ field.
     *
     *  @param  list    The image list to draw from
     *  @param  viewIndex The index of the view
     *  @param  imageIndex The index of the image in the view
     *  @param  flags            AXR flags to use when creating the decoder
     *  @param  alpha           The value [0,1] to use for any extra constant value alpha channels. Pass NAN for no additional alpha channel.
     *  @param  isAlphaFirst     if a  constant value alpha channel is added and this is true, it will appear before the color channels
     *                        If it is false, the constant value alpha channel (if any) is added after the color channels
     *  @return A valid axr_decoder_t if the call succeeded. NULL otherwise. */
    __nullable axr_decoder_t axr_logical_image_list_create_decoder( __nullable axr_logical_image_list_t list,
                                                                   __nonnull axr_data_t data,
                                                                   unsigned long viewIndex,
                                                                   unsigned long imageIndex,
                                                                   double alpha _AXR_DEFAULT(NAN),
                                                                   bool isAlphaFirst _AXR_DEFAULT(false),
                                                                   unsigned long levelIndex _AXR_DEFAULT(0),
                                                                   axr_flags_t   flags _AXR_DEFAULT_FLAGS) AXR_AVAILABILITY_v3;
    
    
    /*! @abstract return true if two logical image lists are equal
     *  @discuassion if one or both are NULL, the result is false
     *  @param list     The first logical image list
     *  @param list2    The second logical image list
     *  @return true if the logical images are equal and neither is NULL */
    bool axr_logical_image_list_is_equal( __nullable axr_logical_image_list_t list,
                                     __nullable axr_logical_image_list_t list2) AXR_AVAILABILITY_v3;
    
    /*! @abstract return the size of a serialized image list in bytes */
    size_t axr_logical_image_list_get_serialized_size( __nullable axr_logical_image_list_t list ) AXR_AVAILABILITY_v3;
     
    /*! @abstract Serialize a logical image list
     *  @param list     The axr_logical_image_list_t
     *  @param where    A buffer to write to of at least the size returned by axr_logical_image_get_serialized_size */
    size_t axr_logical_image_list_serialize( __nullable axr_logical_image_list_t list,
                                             void * __nonnull where,
                                             size_t size) AXR_AVAILABILITY_v3;
    
    /*! @abstract Deserialize a logical image size
     *  @param where    A pointer to a pointer to  bytes to read to deserialize the object. On return points to the first byte unread.
     *  @param size      A pointer to the size of the maximum bytes to read (can be file size). On return points to the size remaining.
     *  @param flags   flags for controlling output when deserializing
     *  @outErr             If not NULL, a error code will be written here to indicate success or failure
     *  @return         A valid axr_logical_image_list on success, otherwise NULL */
    __nullable axr_logical_image_list_t axr_logical_image_list_deserialize( const void * __nonnull * __nonnull where,
                                                                       size_t * __nonnull size,
                                                                       axr_error_t * __nullable outErr,
                                                                       axr_flags_t flags _AXR_DEFAULT_FLAGS ) AXR_AVAILABILITY_v3;
    
    
#if defined __cplusplus
    }
#endif

#endif /* AppleEXR_h */
