aboutsummaryrefslogtreecommitdiff
path: root/vendor/OpenEXRCore/exr_context.odin
blob: b8d632176a381d0e5eee4af306f3a772bbdac37c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
package vendor_openexr

import "core:c"

#assert(size_of(c.int) == size_of(b32))

context_t       :: distinct rawptr
const_context_t :: context_t

/**
 * @defgroup ContextFunctions OpenEXR Context Stream/File Functions
 *
 * @brief These are a group of function interfaces used to customize
 * the error handling, memory allocations, or I/O behavior of an
 * OpenEXR context.
 *
 * @{
 */

/** @brief Stream error notifier
 *
 *  This function pointer is provided to the stream functions by the
 *  library such that they can provide a nice error message to the
 *  user during stream operations.
 */
stream_error_func_ptr_t :: proc "c" (ctxt: const_context_t, code: result_t, fmt: cstring, #c_vararg args: ..any) -> result_t

/** @brief Error callback function
 *
 *  Because a file can be read from using many threads at once, it is
 *  difficult to store an error message for later retrieval. As such,
 *  when a file is constructed, a callback function can be provided
 *  which delivers an error message for the calling application to
 *  handle. This will then be delivered on the same thread causing the
 *  error.
 */
error_handler_cb_t :: proc "c" (ctxt: const_context_t, code: result_t, msg: cstring)

/** Destroy custom stream function pointer
 *
 *  Generic callback to clean up user data for custom streams.
 *  This is called when the file is closed and expected not to
 *  error.
 *
 *  @param failed Indicates the write operation failed, the
 *                implementor may wish to cleanup temporary files
 */
destroy_stream_func_ptr_t :: proc "c" (ctxt: const_context_t, userdata: rawptr, failed: c.int)

/** Query stream size function pointer
 *
 * Used to query the size of the file, or amount of data representing
 * the openexr file in the data stream.
 *
 * This is used to validate requests against the file. If the size is
 * unavailable, return -1, which will disable these validation steps
 * for this file, although appropriate memory safeguards must be in
 * place in the calling application.
 */
query_size_func_ptr_t :: proc "c" (ctxt: const_context_t, userdata: rawptr) -> i64

/** @brief Read custom function pointer
 *
 * Used to read data from a custom output. Expects similar semantics to
 * pread or ReadFile with overlapped data under win32.
 *
 * It is required that this provides thread-safe concurrent access to
 * the same file. If the stream/input layer you are providing does
 * not have this guarantee, your are responsible for providing
 * appropriate serialization of requests.
 *
 * A file should be expected to be accessed in the following pattern:
 *  - upon open, the header and part information attributes will be read
 *  - upon the first image read request, the offset tables will be read
 *    multiple threads accessing this concurrently may actually read
 *    these values at the same time
 *  - chunks can then be read in any order as preferred by the
 *    application
 *
 * While this should mean that the header will be read in 'stream'
 * order (no seeks required), no guarantee is made beyond that to
 * retrieve image/deep data in order. So if the backing file is
 * truly a stream, it is up to the provider to implement appropriate
 * caching of data to give the appearance of being able to seek/read
 * atomically.
 */
read_func_ptr_t :: proc "c" (
	ctxt:     const_context_t,
	userdata: rawptr,
	buffer:   rawptr,
	sz:       u64,
	offset:   u64,
	error_cb: stream_error_func_ptr_t) -> i64

/** Write custom function pointer
 *
 *  Used to write data to a custom output. Expects similar semantics to
 *  pwrite or WriteFile with overlapped data under win32.
 *
 *  It is required that this provides thread-safe concurrent access to
 *  the same file. While it is unlikely that multiple threads will
 *  be used to write data for compressed forms, it is possible.
 *
 *  A file should be expected to be accessed in the following pattern:
 *  - upon open, the header and part information attributes is constructed.
 *
 *  - when the write_header routine is called, the header becomes immutable
 *    and is written to the file. This computes the space to store the chunk
 *    offsets, but does not yet write the values.
 *
 *  - Image chunks are written to the file, and appear in the order
 *    they are written, not in the ordering that is required by the
 *    chunk offset table (unless written in that order). This may vary
 *    slightly if the size of the chunks is not directly known and
 *    tight packing of data is necessary.
 *
 *  - at file close, the chunk offset tables are written to the file.
 */
write_func_ptr_t :: proc "c" (
	ctxt:         const_context_t,
	userdata:     rawptr,
	buffer:       rawptr,
	sz:           u64,
	offset:       u64,
	error_cb:     stream_error_func_ptr_t) -> i64

/** @brief Struct used to pass function pointers into the context
 * initialization routines.
 *
 * This partly exists to avoid the chicken and egg issue around
 * creating the storage needed for the context on systems which want
 * to override the malloc/free routines.
 *
 * However, it also serves to make a tidier/simpler set of functions
 * to create and start processing exr files.
 *
 * The size member is required for version portability.
 *
 * It can be initialized using \c EXR_DEFAULT_CONTEXT_INITIALIZER.
 *
 * \code{.c}
 * exr_context_initializer_t myctxtinit = DEFAULT_CONTEXT_INITIALIZER;
 * myctxtinit.error_cb = &my_super_cool_error_callback_function;
 * ...
 * \endcode
 *
 */
context_initializer_t :: struct {
	/** @brief Size member to tag initializer for version stability.
	 *
	 * This should be initialized to the size of the current
	 * structure. This allows EXR to add functions or other
	 * initializers in the future, and retain version compatibility
	 */
	size: c.size_t,

	/** @brief Error callback function pointer
	 *
	 * The error callback is allowed to be `NULL`, and will use a
	 * default print which outputs to \c stderr.
	 *
	 * @sa exr_error_handler_cb_t
	 */
	error_handler_fn: error_handler_cb_t,

	/** Custom allocator, if `NULL`, will use malloc. @sa memory_allocation_func_t */
	alloc_fn:         memory_allocation_func_t,

	/** Custom deallocator, if `NULL`, will use free. @sa memory_free_func_t */
	free_fn:          memory_free_func_t,

	/** Blind data passed to custom read, size, write, destroy
	 * functions below. Up to user to manage this pointer.
	 */
	user_data: rawptr,

	/** @brief Custom read routine.
	 *
	 * This is only used during read or update contexts. If this is
	 * provided, it is expected that the caller has previously made
	 * the stream available, and placed whatever stream/file data
	 * into \c user_data above.
	 *
	 * If this is `NULL`, and the context requested is for reading an
	 * exr file, an internal implementation is provided for reading
	 * from normal filesystem files, and the filename provided is
	 * attempted to be opened as such.
	 *
	 * Expected to be `NULL` for a write-only operation, but is ignored
	 * if it is provided.
	 *
	 * For update contexts, both read and write functions must be
	 * provided if either is.
	 *
	 * @sa exr_read_func_ptr_t
	 */
	read_fn: read_func_ptr_t,

	/** @brief Custom size query routine.
	 *
	 * Used to provide validation when reading header values. If this
	 * is not provided, but a custom read routine is provided, this
	 * will disable some of the validation checks when parsing the
	 * image header.
	 *
	 * Expected to be `NULL` for a write-only operation, but is ignored
	 * if it is provided.
	 *
	 * @sa exr_query_size_func_ptr_t
	 */
	size_fn: query_size_func_ptr_t,

	/** @brief Custom write routine.
	 *
	 * This is only used during write or update contexts. If this is
	 * provided, it is expected that the caller has previously made
	 * the stream available, and placed whatever stream/file data
	 * into \c user_data above.
	 *
	 * If this is `NULL`, and the context requested is for writing an
	 * exr file, an internal implementation is provided for reading
	 * from normal filesystem files, and the filename provided is
	 * attempted to be opened as such.
	 *
	 * For update contexts, both read and write functions must be
	 * provided if either is.
	 *
	 * @sa exr_write_func_ptr_t
	 */
	write_fn: write_func_ptr_t,

	/** @brief Optional function to destroy the user data block of a custom stream.
	 *
	 * Allows one to free any user allocated data, and close any handles.
	 *
	 * @sa exr_destroy_stream_func_ptr_t
	 * */
	destroy_fn: destroy_stream_func_ptr_t,

	/** Initialize a field specifying what the maximum image width
	 * allowed by the context is. See exr_set_default_maximum_image_size() to
	 * understand how this interacts with global defaults.
	 */
	max_image_width: c.int,

	/** Initialize a field specifying what the maximum image height
	 * allowed by the context is. See exr_set_default_maximum_image_size() to
	 * understand how this interacts with global defaults.
	 */
	max_image_height: c.int,

	/** Initialize a field specifying what the maximum tile width
	 * allowed by the context is. See exr_set_default_maximum_tile_size() to
	 * understand how this interacts with global defaults.
	 */
	max_tile_width: c.int,

	/** Initialize a field specifying what the maximum tile height
	 * allowed by the context is. See exr_set_default_maximum_tile_size() to
	 * understand how this interacts with global defaults.
	 */
	max_tile_height: c.int,

	/** Initialize a field specifying what the default zip compression level should be
	 * for this context. See exr_set_default_zip_compresion_level() to
	 * set it for all contexts.
	 */
	zip_level: c.int,

	/** Initialize the default dwa compression quality. See
	 * exr_set_default_dwa_compression_quality() to set the default
	 * for all contexts.
	 */
	dwa_quality: f32,

	/** Initialize with a bitwise or of the various context flags
	 */
	flags: c.int,

	pad: [4]u8,
}

/** @brief context flag which will enforce strict header validation
 * checks and may prevent reading of files which could otherwise be
 * processed.
 */
CONTEXT_FLAG_STRICT_HEADER :: (1 << 0)

/** @brief Disables error messages while parsing headers
 *
 * The return values will remain the same, but error reporting will be
 * skipped. This is only valid for reading contexts
 */
CONTEXT_FLAG_SILENT_HEADER_PARSE :: (1 << 1)

/** @brief Disables reconstruction logic upon corrupt / missing data chunks
 *
 * This will disable the reconstruction logic that searches through an
 * incomplete file, and will instead just return errors at read
 * time. This is only valid for reading contexts
 */
CONTEXT_FLAG_DISABLE_CHUNK_RECONSTRUCTION :: (1 << 2)

/** @brief Simple macro to initialize the context initializer with default values. */
DEFAULT_CONTEXT_INITIALIZER :: context_initializer_t{zip_level = -2, dwa_quality = -1}

/** @} */ /* context function pointer declarations */


/** @brief Enum describing how default files are handled during write. */
default_write_mode_t :: enum c.int {
	WRITE_FILE_DIRECTLY = 0, /**< Overwrite filename provided directly, deleted upon error. */
	INTERMEDIATE_TEMP_FILE = 1, /**< Create a temporary file, renaming it upon successful write, leaving original upon error */
}


@(link_prefix="exr_", default_calling_convention="c")
foreign lib {
	/** @brief Check the magic number of the file and report
	 * `EXR_ERR_SUCCESS` if the file appears to be a valid file (or at least
	 * has the correct magic number and can be read).
	 */
	test_file_header :: proc(filename: cstring, ctxtdata: ^context_initializer_t) -> result_t ---

	/** @brief Close and free any internally allocated memory,
	 * calling any provided destroy function for custom streams.
	 *
	 * If the file was opened for write, first save the chunk offsets
	 * or any other unwritten data.
	 */
	finish :: proc(ctxt: ^context_t) -> result_t ---

	/** @brief Create and initialize a read-only exr read context.
	 *
	 * If a custom read function is provided, the filename is for
	 * informational purposes only, the system assumes the user has
	 * previously opened a stream, file, or whatever and placed relevant
	 * data in userdata to access that.
	 *
	 * One notable attribute of the context is that once it has been
	 * created and returned a successful code, it has parsed all the
	 * header data. This is done as one step such that it is easier to
	 * provide a safe context for multiple threads to request data from
	 * the same context concurrently.
	 *
	 * Once finished reading data, use exr_finish() to clean up
	 * the context.
	 *
	 * If you have custom I/O requirements, see the initializer context
	 * documentation \ref exr_context_initializer_t. The @p ctxtdata parameter
	 * is optional, if `NULL`, default values will be used.
	 */
	start_read :: proc(
		ctxt:     ^context_t,
		filename: cstring,
		ctxtdata: ^context_initializer_t) -> result_t ---

	/** @brief Create and initialize a write-only context.
	 *
	 * If a custom write function is provided, the filename is for
	 * informational purposes only, and the @p default_mode parameter will be
	 * ignored. As such, the system assumes the user has previously opened
	 * a stream, file, or whatever and placed relevant data in userdata to
	 * access that.
	 *
	 * Multi-Threading: To avoid issues with creating multi-part EXR
	 * files, the library approaches writing as a multi-step process, so
	 * the same concurrent guarantees can not be made for writing a
	 * file. The steps are:
	 *
	 * 1. Context creation (this function)
	 *
	 * 2. Part definition (required attributes and additional metadata)
	 *
	 * 3. Transition to writing data (this "commits" the part definitions,
	 * any changes requested after will result in an error)
	 *
	 * 4. Write part data in sequential order of parts (part<sub>0</sub>
	 * -> part<sub>N-1</sub>).
	 *
	 * 5. Within each part, multiple threads can be encoding and writing
	 * data concurrently. For some EXR part definitions, this may be able
	 * to write data concurrently when it can predict the chunk sizes, or
	 * data is allowed to be padded. For others, it may need to
	 * temporarily cache chunks until the data is received to flush in
	 * order. The concurrency around this is handled by the library
	 *
	 * 6. Once finished writing data, use exr_finish() to clean
	 * up the context, which will flush any unwritten data such as the
	 * final chunk offset tables, and handle the temporary file flags.
	 *
	 * If you have custom I/O requirements, see the initializer context
	 * documentation \ref exr_context_initializer_t. The @p ctxtdata
	 * parameter is optional, if `NULL`, default values will be used.
	 */
	start_write :: proc(
		ctxt:         ^context_t,
		filename:     cstring,
		default_mode: default_write_mode_t,
		ctxtdata:     ^context_initializer_t) -> result_t ---

	/** @brief Create a new context for updating an exr file in place.
	 *
	 * This is a custom mode that allows one to modify the value of a
	 * metadata entry, although not to change the size of the header, or
	 * any of the image data.
	 *
	 * If you have custom I/O requirements, see the initializer context
	 * documentation \ref exr_context_initializer_t. The @p ctxtdata parameter
	 * is optional, if `NULL`, default values will be used.
	 */
	start_inplace_header_update :: proc(
		ctxt:     ^context_t,
		filename: cstring,
		ctxtdata: ^context_initializer_t) -> result_t ---

	/** @brief Create a new context for temporary use in memory.
	*
	* This is a custom mode that does not supporting writing actual image
	* data, but one can create one of these, manipulate attributes,
	* define additional parts, run validation, etc. without any
	* requirement of actual file i/o.
	*
	* Note that this creates an defines an initial part for use, so one
	* can immediately start definining attributes into part index 0.
	*
	* See the initializer context documentation \ref
	* exr_context_initializer_t to be able to provide allocation
	* overrides or other controls. The @p ctxtdata parameter is optional,
	* if `NULL`, default values will be used.
	*/
	start_temporary_context :: proc(
		ctxt:         ^context_t,
		context_name: [^]c.char,
		ctxtdata:     ^context_initializer_t) -> result_t ---

	/** @brief Retrieve the file name the context is for as provided
	 * during the start routine.
	 *
	 * Do not free the resulting string.
	 */
	get_file_name :: proc(ctxt: const_context_t, name: ^cstring) -> result_t ---

	/** @brief Retrieve the file version and flags the context is for as
	 * parsed during the start routine.
	 */
	get_file_version_and_flags :: proc(ctxt: const_context_t, ver: ^u32) -> result_t ---

	/** @brief Query the user data the context was constructed with. This
	 * is perhaps useful in the error handler callback to jump back into
	 * an object the user controls.
	 */
	get_user_data :: proc(ctxt: const_context_t, userdata: ^rawptr) -> result_t ---

	/** Any opaque attribute data entry of the specified type is tagged
	 * with these functions enabling downstream users to unpack (or pack)
	 * the data.
	 *
	 * The library handles the memory packed data internally, but the
	 * handler is expected to allocate and manage memory for the
	 * *unpacked* buffer (the library will call the destroy function).
	 *
	 * NB: the pack function will be called twice (unless there is a
	 * memory failure), the first with a `NULL` buffer, requesting the
	 * maximum size (or exact size if known) for the packed buffer, then
	 * the second to fill the output packed buffer, at which point the
	 * size can be re-updated to have the final, precise size to put into
	 * the file.
	 */
	register_attr_type_handler :: proc(
		ctxt: context_t,
		type: cstring,
		unpack_func_ptr: proc "c" (
			ctxt:      context_t,
			data:      rawptr,
			attrsize:  i32,
			outsize:   ^i32,
			outbuffer: ^rawptr) -> result_t,
		pack_func_ptr: proc "c" (
			ctxt:      context_t,
			data:      rawptr,
			datasize:  i32,
			outsize:   ^i32,
			outbuffer: rawptr) -> result_t,
		destroy_unpacked_func_ptr: proc "c" (
			ctxt: context_t, data: rawptr, datasize: i32),
	) -> result_t ---

	/** @brief Enable long name support in the output context */

	set_longname_support :: proc(ctxt: context_t, onoff: b32) -> result_t ---

	/** @brief Write the header data.
	 *
	 * Opening a new output file has a small initialization state problem
	 * compared to opening for read/update: we need to enable the user
	 * to specify an arbitrary set of metadata across an arbitrary number
	 * of parts. To avoid having to create the list of parts and entire
	 * metadata up front, prior to calling the above exr_start_write(),
	 * allow the data to be set, then once this is called, it switches
	 * into a mode where the library assumes the data is now valid.
	 *
	 * It will recompute the number of chunks that will be written, and
	 * reset the chunk offsets. If you modify file attributes or part
	 * information after a call to this, it will error.
	 */
	write_header :: proc(ctxt: context_t) -> result_t ---
}