1 /*
2 ** SPDX-License-Identifier: BSD-3-Clause
3 ** Copyright Contributors to the OpenEXR Project.
4 */
5
6 #ifndef OPENEXR_PRIVATE_STRUCTS_H
7 #define OPENEXR_PRIVATE_STRUCTS_H
8
9 #include "internal_attr.h"
10
11 #include <IlmThreadConfig.h>
12
13 #ifdef ILMTHREAD_THREADING_ENABLED
14 # ifdef _WIN32
15 # include <windows.h>
16 # include <synchapi.h>
17 # else
18 # include <pthread.h>
19 # endif
20 #endif
21
22 #include <inttypes.h>
23
24 #ifdef _MSC_VER
25 # ifndef PRId64
26 # define PRId64 "I64d"
27 # endif
28 # ifndef PRIu64
29 # define PRIu64 "I64u"
30 # endif
31 #endif
32
33 /* for testing, we include a bunch of internal stuff into the unit tests which are in c++ */
34 #ifdef __cplusplus
35 # include <atomic>
36 using atomic_uintptr_t = std::atomic_uintptr_t;
37 #else
38 # if defined __has_include
39 # if __has_include(<stdatomic.h>)
40 # define EXR_HAS_STD_ATOMICS 1
41 # endif
42 # endif
43
44 # ifdef EXR_HAS_STD_ATOMICS
45 # include <stdatomic.h>
46 # elif defined(_MSC_VER)
47 /* msvc w/ c11 support is only very new, until we know what the preprocessor checks are, provide defaults */
48 # include <windows.h>
49 /* yeah, yeah, might be a 32-bit pointer, but if we make it the same, we
50 * can write less since we know support is coming (eventually) */
51 typedef uint64_t atomic_uintptr_t;
52 # else
53 # error OS unimplemented support for atomics
54 # endif
55 #endif
56
57 struct _internal_exr_part
58 {
59 int part_index;
60 exr_storage_t
61 storage_mode; /**< Part of the file version flag declaring scanlines or tiled mode */
62
63 exr_attribute_list_t attributes;
64
65 /* required attributes */
66 exr_attribute_t* channels;
67 exr_attribute_t* compression;
68 exr_attribute_t* dataWindow;
69 exr_attribute_t* displayWindow;
70 exr_attribute_t* lineOrder;
71 exr_attribute_t* pixelAspectRatio;
72 exr_attribute_t* screenWindowCenter;
73 exr_attribute_t* screenWindowWidth;
74
75 /** required for tiled files (@see storage_mode) */
76 exr_attribute_t* tiles;
77
78 /** required for deep or multipart files */
79 exr_attribute_t* name;
80 exr_attribute_t* type;
81 exr_attribute_t* version;
82 exr_attribute_t* chunkCount;
83 /* in the file layout doc, but not required any more? */
84 exr_attribute_t* maxSamplesPerPixel;
85
86 /* copy commonly accessed required attributes to local struct memory for quick access */
87 exr_attr_box2i_t data_window;
88 exr_attr_box2i_t display_window;
89 exr_compression_t comp_type;
90 exr_lineorder_t lineorder;
91
92 int32_t zip_compression_level;
93 float dwa_compression_level;
94
95 int32_t num_tile_levels_x;
96 int32_t num_tile_levels_y;
97 int32_t* tile_level_tile_count_x;
98 int32_t* tile_level_tile_count_y;
99 int32_t* tile_level_tile_size_x;
100 int32_t* tile_level_tile_size_y;
101
102 uint64_t unpacked_size_per_chunk;
103 int16_t lines_per_chunk;
104 int16_t chan_has_line_sampling;
105
106 int32_t chunk_count;
107 uint64_t chunk_table_offset;
108 atomic_uintptr_t chunk_table;
109 };
110
111 enum _INTERNAL_EXR_READ_MODE
112 {
113 EXR_MUST_READ_ALL = 0,
114 EXR_ALLOW_SHORT_READ = 1
115 };
116
117 enum _INTERNAL_EXR_CONTEXT_MODE
118 {
119 EXR_CONTEXT_READ = 0,
120 EXR_CONTEXT_WRITE = 1,
121 EXR_CONTEXT_UPDATE_HEADER = 2,
122 EXR_CONTEXT_WRITING_DATA = 3,
123 EXR_CONTEXT_WRITE_FINISHED
124 };
125
126 struct _internal_exr_context
127 {
128 uint8_t mode;
129 uint8_t version;
130 uint8_t max_name_length;
131
132 uint8_t is_singlepart_tiled;
133 uint8_t has_nonimage_data;
134 uint8_t is_multipart;
135
136 uint8_t pad[2];
137
138 exr_attr_string_t filename;
139 exr_attr_string_t tmp_filename;
140
141 exr_result_t (*do_read) (
142 const struct _internal_exr_context* file,
143 void*,
144 uint64_t,
145 uint64_t*,
146 int64_t*,
147 enum _INTERNAL_EXR_READ_MODE);
148 exr_result_t (*do_write) (
149 struct _internal_exr_context* file, const void*, uint64_t, uint64_t*);
150
151 exr_result_t (*standard_error) (
152 const struct _internal_exr_context* ctxt, exr_result_t code);
153 exr_result_t (*report_error) (
154 const struct _internal_exr_context* ctxt,
155 exr_result_t code,
156 const char* msg);
157 exr_result_t (*print_error) (
158 const struct _internal_exr_context* ctxt,
159 exr_result_t code,
160 const char* msg,
161 ...) EXR_PRINTF_FUNC_ATTRIBUTE;
162
163 exr_error_handler_cb_t error_handler_fn;
164
165 exr_memory_allocation_func_t alloc_fn;
166 exr_memory_free_func_t free_fn;
167
168 int max_image_w;
169 int max_image_h;
170 int max_tile_w;
171 int max_tile_h;
172
173 int default_zip_level;
174 float default_dwa_quality;
175
176 void* real_user_data;
177 void* user_data;
178 exr_destroy_stream_func_ptr_t destroy_fn;
179
180 int64_t file_size;
181 exr_read_func_ptr_t read_fn;
182
183 exr_write_func_ptr_t write_fn;
184 /* used when writing under a mutex, is there a better way? */
185 uint64_t output_file_offset;
186 int cur_output_part;
187 int last_output_chunk;
188 int output_chunk_count;
189
190 /** all files have at least one part */
191 int num_parts;
192
193 struct _internal_exr_part first_part;
194 /* cheap array of one */
195 struct _internal_exr_part* init_part;
196 struct _internal_exr_part** parts;
197
198 exr_attribute_list_t custom_handlers;
199
200 /* mostly needed for writing, but used during read to ensure
201 * custom attribute handlers are safe */
202 #ifdef ILMTHREAD_THREADING_ENABLED
203 # ifdef _WIN32
204 CRITICAL_SECTION mutex;
205 # else
206 pthread_mutex_t mutex;
207 # endif
208 #endif
209 };
210
211 #define EXR_CTXT(c) ((struct _internal_exr_context*) (c))
212 #define EXR_CCTXT(c) ((const struct _internal_exr_context*) (c))
213
214 #define EXR_CONST_CAST(t, v) ((t) (uintptr_t) v)
215
216 static inline void
internal_exr_lock(const struct _internal_exr_context * c)217 internal_exr_lock (const struct _internal_exr_context* c)
218 {
219 #ifdef ILMTHREAD_THREADING_ENABLED
220 struct _internal_exr_context* nonc =
221 EXR_CONST_CAST (struct _internal_exr_context*, c);
222 # ifdef _WIN32
223 EnterCriticalSection (&nonc->mutex);
224 # else
225 pthread_mutex_lock (&nonc->mutex);
226 # endif
227 #endif
228 }
229
230 static inline void
internal_exr_unlock(const struct _internal_exr_context * c)231 internal_exr_unlock (const struct _internal_exr_context* c)
232 {
233 #ifdef ILMTHREAD_THREADING_ENABLED
234 struct _internal_exr_context* nonc =
235 EXR_CONST_CAST (struct _internal_exr_context*, c);
236 # ifdef _WIN32
237 LeaveCriticalSection (&nonc->mutex);
238 # else
239 pthread_mutex_unlock (&nonc->mutex);
240 # endif
241 #endif
242 }
243
244 #define EXR_LOCK(c) internal_exr_lock ((const struct _internal_exr_context*) c)
245 #define EXR_UNLOCK(c) \
246 internal_exr_unlock ((const struct _internal_exr_context*) c)
247
248 #define EXR_LOCK_WRITE(c) \
249 if (c->mode == EXR_CONTEXT_WRITE) internal_exr_lock (c)
250
251 #define EXR_UNLOCK_WRITE(c) \
252 if (c->mode == EXR_CONTEXT_WRITE) internal_exr_unlock (c)
253
254 #define EXR_RETURN_WRITE(c) \
255 ((c->mode == EXR_CONTEXT_WRITE) ? internal_exr_unlock (c) : ((void) 0))
256
257 #define EXR_UNLOCK_AND_RETURN_PCTXT(v) ((void) (EXR_UNLOCK (pctxt)), v)
258 #define EXR_UNLOCK_WRITE_AND_RETURN_PCTXT(v) \
259 ((void) (EXR_RETURN_WRITE (pctxt)), v)
260
261 #define INTERN_EXR_PROMOTE_CONTEXT_OR_ERROR(c) \
262 struct _internal_exr_context* pctxt = EXR_CTXT (c); \
263 if (!pctxt) return EXR_ERR_MISSING_CONTEXT_ARG
264
265 #define INTERN_EXR_PROMOTE_CONST_CONTEXT_OR_ERROR(c) \
266 const struct _internal_exr_context* pctxt = EXR_CCTXT (c); \
267 if (!pctxt) return EXR_ERR_MISSING_CONTEXT_ARG
268
269 #define EXR_PROMOTE_LOCKED_CONTEXT_OR_ERROR(c) \
270 INTERN_EXR_PROMOTE_CONTEXT_OR_ERROR (c); \
271 EXR_LOCK (c)
272
273 #define EXR_PROMOTE_CONST_CONTEXT_OR_ERROR(c) \
274 INTERN_EXR_PROMOTE_CONST_CONTEXT_OR_ERROR (c); \
275 EXR_LOCK_WRITE (pctxt)
276
277 #define EXR_PROMOTE_LOCKED_CONTEXT_AND_PART_OR_ERROR(c, pi) \
278 struct _internal_exr_context* pctxt = EXR_CTXT (c); \
279 struct _internal_exr_part* part; \
280 if (!pctxt) return EXR_ERR_MISSING_CONTEXT_ARG; \
281 EXR_LOCK (pctxt); \
282 if (pi < 0 || pi >= pctxt->num_parts) \
283 return ( \
284 (void) (EXR_UNLOCK (pctxt)), \
285 pctxt->print_error ( \
286 pctxt, \
287 EXR_ERR_ARGUMENT_OUT_OF_RANGE, \
288 "Part index (%d) out of range", \
289 pi)); \
290 part = pctxt->parts[pi]
291
292 #define EXR_PROMOTE_CONST_CONTEXT_AND_PART_OR_ERROR(c, pi) \
293 const struct _internal_exr_context* pctxt = EXR_CCTXT (c); \
294 const struct _internal_exr_part* part; \
295 if (!pctxt) return EXR_ERR_MISSING_CONTEXT_ARG; \
296 EXR_LOCK_WRITE (pctxt); \
297 if (pi < 0 || pi >= pctxt->num_parts) \
298 return ( \
299 (void) (EXR_RETURN_WRITE (pctxt)), \
300 pctxt->print_error ( \
301 pctxt, \
302 EXR_ERR_ARGUMENT_OUT_OF_RANGE, \
303 "Part index (%d) out of range", \
304 pi)); \
305 part = pctxt->parts[pi]
306
307 #define EXR_PROMOTE_CONST_CONTEXT_AND_PART_OR_ERROR_NO_LOCK(c, pi) \
308 const struct _internal_exr_context* pctxt = EXR_CCTXT (c); \
309 const struct _internal_exr_part* part; \
310 if (!pctxt) return EXR_ERR_MISSING_CONTEXT_ARG; \
311 if (pi < 0 || pi >= pctxt->num_parts) \
312 return ( \
313 (void) (EXR_RETURN_WRITE (pctxt)), \
314 pctxt->print_error ( \
315 pctxt, \
316 EXR_ERR_ARGUMENT_OUT_OF_RANGE, \
317 "Part index (%d) out of range", \
318 pi)); \
319 part = pctxt->parts[pi]
320
321 #define EXR_PROMOTE_READ_CONST_CONTEXT_AND_PART_OR_ERROR(c, pi) \
322 const struct _internal_exr_context* pctxt = EXR_CCTXT (c); \
323 const struct _internal_exr_part* part; \
324 if (!pctxt) return EXR_ERR_MISSING_CONTEXT_ARG; \
325 if (pctxt->mode != EXR_CONTEXT_READ) \
326 return pctxt->standard_error (pctxt, EXR_ERR_NOT_OPEN_READ); \
327 if (pi < 0 || pi >= pctxt->num_parts) \
328 return pctxt->print_error ( \
329 pctxt, \
330 EXR_ERR_ARGUMENT_OUT_OF_RANGE, \
331 "Part index (%d) out of range", \
332 pi); \
333 part = pctxt->parts[pi]
334
335 void internal_exr_update_default_handlers (exr_context_initializer_t* inits);
336
337 exr_result_t internal_exr_add_part (
338 struct _internal_exr_context*, struct _internal_exr_part**, int* new_index);
339 void internal_exr_revert_add_part (
340 struct _internal_exr_context*, struct _internal_exr_part**, int* new_index);
341
342 exr_result_t internal_exr_alloc_context (
343 struct _internal_exr_context** out,
344 const exr_context_initializer_t* initializers,
345 enum _INTERNAL_EXR_CONTEXT_MODE mode,
346 size_t extra_data);
347 void internal_exr_destroy_context (struct _internal_exr_context* ctxt);
348
349 #endif /* OPENEXR_PRIVATE_STRUCTS_H */
350