1 /*
2 ** SPDX-License-Identifier: BSD-3-Clause
3 ** Copyright Contributors to the OpenEXR Project.
4 */
5
6 #include "openexr_chunkio.h"
7
8 #include "internal_coding.h"
9 #include "internal_structs.h"
10 #include "internal_xdr.h"
11
12 #include <limits.h>
13 #include <string.h>
14
15 /**************************************/
16
17 /* for testing, we include a bunch of internal stuff into the unit tests which are in c++ */
18 #if defined __has_include
19 # if __has_include(<stdatomic.h>)
20 # define EXR_HAS_STD_ATOMICS 1
21 # endif
22 #endif
23
24 #ifdef EXR_HAS_STD_ATOMICS
25 # include <stdatomic.h>
26 #elif defined(_MSC_VER)
27
28 /* msvc w/ c11 support is only very new, until we know what the preprocessor checks are, provide defaults */
29 # include <windows.h>
30
31 # define atomic_load(object) InterlockedOr64 ((int64_t volatile*) object, 0)
32
33 static inline int
atomic_compare_exchange_strong(uint64_t volatile * object,uint64_t * expected,uint64_t desired)34 atomic_compare_exchange_strong (
35 uint64_t volatile* object, uint64_t* expected, uint64_t desired)
36 {
37 uint64_t prev =
38 (uint64_t) InterlockedCompareExchange64 (object, desired, *expected);
39 if (prev == *expected) return 1;
40 *expected = prev;
41 return 0;
42 }
43
44 #else
45 # error OS unimplemented support for atomics
46 #endif
47
48 /**************************************/
49
50 static exr_result_t
extract_chunk_table(const struct _internal_exr_context * ctxt,const struct _internal_exr_part * part,uint64_t ** chunktable,uint64_t * chunkminoffset)51 extract_chunk_table (
52 const struct _internal_exr_context* ctxt,
53 const struct _internal_exr_part* part,
54 uint64_t** chunktable,
55 uint64_t* chunkminoffset)
56 {
57 uint64_t* ctable = NULL;
58 uint64_t chunkoff = part->chunk_table_offset;
59 uint64_t chunkbytes = sizeof (uint64_t) * (uint64_t) part->chunk_count;
60
61 *chunkminoffset = chunkoff + chunkbytes;
62
63 ctable = (uint64_t*) atomic_load (
64 EXR_CONST_CAST (atomic_uintptr_t*, &(part->chunk_table)));
65 if (ctable == NULL)
66 {
67 int64_t nread = 0;
68 uintptr_t eptr = 0, nptr = 0;
69
70 exr_result_t rv;
71
72 if (part->chunk_count <= 0)
73 return ctxt->report_error (
74 ctxt, EXR_ERR_INVALID_ARGUMENT, "Invalid file with no chunks");
75
76 if (ctxt->file_size > 0 &&
77 chunkbytes + chunkoff > (uint64_t) ctxt->file_size)
78 return ctxt->print_error (
79 ctxt,
80 EXR_ERR_INVALID_ARGUMENT,
81 "chunk table size (%" PRIu64 ") too big for file size (%" PRId64
82 ")",
83 chunkbytes,
84 ctxt->file_size);
85
86 ctable = (uint64_t*) ctxt->alloc_fn (chunkbytes);
87 if (ctable == NULL)
88 return ctxt->standard_error (ctxt, EXR_ERR_OUT_OF_MEMORY);
89
90 rv = ctxt->do_read (
91 ctxt, ctable, chunkbytes, &chunkoff, &nread, EXR_MUST_READ_ALL);
92 if (rv != EXR_ERR_SUCCESS)
93 {
94 ctxt->free_fn (ctable);
95 return rv;
96 }
97 priv_to_native64 (ctable, part->chunk_count);
98
99 //EXR_GETFILE(f)->report_error( ctxt, EXR_ERR_UNKNOWN, "TODO: implement reconstructLineOffsets and similar" );
100 nptr = (uintptr_t) ctable;
101 // see if we win or not
102 if (!atomic_compare_exchange_strong (
103 EXR_CONST_CAST (atomic_uintptr_t*, &(part->chunk_table)),
104 &eptr,
105 nptr))
106 {
107 ctxt->free_fn (ctable);
108 ctable = (uint64_t*) eptr;
109 if (ctable == NULL)
110 return ctxt->standard_error (ctxt, EXR_ERR_OUT_OF_MEMORY);
111 }
112 }
113
114 *chunktable = ctable;
115 return EXR_ERR_SUCCESS;
116 }
117
118 /**************************************/
119
120 static exr_result_t
alloc_chunk_table(const struct _internal_exr_context * ctxt,const struct _internal_exr_part * part,uint64_t ** chunktable)121 alloc_chunk_table (
122 const struct _internal_exr_context* ctxt,
123 const struct _internal_exr_part* part,
124 uint64_t** chunktable)
125 {
126 uint64_t* ctable = NULL;
127
128 /* we have the lock, but to access the type, we'll use the atomic function anyway */
129 ctable = (uint64_t*) atomic_load (
130 EXR_CONST_CAST (atomic_uintptr_t*, &(part->chunk_table)));
131 if (ctable == NULL)
132 {
133 uint64_t chunkbytes = sizeof (uint64_t) * (uint64_t) part->chunk_count;
134 uintptr_t eptr = 0, nptr = 0;
135
136 ctable = (uint64_t*) ctxt->alloc_fn (chunkbytes);
137 if (ctable == NULL)
138 return ctxt->standard_error (ctxt, EXR_ERR_OUT_OF_MEMORY);
139 memset (ctable, 0, chunkbytes);
140
141 nptr = (uintptr_t) ctable;
142 if (!atomic_compare_exchange_strong (
143 EXR_CONST_CAST (atomic_uintptr_t*, &(part->chunk_table)),
144 &eptr,
145 nptr))
146 {
147 ctxt->free_fn (ctable);
148 ctable = (uint64_t*) eptr;
149 if (ctable == NULL)
150 return ctxt->standard_error (ctxt, EXR_ERR_OUT_OF_MEMORY);
151 }
152 }
153 *chunktable = ctable;
154 return EXR_ERR_SUCCESS;
155 }
156
157 /**************************************/
158
159 static uint64_t
compute_chunk_unpack_size(int y,int width,int height,int lpc,const struct _internal_exr_part * part)160 compute_chunk_unpack_size (
161 int y,
162 int width,
163 int height,
164 int lpc,
165 const struct _internal_exr_part* part)
166 {
167 uint64_t unpacksize = 0;
168 if (part->chan_has_line_sampling || height != lpc)
169 {
170 const exr_attr_chlist_t* chanlist = part->channels->chlist;
171 for (int c = 0; c < chanlist->num_channels; ++c)
172 {
173 const exr_attr_chlist_entry_t* curc = (chanlist->entries + c);
174 uint64_t chansz = ((curc->pixel_type == EXR_PIXEL_HALF) ? 2 : 4);
175 chansz *= ((uint64_t) width);
176 if (curc->x_sampling > 1) chansz /= ((uint64_t) curc->x_sampling);
177 chansz *= ((uint64_t) height);
178 if (curc->y_sampling > 1)
179 {
180 if (height > 1)
181 {
182 if (curc->y_sampling > height)
183 chansz /= ((uint64_t) height);
184 else
185 chansz /= ((uint64_t) curc->y_sampling);
186 }
187 else if ((y % ((int) curc->y_sampling)) != 0)
188 chansz = 0;
189 }
190 unpacksize += chansz;
191 }
192 }
193 else
194 unpacksize = part->unpacked_size_per_chunk;
195 return unpacksize;
196 }
197
198 /**************************************/
199
200 exr_result_t
exr_read_scanline_chunk_info(exr_const_context_t ctxt,int part_index,int y,exr_chunk_info_t * cinfo)201 exr_read_scanline_chunk_info (
202 exr_const_context_t ctxt, int part_index, int y, exr_chunk_info_t* cinfo)
203 {
204 exr_result_t rv;
205 int miny, cidx, rdcnt, lpc;
206 int32_t data[3];
207 int64_t ddata[3];
208 int64_t fsize;
209 uint64_t chunkmin, dataoff;
210 exr_attr_box2i_t dw;
211 uint64_t* ctable;
212 EXR_PROMOTE_READ_CONST_CONTEXT_AND_PART_OR_ERROR (ctxt, part_index);
213
214 if (!cinfo) return pctxt->standard_error (pctxt, EXR_ERR_INVALID_ARGUMENT);
215
216 if (part->storage_mode == EXR_STORAGE_TILED ||
217 part->storage_mode == EXR_STORAGE_DEEP_TILED)
218 {
219 return pctxt->standard_error (pctxt, EXR_ERR_SCAN_TILE_MIXEDAPI);
220 }
221
222 dw = part->data_window;
223 if (y < dw.min.y || y > dw.max.y)
224 {
225 return pctxt->print_error (
226 pctxt,
227 EXR_ERR_INVALID_ARGUMENT,
228 "Invalid request for scanline %d outside range of data window (%d - %d)",
229 y,
230 dw.min.y,
231 dw.max.y);
232 }
233
234 lpc = part->lines_per_chunk;
235 cidx = (y - dw.min.y);
236 if (lpc > 1) cidx /= lpc;
237
238 // do we need to invert this when reading decreasing y? it appears not
239 //if (part->lineorder == EXR_LINEORDER_DECREASING_Y)
240 // cidx = part->chunk_count - (cidx + 1);
241 miny = (dw.min.y + cidx * lpc);
242
243 if (cidx < 0 || cidx >= part->chunk_count)
244 {
245 return pctxt->print_error (
246 pctxt,
247 EXR_ERR_INVALID_ARGUMENT,
248 "Invalid request for scanline %d in chunk %d outside chunk count %d",
249 y,
250 cidx,
251 part->chunk_count);
252 }
253
254 cinfo->idx = cidx;
255 cinfo->type = (uint8_t) part->storage_mode;
256 cinfo->compression = (uint8_t) part->comp_type;
257 cinfo->start_x = dw.min.x;
258 cinfo->start_y = miny;
259 cinfo->width = dw.max.x - dw.min.x + 1;
260 cinfo->height = lpc;
261 if (miny < dw.min.y)
262 {
263 cinfo->start_y = dw.min.y;
264 cinfo->height -= (dw.min.y - miny);
265 }
266 else if ((miny + lpc) > dw.max.y)
267 {
268 cinfo->height = (dw.max.y - miny + 1);
269 }
270 cinfo->level_x = 0;
271 cinfo->level_y = 0;
272
273 /* need to read from the file to get the packed chunk size */
274 rv = extract_chunk_table (pctxt, part, &ctable, &chunkmin);
275 if (rv != EXR_ERR_SUCCESS) return rv;
276
277 fsize = pctxt->file_size;
278
279 dataoff = ctable[cidx];
280 if (dataoff < chunkmin || (fsize > 0 && dataoff > (uint64_t) fsize))
281 {
282 return pctxt->print_error (
283 pctxt,
284 EXR_ERR_BAD_CHUNK_LEADER,
285 "Corrupt chunk offset table: scanline %d, chunk index %d recorded at file offset %" PRIu64,
286 y,
287 cidx,
288 dataoff);
289 }
290
291 /* multi part files have the part for validation */
292 rdcnt = (pctxt->is_multipart) ? 2 : 1;
293 /* deep has 64-bit data, so be variable about what we read */
294 if (part->storage_mode != EXR_STORAGE_DEEP_SCANLINE) ++rdcnt;
295
296 rv = pctxt->do_read (
297 pctxt,
298 data,
299 (size_t) (rdcnt) * sizeof (int32_t),
300 &dataoff,
301 NULL,
302 EXR_MUST_READ_ALL);
303
304 if (rv != EXR_ERR_SUCCESS) return rv;
305
306 priv_to_native32 (data, rdcnt);
307
308 rdcnt = 0;
309 if (pctxt->is_multipart)
310 {
311 if (data[rdcnt] != part_index)
312 {
313 return pctxt->print_error (
314 pctxt,
315 EXR_ERR_BAD_CHUNK_LEADER,
316 "Preparing read scanline %d (chunk %d), found corrupt leader: part says %d, expected %d",
317 y,
318 cidx,
319 data[rdcnt],
320 part_index);
321 }
322 ++rdcnt;
323 }
324 if (miny != data[rdcnt])
325 {
326 return pctxt->print_error (
327 pctxt,
328 EXR_ERR_BAD_CHUNK_LEADER,
329 "Preparing to read scanline %d (chunk %d), found corrupt leader: scanline says %d, expected %d",
330 y,
331 cidx,
332 data[rdcnt],
333 miny);
334 }
335
336 if (part->storage_mode == EXR_STORAGE_DEEP_SCANLINE)
337 {
338 rv = pctxt->do_read (
339 pctxt,
340 ddata,
341 3 * sizeof (int64_t),
342 &dataoff,
343 NULL,
344 EXR_MUST_READ_ALL);
345 if (rv != EXR_ERR_SUCCESS) { return rv; }
346 priv_to_native64 (ddata, 3);
347
348 if (ddata[0] < 0)
349 {
350 return pctxt->print_error (
351 pctxt,
352 EXR_ERR_BAD_CHUNK_LEADER,
353 "Preparing to read scanline %d (chunk %d), found corrupt leader: invalid sample table size %" PRId64,
354 y,
355 cidx,
356 ddata[0]);
357 }
358 if (ddata[1] < 0 || ddata[1] > (int64_t) INT_MAX)
359 {
360 return pctxt->print_error (
361 pctxt,
362 EXR_ERR_BAD_CHUNK_LEADER,
363 "Preparing to read scanline %d (chunk %d), found corrupt leader: invalid packed data size %" PRId64,
364 y,
365 cidx,
366 ddata[1]);
367 }
368 if (ddata[2] < 0 || ddata[2] > (int64_t) INT_MAX)
369 {
370 return pctxt->print_error (
371 pctxt,
372 EXR_ERR_BAD_CHUNK_LEADER,
373 "Preparing to scanline %d (chunk %d), found corrupt leader: unsupported unpacked data size %" PRId64,
374 y,
375 cidx,
376 ddata[2]);
377 }
378
379 cinfo->sample_count_data_offset = dataoff;
380 cinfo->sample_count_table_size = (uint64_t) ddata[0];
381 cinfo->data_offset = dataoff + (uint64_t) ddata[0];
382 cinfo->packed_size = (uint64_t) ddata[1];
383 cinfo->unpacked_size = (uint64_t) ddata[2];
384
385 if (fsize > 0 &&
386 ((cinfo->sample_count_data_offset +
387 cinfo->sample_count_table_size) > ((uint64_t) fsize) ||
388 (cinfo->data_offset + cinfo->packed_size) > ((uint64_t) fsize)))
389 {
390 return pctxt->print_error (
391 pctxt,
392 EXR_ERR_BAD_CHUNK_LEADER,
393 "Preparing to scanline %d (chunk %d), found corrupt leader: sample table and data result in access past end of the file: sample table size %" PRId64
394 " + data size %" PRId64 " larger than file %" PRId64,
395 y,
396 cidx,
397 ddata[0],
398 ddata[1],
399 fsize);
400 }
401 }
402 else
403 {
404 uint64_t unpacksize = compute_chunk_unpack_size (
405 y, cinfo->width, cinfo->height, lpc, part);
406
407 ++rdcnt;
408 if (data[rdcnt] < 0 ||
409 (uint64_t) data[rdcnt] > part->unpacked_size_per_chunk)
410 {
411 return pctxt->print_error (
412 pctxt,
413 EXR_ERR_BAD_CHUNK_LEADER,
414 "Preparing to read scanline %d (chunk %d), found corrupt leader: packed data size says %" PRIu64
415 ", must be between 0 and %" PRIu64,
416 y,
417 cidx,
418 (uint64_t) data[rdcnt],
419 part->unpacked_size_per_chunk);
420 }
421
422 cinfo->data_offset = dataoff;
423 cinfo->packed_size = (uint64_t) data[rdcnt];
424 cinfo->unpacked_size = unpacksize;
425 cinfo->sample_count_data_offset = 0;
426 cinfo->sample_count_table_size = 0;
427
428 if (fsize > 0 &&
429 (cinfo->data_offset + cinfo->packed_size) > ((uint64_t) fsize))
430 {
431 return pctxt->print_error (
432 pctxt,
433 EXR_ERR_BAD_CHUNK_LEADER,
434 "Preparing to read scanline %d (chunk %d), found corrupt leader: packed size %" PRIu64
435 ", file size %" PRId64,
436 y,
437 cidx,
438 (uint64_t) data[rdcnt],
439 fsize);
440 }
441 }
442
443 if (cinfo->packed_size == 0 && cinfo->unpacked_size > 0)
444 return pctxt->report_error (
445 pctxt,
446 EXR_ERR_INVALID_ARGUMENT,
447 "Invalid packed size of 0");
448 return EXR_ERR_SUCCESS;
449 }
450
451 /**************************************/
452
453 static exr_result_t
compute_tile_chunk_off(const struct _internal_exr_context * ctxt,const struct _internal_exr_part * part,int tilex,int tiley,int levelx,int levely,int32_t * chunkoffout)454 compute_tile_chunk_off (
455 const struct _internal_exr_context* ctxt,
456 const struct _internal_exr_part* part,
457 int tilex,
458 int tiley,
459 int levelx,
460 int levely,
461 int32_t* chunkoffout)
462 {
463 int numx, numy;
464 int64_t chunkoff = 0;
465 const exr_attr_tiledesc_t* tiledesc = part->tiles->tiledesc;
466
467 switch (EXR_GET_TILE_LEVEL_MODE ((*tiledesc)))
468 {
469 case EXR_TILE_ONE_LEVEL:
470 case EXR_TILE_MIPMAP_LEVELS:
471 if (levelx != levely)
472 {
473 return ctxt->print_error (
474 ctxt,
475 EXR_ERR_INVALID_ARGUMENT,
476 "Request for tile (%d, %d) level (%d, %d), but single level and mipmap tiles must have same level x and y",
477 tilex,
478 tiley,
479 levelx,
480 levely);
481 }
482 if (levelx >= part->num_tile_levels_x)
483 {
484 return ctxt->print_error (
485 ctxt,
486 EXR_ERR_INVALID_ARGUMENT,
487 "Request for tile (%d, %d) level %d, but level past available levels (%d)",
488 tilex,
489 tiley,
490 levelx,
491 part->num_tile_levels_x);
492 }
493
494 numx = part->tile_level_tile_count_x[levelx];
495 numy = part->tile_level_tile_count_y[levelx];
496
497 if (tilex >= numx || tiley >= numy)
498 {
499 return ctxt->print_error (
500 ctxt,
501 EXR_ERR_INVALID_ARGUMENT,
502 "Request for tile (%d, %d) level %d, but level only has %d x %d tiles",
503 tilex,
504 tiley,
505 levelx,
506 numx,
507 numy);
508 }
509
510 for (int l = 0; l < levelx; ++l)
511 chunkoff +=
512 ((int64_t) part->tile_level_tile_count_x[l] *
513 (int64_t) part->tile_level_tile_count_y[l]);
514 chunkoff += tiley * numx + tilex;
515 break;
516
517 case EXR_TILE_RIPMAP_LEVELS:
518 if (levelx >= part->num_tile_levels_x)
519 {
520 return ctxt->print_error (
521 ctxt,
522 EXR_ERR_INVALID_ARGUMENT,
523 "Request for tile (%d, %d) level %d, %d, but x level past available levels (%d)",
524 tilex,
525 tiley,
526 levelx,
527 levely,
528 part->num_tile_levels_x);
529 }
530 if (levely >= part->num_tile_levels_y)
531 {
532 return ctxt->print_error (
533 ctxt,
534 EXR_ERR_INVALID_ARGUMENT,
535 "Request for tile (%d, %d) level %d, %d, but y level past available levels (%d)",
536 tilex,
537 tiley,
538 levelx,
539 levely,
540 part->num_tile_levels_y);
541 }
542
543 numx = part->tile_level_tile_count_x[levelx];
544 numy = part->tile_level_tile_count_y[levely];
545
546 if (tilex >= numx || tiley >= numy)
547 {
548 return ctxt->print_error (
549 ctxt,
550 EXR_ERR_INVALID_ARGUMENT,
551 "Request for tile (%d, %d) at rip level %d, %d level only has %d x %d tiles",
552 tilex,
553 tiley,
554 levelx,
555 levely,
556 numx,
557 numy);
558 }
559
560 for (int ly = 0; ly < levely; ++ly)
561 {
562 for (int lx = 0; lx < levelx; ++lx)
563 {
564 chunkoff +=
565 ((int64_t) part->tile_level_tile_count_x[lx] *
566 (int64_t) part->tile_level_tile_count_y[ly]);
567 }
568 }
569 for (int lx = 0; lx < levelx; ++lx)
570 {
571 chunkoff +=
572 ((int64_t) part->tile_level_tile_count_x[lx] *
573 (int64_t) numy);
574 }
575 chunkoff += tiley * numx + tilex;
576 break;
577 case EXR_TILE_LAST_TYPE:
578 default:
579 return ctxt->print_error (
580 ctxt, EXR_ERR_UNKNOWN, "Invalid tile description");
581 }
582
583 if (chunkoff >= part->chunk_count)
584 {
585 return ctxt->print_error (
586 ctxt,
587 EXR_ERR_UNKNOWN,
588 "Invalid tile chunk offset %" PRId64 " (%d avail)",
589 chunkoff,
590 part->chunk_count);
591 }
592
593 *chunkoffout = (int32_t) chunkoff;
594 return EXR_ERR_SUCCESS;
595 }
596
597 /**************************************/
598
599 exr_result_t
exr_read_tile_chunk_info(exr_const_context_t ctxt,int part_index,int tilex,int tiley,int levelx,int levely,exr_chunk_info_t * cinfo)600 exr_read_tile_chunk_info (
601 exr_const_context_t ctxt,
602 int part_index,
603 int tilex,
604 int tiley,
605 int levelx,
606 int levely,
607 exr_chunk_info_t* cinfo)
608 {
609 exr_result_t rv;
610 int32_t data[6];
611 int32_t* tdata;
612 int32_t cidx, ntoread;
613 uint64_t chunkmin, dataoff;
614 int64_t nread, fsize, tend, dend;
615 const exr_attr_chlist_t* chanlist;
616 const exr_attr_tiledesc_t* tiledesc;
617 int tilew, tileh;
618 uint64_t texels, unpacksize = 0;
619 uint64_t* ctable;
620 EXR_PROMOTE_READ_CONST_CONTEXT_AND_PART_OR_ERROR (ctxt, part_index);
621
622 if (!cinfo) return pctxt->standard_error (pctxt, EXR_ERR_INVALID_ARGUMENT);
623
624 if (tilex < 0 || tiley < 0 || levelx < 0 || levely < 0)
625 return pctxt->standard_error (pctxt, EXR_ERR_INVALID_ARGUMENT);
626 if (part->storage_mode == EXR_STORAGE_SCANLINE ||
627 part->storage_mode == EXR_STORAGE_DEEP_SCANLINE)
628 {
629 return pctxt->standard_error (pctxt, EXR_ERR_TILE_SCAN_MIXEDAPI);
630 }
631
632 if (!part->tiles || part->num_tile_levels_x <= 0 ||
633 part->num_tile_levels_y <= 0 || !part->tile_level_tile_count_x ||
634 !part->tile_level_tile_count_y)
635 {
636 return pctxt->print_error (
637 pctxt, EXR_ERR_MISSING_REQ_ATTR, "Tile data missing or corrupt");
638 }
639
640 tiledesc = part->tiles->tiledesc;
641
642 tilew = (int) (tiledesc->x_size);
643 dend = ((int64_t) part->tile_level_tile_size_x[levelx]);
644 tend = ((int64_t) tilew) * ((int64_t) (tilex + 1));
645 if (tend > dend)
646 {
647 tend -= dend;
648 if (tend < tilew) tilew = tilew - ((int) tend);
649 }
650
651 tileh = (int) (tiledesc->y_size);
652 dend = ((int64_t) part->tile_level_tile_size_y[levely]);
653 tend = ((int64_t) tileh) * ((int64_t) (tiley + 1));
654 if (tend > dend)
655 {
656 tend -= dend;
657 if (tend < tileh) tileh = tileh - ((int) tend);
658 }
659
660 cidx = 0;
661 rv = compute_tile_chunk_off (
662 pctxt, part, tilex, tiley, levelx, levely, &cidx);
663 if (rv != EXR_ERR_SUCCESS) return rv;
664
665 cinfo->idx = cidx;
666 cinfo->type = (uint8_t) part->storage_mode;
667 cinfo->compression = (uint8_t) part->comp_type;
668 cinfo->start_x = tilex;
669 cinfo->start_y = tiley;
670 cinfo->height = tileh;
671 cinfo->width = tilew;
672 if (levelx > 255 || levely > 255)
673 return pctxt->print_error (
674 pctxt,
675 EXR_ERR_ATTR_SIZE_MISMATCH,
676 "Unable to represent tile level %d, %d in chunk structure",
677 levelx,
678 levely);
679
680 cinfo->level_x = (uint8_t) levelx;
681 cinfo->level_y = (uint8_t) levely;
682
683 chanlist = part->channels->chlist;
684 texels = (uint64_t) tilew * (uint64_t) tileh;
685 for (int c = 0; c < chanlist->num_channels; ++c)
686 {
687 const exr_attr_chlist_entry_t* curc = (chanlist->entries + c);
688 unpacksize +=
689 texels * (uint64_t) ((curc->pixel_type == EXR_PIXEL_HALF) ? 2 : 4);
690 }
691
692 rv = extract_chunk_table (pctxt, part, &ctable, &chunkmin);
693 if (rv != EXR_ERR_SUCCESS) return rv;
694
695 if (part->storage_mode == EXR_STORAGE_DEEP_TILED)
696 {
697 if (pctxt->is_multipart)
698 ntoread = 5;
699 else
700 ntoread = 4;
701 }
702 else if (pctxt->is_multipart)
703 ntoread = 6;
704 else
705 ntoread = 5;
706
707 fsize = pctxt->file_size;
708
709 dataoff = ctable[cidx];
710 if (dataoff < chunkmin || (fsize > 0 && dataoff > (uint64_t) fsize))
711 {
712 return pctxt->print_error (
713 pctxt,
714 EXR_ERR_BAD_CHUNK_LEADER,
715 "Corrupt chunk offset table: tile (%d, %d), level (%d, %d), chunk index %d recorded at file offset %" PRIu64,
716 tilex,
717 tiley,
718 levelx,
719 levely,
720 cidx,
721 dataoff);
722 }
723
724 rv = pctxt->do_read (
725 pctxt,
726 data,
727 (uint64_t) (ntoread) * sizeof (int32_t),
728 &dataoff,
729 &nread,
730 EXR_MUST_READ_ALL);
731 if (rv != EXR_ERR_SUCCESS)
732 {
733 return pctxt->print_error (
734 pctxt,
735 rv,
736 "Unable to read information block for tile (%d, %d), level (%d, %d): request %" PRIu64
737 " bytes from offset %" PRIu64 ", got %" PRIu64 " bytes",
738 tilex,
739 tiley,
740 levelx,
741 levely,
742 (uint64_t) (ntoread) * sizeof (int32_t),
743 ctable[cidx],
744 (uint64_t) nread);
745 }
746 priv_to_native32 (data, ntoread);
747
748 tdata = data;
749 if (pctxt->is_multipart)
750 {
751 if (part_index != data[0])
752 {
753 return pctxt->print_error (
754 pctxt,
755 EXR_ERR_BAD_CHUNK_LEADER,
756 "Corrupt tile (%d, %d), level (%d, %d) (chunk %d): bad part number (%d, expect %d)",
757 tilex,
758 tiley,
759 levelx,
760 levely,
761 cidx,
762 data[0],
763 part_index);
764 }
765 ++tdata;
766 }
767 if (tdata[0] != tilex)
768 {
769 return pctxt->print_error (
770 pctxt,
771 EXR_ERR_BAD_CHUNK_LEADER,
772 "Corrupt tile (%d, %d), level (%d, %d) (chunk %d): bad tile x coordinate (%d, expect %d)",
773 tilex,
774 tiley,
775 levelx,
776 levely,
777 cidx,
778 tdata[0],
779 tilex);
780 }
781 if (tdata[1] != tiley)
782 {
783 return pctxt->print_error (
784 pctxt,
785 EXR_ERR_BAD_CHUNK_LEADER,
786 "Corrupt tile (%d, %d), level (%d, %d) (chunk %d): bad tile Y coordinate (%d, expect %d)",
787 tilex,
788 tiley,
789 levelx,
790 levely,
791 cidx,
792 tdata[1],
793 tiley);
794 }
795 if (tdata[2] != levelx)
796 {
797 return pctxt->print_error (
798 pctxt,
799 EXR_ERR_BAD_CHUNK_LEADER,
800 "Corrupt tile (%d, %d), level (%d, %d) (chunk %d): bad tile mip/rip level X (%d, expect %d)",
801 tilex,
802 tiley,
803 levelx,
804 levely,
805 cidx,
806 tdata[2],
807 levelx);
808 }
809 if (tdata[3] != levely)
810 {
811 return pctxt->print_error (
812 pctxt,
813 EXR_ERR_BAD_CHUNK_LEADER,
814 "Corrupt tile (%d, %d), level (%d, %d) (chunk %d): bad tile mip/rip level Y (%d, expect %d)",
815 tilex,
816 tiley,
817 levelx,
818 levely,
819 cidx,
820 tdata[3],
821 levely);
822 }
823
824 if (part->storage_mode == EXR_STORAGE_DEEP_TILED)
825 {
826 int64_t ddata[3];
827 rv = pctxt->do_read (
828 pctxt,
829 ddata,
830 3 * sizeof (int64_t),
831 &dataoff,
832 NULL,
833 EXR_MUST_READ_ALL);
834 if (rv != EXR_ERR_SUCCESS) { return rv; }
835 priv_to_native64 (ddata, 3);
836
837 if (ddata[0] < 0 || (ddata[0] == 0 && (ddata[1] != 0 || ddata[2] != 0)))
838 {
839 return pctxt->print_error (
840 pctxt,
841 EXR_ERR_BAD_CHUNK_LEADER,
842 "Corrupt deep tile (%d, %d), level (%d, %d) (chunk %d): invalid sample table size %" PRId64,
843 tilex,
844 tiley,
845 levelx,
846 levely,
847 cidx,
848 ddata[0]);
849 }
850
851 /* not all compressors support 64-bit */
852 if (ddata[1] < 0 || ddata[1] > (int64_t) INT32_MAX ||
853 (ddata[1] == 0 && ddata[2] != 0))
854 {
855 return pctxt->print_error (
856 pctxt,
857 EXR_ERR_BAD_CHUNK_LEADER,
858 "Corrupt deep tile (%d, %d), level (%d, %d) (chunk %d): invalid packed data size %" PRId64,
859 tilex,
860 tiley,
861 levelx,
862 levely,
863 cidx,
864 ddata[1]);
865 }
866
867 if (ddata[2] < 0 || ddata[2] > (int64_t) INT32_MAX ||
868 (ddata[2] == 0 && ddata[1] != 0))
869 {
870 return pctxt->print_error (
871 pctxt,
872 EXR_ERR_BAD_CHUNK_LEADER,
873 "Corrupt deep tile (%d, %d), level (%d, %d) (chunk %d): invalid unpacked size %" PRId64,
874 tilex,
875 tiley,
876 levelx,
877 levely,
878 cidx,
879 ddata[1]);
880 }
881 cinfo->sample_count_data_offset = dataoff;
882 cinfo->sample_count_table_size = (uint64_t) ddata[0];
883 cinfo->packed_size = (uint64_t) ddata[1];
884 cinfo->unpacked_size = (uint64_t) ddata[2];
885 cinfo->data_offset = dataoff + (uint64_t) ddata[0];
886
887 if (fsize > 0 &&
888 ((cinfo->sample_count_data_offset +
889 cinfo->sample_count_table_size) > ((uint64_t) fsize) ||
890 (cinfo->data_offset + cinfo->packed_size) > ((uint64_t) fsize)))
891 {
892 return pctxt->print_error (
893 pctxt,
894 EXR_ERR_BAD_CHUNK_LEADER,
895 "Corrupt deep tile (%d, %d), level (%d, %d) (chunk %d): access past end of the file: sample table size %" PRId64
896 " + data size %" PRId64 " larger than file %" PRId64,
897 tilex,
898 tiley,
899 levelx,
900 levely,
901 cidx,
902 ddata[0],
903 ddata[1],
904 fsize);
905 }
906 }
907 else
908 {
909 if (tdata[4] < 0 || ((uint64_t) tdata[4]) > unpacksize ||
910 (tdata[4] == 0 && unpacksize != 0))
911 {
912 return pctxt->print_error (
913 pctxt,
914 EXR_ERR_BAD_CHUNK_LEADER,
915 "Corrupt tile (%d, %d), level (%d, %d) (chunk %d): invalid packed size %d vs unpacked size %" PRIu64,
916 tilex,
917 tiley,
918 levelx,
919 levely,
920 cidx,
921 (int) tdata[4],
922 unpacksize);
923 }
924 else if (fsize > 0)
925 {
926 uint64_t finpos = dataoff + (uint64_t) tdata[4];
927 if (finpos > fsize)
928 {
929 return pctxt->print_error (
930 pctxt,
931 EXR_ERR_BAD_CHUNK_LEADER,
932 "Corrupt tile (%d, %d), level (%d, %d) (chunk %d): access past end of file: packed size (%d) at offset %" PRIu64
933 " vs size of file %" PRId64,
934 tilex,
935 tiley,
936 levelx,
937 levely,
938 cidx,
939 (int) tdata[4],
940 dataoff,
941 fsize);
942 }
943 }
944
945 cinfo->packed_size = (uint64_t) tdata[4];
946 cinfo->unpacked_size = unpacksize;
947 cinfo->data_offset = dataoff;
948 cinfo->sample_count_data_offset = 0;
949 cinfo->sample_count_table_size = 0;
950 }
951
952 if (cinfo->packed_size == 0 && cinfo->unpacked_size > 0)
953 return pctxt->report_error (
954 pctxt,
955 EXR_ERR_INVALID_ARGUMENT,
956 "Invalid packed size of 0");
957
958 return EXR_ERR_SUCCESS;
959 }
960
961 exr_result_t
exr_read_chunk(exr_const_context_t ctxt,int part_index,const exr_chunk_info_t * cinfo,void * packed_data)962 exr_read_chunk (
963 exr_const_context_t ctxt,
964 int part_index,
965 const exr_chunk_info_t* cinfo,
966 void* packed_data)
967 {
968 exr_result_t rv;
969 uint64_t dataoffset, toread;
970 int64_t nread;
971 enum _INTERNAL_EXR_READ_MODE rmode = EXR_MUST_READ_ALL;
972 EXR_PROMOTE_READ_CONST_CONTEXT_AND_PART_OR_ERROR (ctxt, part_index);
973
974 if (!cinfo) return pctxt->standard_error (pctxt, EXR_ERR_INVALID_ARGUMENT);
975 if (cinfo->packed_size > 0 && !packed_data)
976 return pctxt->standard_error (pctxt, EXR_ERR_INVALID_ARGUMENT);
977
978 if (cinfo->idx < 0 || cinfo->idx >= part->chunk_count)
979 return pctxt->print_error (
980 pctxt,
981 EXR_ERR_INVALID_ARGUMENT,
982 "invalid chunk index (%d) vs part chunk count %d",
983 cinfo->idx,
984 part->chunk_count);
985 if (cinfo->type != (uint8_t) part->storage_mode)
986 return pctxt->report_error (
987 pctxt,
988 EXR_ERR_INVALID_ARGUMENT,
989 "mis-matched storage type for chunk block info");
990 if (cinfo->compression != (uint8_t) part->comp_type)
991 return pctxt->report_error (
992 pctxt,
993 EXR_ERR_INVALID_ARGUMENT,
994 "mis-matched compression type for chunk block info");
995
996 dataoffset = cinfo->data_offset;
997 if (pctxt->file_size > 0 && dataoffset > (uint64_t) pctxt->file_size)
998 return pctxt->print_error (
999 pctxt,
1000 EXR_ERR_INVALID_ARGUMENT,
1001 "chunk block info data offset (%" PRIu64
1002 ") past end of file (%" PRId64 ")",
1003 dataoffset,
1004 pctxt->file_size);
1005
1006 /* allow a short read if uncompressed */
1007 if (part->comp_type == EXR_COMPRESSION_NONE) rmode = EXR_ALLOW_SHORT_READ;
1008
1009 toread = cinfo->packed_size;
1010 if (toread > 0)
1011 {
1012 nread = 0;
1013 rv = pctxt->do_read (
1014 pctxt, packed_data, toread, &dataoffset, &nread, rmode);
1015
1016 if (rmode == EXR_ALLOW_SHORT_READ && nread < (int64_t) toread)
1017 memset (
1018 ((uint8_t*) packed_data) + nread,
1019 0,
1020 toread - (uint64_t) (nread));
1021 }
1022 else
1023 rv = EXR_ERR_SUCCESS;
1024
1025 return rv;
1026 }
1027
1028 /**************************************/
1029
1030 exr_result_t
exr_read_deep_chunk(exr_const_context_t ctxt,int part_index,const exr_chunk_info_t * cinfo,void * packed_data,void * sample_data)1031 exr_read_deep_chunk (
1032 exr_const_context_t ctxt,
1033 int part_index,
1034 const exr_chunk_info_t* cinfo,
1035 void* packed_data,
1036 void* sample_data)
1037 {
1038 exr_result_t rv;
1039 uint64_t dataoffset, toread;
1040 int64_t nread;
1041 enum _INTERNAL_EXR_READ_MODE rmode = EXR_MUST_READ_ALL;
1042 EXR_PROMOTE_READ_CONST_CONTEXT_AND_PART_OR_ERROR (ctxt, part_index);
1043
1044 if (!cinfo) return pctxt->standard_error (pctxt, EXR_ERR_INVALID_ARGUMENT);
1045
1046 if (cinfo->idx < 0 || cinfo->idx >= part->chunk_count)
1047 return pctxt->print_error (
1048 pctxt,
1049 EXR_ERR_INVALID_ARGUMENT,
1050 "invalid chunk index (%d) vs part chunk count %d",
1051 cinfo->idx,
1052 part->chunk_count);
1053 if (cinfo->type != (uint8_t) part->storage_mode)
1054 return pctxt->report_error (
1055 pctxt,
1056 EXR_ERR_INVALID_ARGUMENT,
1057 "mis-matched storage type for chunk block info");
1058 if (cinfo->compression != (uint8_t) part->comp_type)
1059 return pctxt->report_error (
1060 pctxt,
1061 EXR_ERR_INVALID_ARGUMENT,
1062 "mis-matched compression type for chunk block info");
1063
1064 if (pctxt->file_size > 0 &&
1065 cinfo->sample_count_data_offset > (uint64_t) pctxt->file_size)
1066 return pctxt->print_error (
1067 pctxt,
1068 EXR_ERR_INVALID_ARGUMENT,
1069 "chunk block info sample count offset (%" PRIu64
1070 ") past end of file (%" PRId64 ")",
1071 cinfo->sample_count_data_offset,
1072 pctxt->file_size);
1073
1074 if (pctxt->file_size > 0 &&
1075 cinfo->data_offset > (uint64_t) pctxt->file_size)
1076 return pctxt->print_error (
1077 pctxt,
1078 EXR_ERR_INVALID_ARGUMENT,
1079 "chunk block info data offset (%" PRIu64
1080 ") past end of file (%" PRId64 ")",
1081 cinfo->data_offset,
1082 pctxt->file_size);
1083
1084 rv = EXR_ERR_SUCCESS;
1085 if (sample_data && cinfo->sample_count_table_size > 0)
1086 {
1087 dataoffset = cinfo->sample_count_data_offset;
1088 toread = cinfo->sample_count_table_size;
1089 nread = 0;
1090 rv = pctxt->do_read (
1091 pctxt, sample_data, toread, &dataoffset, &nread, rmode);
1092 }
1093
1094 if (rv != EXR_ERR_SUCCESS) return rv;
1095
1096 if (packed_data && cinfo->packed_size > 0)
1097 {
1098 dataoffset = cinfo->data_offset;
1099 toread = cinfo->packed_size;
1100 nread = 0;
1101 rv = pctxt->do_read (
1102 pctxt, packed_data, toread, &dataoffset, &nread, rmode);
1103 }
1104
1105 return rv;
1106 }
1107
1108 /**************************************/
1109
1110 /* pull most of the logic to here to avoid having to unlock at every
1111 * error exit point and re-use mostly shared logic */
1112 static exr_result_t
write_scan_chunk(struct _internal_exr_context * pctxt,int part_index,struct _internal_exr_part * part,int y,const void * packed_data,uint64_t packed_size,uint64_t unpacked_size,const void * sample_data,uint64_t sample_data_size)1113 write_scan_chunk (
1114 struct _internal_exr_context* pctxt,
1115 int part_index,
1116 struct _internal_exr_part* part,
1117 int y,
1118 const void* packed_data,
1119 uint64_t packed_size,
1120 uint64_t unpacked_size,
1121 const void* sample_data,
1122 uint64_t sample_data_size)
1123 {
1124 exr_result_t rv;
1125 int32_t data[3];
1126 int32_t psize;
1127 int cidx, lpc, miny, wrcnt;
1128 uint64_t* ctable;
1129
1130 if (pctxt->mode != EXR_CONTEXT_WRITING_DATA)
1131 {
1132 if (pctxt->mode == EXR_CONTEXT_WRITE)
1133 return pctxt->standard_error (pctxt, EXR_ERR_HEADER_NOT_WRITTEN);
1134 return pctxt->standard_error (pctxt, EXR_ERR_NOT_OPEN_WRITE);
1135 }
1136
1137 if (part->storage_mode == EXR_STORAGE_TILED ||
1138 part->storage_mode == EXR_STORAGE_DEEP_TILED)
1139 {
1140 return pctxt->standard_error (pctxt, EXR_ERR_SCAN_TILE_MIXEDAPI);
1141 }
1142
1143 if (pctxt->cur_output_part != part_index)
1144 return pctxt->standard_error (pctxt, EXR_ERR_INCORRECT_PART);
1145
1146 if (packed_size > 0 && !packed_data)
1147 return pctxt->print_error (
1148 pctxt,
1149 EXR_ERR_INVALID_ARGUMENT,
1150 "Invalid packed data argument size %" PRIu64 " pointer %p",
1151 (uint64_t) packed_size,
1152 packed_data);
1153
1154 if (part->storage_mode != EXR_STORAGE_DEEP_SCANLINE &&
1155 packed_size > (uint64_t) INT32_MAX)
1156 return pctxt->print_error (
1157 pctxt,
1158 EXR_ERR_INVALID_ARGUMENT,
1159 "Packed data size %" PRIu64 " too large (max %" PRIu64 ")",
1160 (uint64_t) packed_size,
1161 (uint64_t) INT32_MAX);
1162 psize = (int32_t) packed_size;
1163
1164 if (part->storage_mode == EXR_STORAGE_DEEP_SCANLINE &&
1165 (!sample_data || sample_data_size == 0))
1166 return pctxt->print_error (
1167 pctxt,
1168 EXR_ERR_INVALID_ARGUMENT,
1169 "Invalid sample count data argument size %" PRIu64 " pointer %p",
1170 (uint64_t) sample_data_size,
1171 sample_data);
1172
1173 if (y < part->data_window.min.y || y > part->data_window.max.y)
1174 {
1175 return pctxt->print_error (
1176 pctxt,
1177 EXR_ERR_INVALID_ARGUMENT,
1178 "Invalid attempt to write scanlines starting at %d outside range of data window (%d - %d)",
1179 y,
1180 part->data_window.min.y,
1181 part->data_window.max.y);
1182 }
1183
1184 lpc = part->lines_per_chunk;
1185 cidx = (y - part->data_window.min.y);
1186 if (lpc > 1) cidx /= lpc;
1187
1188 //if (part->lineorder == EXR_LINEORDER_DECREASING_Y)
1189 // cidx = part->chunk_count - (cidx + 1);
1190
1191 miny = cidx * lpc + part->data_window.min.y;
1192
1193 if (y != miny)
1194 {
1195 return pctxt->print_error (
1196 pctxt,
1197 EXR_ERR_INVALID_ARGUMENT,
1198 "Attempt to write scanline %d which does not align with y dims (%d) for chunk index (%d)",
1199 y,
1200 miny,
1201 cidx);
1202 }
1203
1204 if (cidx < 0 || cidx >= part->chunk_count)
1205 {
1206 return pctxt->print_error (
1207 pctxt,
1208 EXR_ERR_INVALID_ARGUMENT,
1209 "Chunk index for scanline %d in chunk %d outside chunk count %d",
1210 y,
1211 cidx,
1212 part->chunk_count);
1213 }
1214
1215 if (part->lineorder != EXR_LINEORDER_RANDOM_Y &&
1216 pctxt->last_output_chunk != (cidx - 1))
1217 {
1218 return pctxt->standard_error (pctxt, EXR_ERR_INCORRECT_CHUNK);
1219 }
1220
1221 if (pctxt->is_multipart)
1222 {
1223 data[0] = part_index;
1224 data[1] = miny;
1225 if (part->storage_mode != EXR_STORAGE_DEEP_SCANLINE)
1226 {
1227 data[2] = psize;
1228 wrcnt = 3;
1229 }
1230 else
1231 wrcnt = 2;
1232 }
1233 else
1234 {
1235 data[0] = miny;
1236 if (part->storage_mode != EXR_STORAGE_DEEP_SCANLINE)
1237 {
1238 data[1] = psize;
1239 wrcnt = 2;
1240 }
1241 else
1242 wrcnt = 1;
1243 }
1244 priv_from_native32 (data, wrcnt);
1245
1246 rv = alloc_chunk_table (pctxt, part, &ctable);
1247 if (rv != EXR_ERR_SUCCESS) return rv;
1248
1249 ctable[cidx] = pctxt->output_file_offset;
1250 rv = pctxt->do_write (
1251 pctxt,
1252 data,
1253 (uint64_t) (wrcnt) * sizeof (int32_t),
1254 &(pctxt->output_file_offset));
1255 if (rv == EXR_ERR_SUCCESS &&
1256 part->storage_mode == EXR_STORAGE_DEEP_SCANLINE)
1257 {
1258 int64_t ddata[3];
1259 ddata[0] = (int64_t) sample_data_size;
1260 ddata[1] = (int64_t) packed_size;
1261 ddata[2] = (int64_t) unpacked_size;
1262 rv = pctxt->do_write (
1263 pctxt, ddata, 3 * sizeof (uint64_t), &(pctxt->output_file_offset));
1264
1265 if (rv == EXR_ERR_SUCCESS)
1266 rv = pctxt->do_write (
1267 pctxt,
1268 sample_data,
1269 sample_data_size,
1270 &(pctxt->output_file_offset));
1271 }
1272 if (rv == EXR_ERR_SUCCESS && packed_size > 0)
1273 rv = pctxt->do_write (
1274 pctxt, packed_data, packed_size, &(pctxt->output_file_offset));
1275
1276 if (rv == EXR_ERR_SUCCESS)
1277 {
1278 ++(pctxt->output_chunk_count);
1279 if (pctxt->output_chunk_count == part->chunk_count)
1280 {
1281 uint64_t chunkoff = part->chunk_table_offset;
1282
1283 ++(pctxt->cur_output_part);
1284 if (pctxt->cur_output_part == pctxt->num_parts)
1285 pctxt->mode = EXR_CONTEXT_WRITE_FINISHED;
1286 pctxt->last_output_chunk = -1;
1287 pctxt->output_chunk_count = 0;
1288
1289 priv_from_native64 (ctable, part->chunk_count);
1290 rv = pctxt->do_write (
1291 pctxt,
1292 ctable,
1293 sizeof (uint64_t) * (uint64_t) (part->chunk_count),
1294 &chunkoff);
1295 /* just in case we look at it again? */
1296 priv_to_native64 (ctable, part->chunk_count);
1297 }
1298 else
1299 {
1300 pctxt->last_output_chunk = cidx;
1301 }
1302 }
1303
1304 return rv;
1305 }
1306
1307 /**************************************/
1308
1309 exr_result_t
exr_write_scanline_chunk_info(exr_context_t ctxt,int part_index,int y,exr_chunk_info_t * cinfo)1310 exr_write_scanline_chunk_info (
1311 exr_context_t ctxt, int part_index, int y, exr_chunk_info_t* cinfo)
1312 {
1313 exr_attr_box2i_t dw;
1314 int lpc, miny, cidx;
1315 exr_chunk_info_t nil = { 0 };
1316
1317 EXR_PROMOTE_LOCKED_CONTEXT_AND_PART_OR_ERROR (ctxt, part_index);
1318
1319 if (!cinfo)
1320 return EXR_UNLOCK_AND_RETURN_PCTXT (
1321 pctxt->standard_error (pctxt, EXR_ERR_INVALID_ARGUMENT));
1322
1323 if (part->storage_mode == EXR_STORAGE_TILED ||
1324 part->storage_mode == EXR_STORAGE_DEEP_TILED)
1325 {
1326 return EXR_UNLOCK_AND_RETURN_PCTXT (
1327 pctxt->standard_error (pctxt, EXR_ERR_SCAN_TILE_MIXEDAPI));
1328 }
1329
1330 if (pctxt->mode != EXR_CONTEXT_WRITING_DATA)
1331 {
1332 if (pctxt->mode == EXR_CONTEXT_WRITE)
1333 return EXR_UNLOCK_AND_RETURN_PCTXT (
1334 pctxt->standard_error (pctxt, EXR_ERR_HEADER_NOT_WRITTEN));
1335 return EXR_UNLOCK_AND_RETURN_PCTXT (
1336 pctxt->standard_error (pctxt, EXR_ERR_NOT_OPEN_WRITE));
1337 }
1338
1339 dw = part->data_window;
1340 if (y < dw.min.y || y > dw.max.y)
1341 {
1342 return EXR_UNLOCK_AND_RETURN_PCTXT (pctxt->print_error (
1343 pctxt,
1344 EXR_ERR_INVALID_ARGUMENT,
1345 "Invalid request for scanline %d outside range of data window (%d - %d)",
1346 y,
1347 dw.min.y,
1348 dw.max.y));
1349 }
1350
1351 lpc = part->lines_per_chunk;
1352 cidx = (y - dw.min.y);
1353 if (lpc > 1) cidx /= lpc;
1354
1355 //if (part->lineorder == EXR_LINEORDER_DECREASING_Y)
1356 // cidx = part->chunk_count - (cidx + 1);
1357 miny = cidx * lpc + dw.min.y;
1358
1359 if (cidx < 0 || cidx >= part->chunk_count)
1360 {
1361 return EXR_UNLOCK_AND_RETURN_PCTXT (pctxt->print_error (
1362 pctxt,
1363 EXR_ERR_INVALID_ARGUMENT,
1364 "Invalid request for scanline %d in chunk %d outside chunk count %d",
1365 y,
1366 cidx,
1367 part->chunk_count));
1368 }
1369
1370 *cinfo = nil;
1371 cinfo->idx = cidx;
1372 cinfo->type = (uint8_t) part->storage_mode;
1373 cinfo->compression = (uint8_t) part->comp_type;
1374 cinfo->start_x = dw.min.x;
1375 cinfo->start_y = miny;
1376 cinfo->width = dw.max.x - dw.min.x + 1;
1377 cinfo->height = lpc;
1378 if (miny < dw.min.y)
1379 {
1380 cinfo->start_y = dw.min.y;
1381 cinfo->height -= (dw.min.y - miny);
1382 }
1383 else if ((miny + lpc) > dw.max.y)
1384 {
1385 cinfo->height = (dw.max.y - miny + 1);
1386 }
1387 cinfo->level_x = 0;
1388 cinfo->level_y = 0;
1389
1390 cinfo->sample_count_data_offset = 0;
1391 cinfo->sample_count_table_size = 0;
1392 cinfo->data_offset = 0;
1393 cinfo->packed_size = 0;
1394 cinfo->unpacked_size =
1395 compute_chunk_unpack_size (y, cinfo->width, cinfo->height, lpc, part);
1396
1397 return EXR_UNLOCK_AND_RETURN_PCTXT (EXR_ERR_SUCCESS);
1398 }
1399
1400 /**************************************/
1401
1402 exr_result_t
exr_write_tile_chunk_info(exr_context_t ctxt,int part_index,int tilex,int tiley,int levelx,int levely,exr_chunk_info_t * cinfo)1403 exr_write_tile_chunk_info (
1404 exr_context_t ctxt,
1405 int part_index,
1406 int tilex,
1407 int tiley,
1408 int levelx,
1409 int levely,
1410 exr_chunk_info_t* cinfo)
1411 {
1412 exr_result_t rv;
1413 int cidx;
1414 const exr_attr_chlist_t* chanlist;
1415 const exr_attr_tiledesc_t* tiledesc;
1416 int tilew, tileh;
1417 uint64_t unpacksize = 0;
1418 exr_chunk_info_t nil = { 0 };
1419
1420 EXR_PROMOTE_LOCKED_CONTEXT_AND_PART_OR_ERROR (ctxt, part_index);
1421
1422 if (!cinfo)
1423 return EXR_UNLOCK_AND_RETURN_PCTXT (
1424 pctxt->standard_error (pctxt, EXR_ERR_INVALID_ARGUMENT));
1425
1426 if (tilex < 0 || tiley < 0 || levelx < 0 || levely < 0)
1427 return EXR_UNLOCK_AND_RETURN_PCTXT (
1428 pctxt->standard_error (pctxt, EXR_ERR_INVALID_ARGUMENT));
1429 if (part->storage_mode == EXR_STORAGE_SCANLINE ||
1430 part->storage_mode == EXR_STORAGE_DEEP_SCANLINE)
1431 {
1432 return EXR_UNLOCK_AND_RETURN_PCTXT (
1433 pctxt->standard_error (pctxt, EXR_ERR_TILE_SCAN_MIXEDAPI));
1434 }
1435
1436 if (!part->tiles || part->num_tile_levels_x <= 0 ||
1437 part->num_tile_levels_y <= 0 || !part->tile_level_tile_count_x ||
1438 !part->tile_level_tile_count_y)
1439 {
1440 return EXR_UNLOCK_WRITE_AND_RETURN_PCTXT (pctxt->report_error (
1441 pctxt, EXR_ERR_MISSING_REQ_ATTR, "Tile data missing or corrupt"));
1442 }
1443
1444 if (pctxt->mode != EXR_CONTEXT_WRITING_DATA)
1445 {
1446 if (pctxt->mode == EXR_CONTEXT_WRITE)
1447 return EXR_UNLOCK_AND_RETURN_PCTXT (
1448 pctxt->standard_error (pctxt, EXR_ERR_HEADER_NOT_WRITTEN));
1449 return EXR_UNLOCK_AND_RETURN_PCTXT (
1450 pctxt->standard_error (pctxt, EXR_ERR_NOT_OPEN_WRITE));
1451 }
1452
1453 tiledesc = part->tiles->tiledesc;
1454 tilew = part->tile_level_tile_size_x[levelx];
1455 if (tiledesc->x_size < (uint32_t) tilew) tilew = (int) tiledesc->x_size;
1456 tileh = part->tile_level_tile_size_y[levely];
1457 if (tiledesc->y_size < (uint32_t) tileh) tileh = (int) tiledesc->y_size;
1458
1459 if (((int64_t) (tilex) * (int64_t) (tilew) + (int64_t) (tilew) +
1460 (int64_t) (part->data_window.min.x) - 1) >
1461 (int64_t) (part->data_window.max.x))
1462 {
1463 int64_t sz = (int64_t) (part->data_window.max.x) -
1464 (int64_t) (part->data_window.min.x) + 1;
1465 tilew = (int) (sz - ((int64_t) (tilex) * (int64_t) (tilew)));
1466 }
1467
1468 if (((int64_t) (tiley) * (int64_t) (tileh) + (int64_t) (tileh) +
1469 (int64_t) (part->data_window.min.y) - 1) >
1470 (int64_t) (part->data_window.max.y))
1471 {
1472 int64_t sz = (int64_t) (part->data_window.max.y) -
1473 (int64_t) (part->data_window.min.y) + 1;
1474 tileh = (int) (sz - ((int64_t) (tiley) * (int64_t) (tileh)));
1475 }
1476
1477 cidx = 0;
1478 rv = compute_tile_chunk_off (
1479 pctxt, part, tilex, tiley, levelx, levely, &cidx);
1480 if (rv != EXR_ERR_SUCCESS) return EXR_UNLOCK_AND_RETURN_PCTXT (rv);
1481
1482 *cinfo = nil;
1483 cinfo->idx = cidx;
1484 cinfo->type = (uint8_t) part->storage_mode;
1485 cinfo->compression = (uint8_t) part->comp_type;
1486 cinfo->start_x = tilex;
1487 cinfo->start_y = tiley;
1488 cinfo->height = tileh;
1489 cinfo->width = tilew;
1490 if (levelx > 255 || levely > 255)
1491 return pctxt->print_error (
1492 pctxt,
1493 EXR_ERR_ATTR_SIZE_MISMATCH,
1494 "Unable to represent tile level %d, %d in chunk structure",
1495 levelx,
1496 levely);
1497
1498 cinfo->level_x = (uint8_t) levelx;
1499 cinfo->level_y = (uint8_t) levely;
1500
1501 chanlist = part->channels->chlist;
1502 for (int c = 0; c < chanlist->num_channels; ++c)
1503 {
1504 const exr_attr_chlist_entry_t* curc = (chanlist->entries + c);
1505 unpacksize += (uint64_t) (tilew) * (uint64_t) (tileh) *
1506 (uint64_t) ((curc->pixel_type == EXR_PIXEL_HALF) ? 2 : 4);
1507 }
1508
1509 cinfo->sample_count_data_offset = 0;
1510 cinfo->sample_count_table_size = 0;
1511 cinfo->data_offset = 0;
1512 cinfo->packed_size = 0;
1513 cinfo->unpacked_size = unpacksize;
1514
1515 return EXR_UNLOCK_AND_RETURN_PCTXT (EXR_ERR_SUCCESS);
1516 }
1517
1518 /**************************************/
1519
1520 exr_result_t
exr_write_scanline_chunk(exr_context_t ctxt,int part_index,int y,const void * packed_data,uint64_t packed_size)1521 exr_write_scanline_chunk (
1522 exr_context_t ctxt,
1523 int part_index,
1524 int y,
1525 const void* packed_data,
1526 uint64_t packed_size)
1527 {
1528 exr_result_t rv;
1529 EXR_PROMOTE_LOCKED_CONTEXT_AND_PART_OR_ERROR (ctxt, part_index);
1530
1531 if (part->storage_mode == EXR_STORAGE_DEEP_SCANLINE)
1532 return EXR_UNLOCK_AND_RETURN_PCTXT (
1533 pctxt->standard_error (pctxt, EXR_ERR_USE_SCAN_DEEP_WRITE));
1534
1535 rv = write_scan_chunk (
1536 pctxt, part_index, part, y, packed_data, packed_size, 0, NULL, 0);
1537 return EXR_UNLOCK_AND_RETURN_PCTXT (rv);
1538 }
1539
1540 /**************************************/
1541
1542 exr_result_t
exr_write_deep_scanline_chunk(exr_context_t ctxt,int part_index,int y,const void * packed_data,uint64_t packed_size,uint64_t unpacked_size,const void * sample_data,uint64_t sample_data_size)1543 exr_write_deep_scanline_chunk (
1544 exr_context_t ctxt,
1545 int part_index,
1546 int y,
1547 const void* packed_data,
1548 uint64_t packed_size,
1549 uint64_t unpacked_size,
1550 const void* sample_data,
1551 uint64_t sample_data_size)
1552 {
1553 exr_result_t rv;
1554 EXR_PROMOTE_LOCKED_CONTEXT_AND_PART_OR_ERROR (ctxt, part_index);
1555
1556 if (part->storage_mode == EXR_STORAGE_SCANLINE)
1557 return EXR_UNLOCK_AND_RETURN_PCTXT (
1558 pctxt->standard_error (pctxt, EXR_ERR_USE_SCAN_NONDEEP_WRITE));
1559
1560 rv = write_scan_chunk (
1561 pctxt,
1562 part_index,
1563 part,
1564 y,
1565 packed_data,
1566 packed_size,
1567 unpacked_size,
1568 sample_data,
1569 sample_data_size);
1570 return EXR_UNLOCK_AND_RETURN_PCTXT (rv);
1571 }
1572
1573 /**************************************/
1574
1575 /* pull most of the logic to here to avoid having to unlock at every
1576 * error exit point and re-use mostly shared logic */
1577 static exr_result_t
write_tile_chunk(struct _internal_exr_context * pctxt,int part_index,struct _internal_exr_part * part,int tilex,int tiley,int levelx,int levely,const void * packed_data,uint64_t packed_size,uint64_t unpacked_size,const void * sample_data,uint64_t sample_data_size)1578 write_tile_chunk (
1579 struct _internal_exr_context* pctxt,
1580 int part_index,
1581 struct _internal_exr_part* part,
1582 int tilex,
1583 int tiley,
1584 int levelx,
1585 int levely,
1586 const void* packed_data,
1587 uint64_t packed_size,
1588 uint64_t unpacked_size,
1589 const void* sample_data,
1590 uint64_t sample_data_size)
1591 {
1592 exr_result_t rv;
1593 int32_t data[6];
1594 int32_t psize;
1595 int cidx, wrcnt;
1596 uint64_t* ctable;
1597
1598 if (pctxt->mode != EXR_CONTEXT_WRITING_DATA)
1599 {
1600 if (pctxt->mode == EXR_CONTEXT_WRITE)
1601 return pctxt->standard_error (pctxt, EXR_ERR_HEADER_NOT_WRITTEN);
1602 return pctxt->standard_error (pctxt, EXR_ERR_NOT_OPEN_WRITE);
1603 }
1604
1605 if (part->storage_mode == EXR_STORAGE_SCANLINE ||
1606 part->storage_mode == EXR_STORAGE_DEEP_SCANLINE)
1607 {
1608 return pctxt->standard_error (pctxt, EXR_ERR_TILE_SCAN_MIXEDAPI);
1609 }
1610
1611 if (pctxt->cur_output_part != part_index)
1612 return pctxt->standard_error (pctxt, EXR_ERR_INCORRECT_PART);
1613
1614 if (!packed_data || packed_size == 0)
1615 return pctxt->print_error (
1616 pctxt,
1617 EXR_ERR_INVALID_ARGUMENT,
1618 "Invalid packed data argument size %" PRIu64 " pointer %p",
1619 (uint64_t) packed_size,
1620 packed_data);
1621
1622 if (part->storage_mode != EXR_STORAGE_DEEP_TILED &&
1623 packed_size > (uint64_t) INT32_MAX)
1624 return pctxt->print_error (
1625 pctxt,
1626 EXR_ERR_INVALID_ARGUMENT,
1627 "Packed data size %" PRIu64 " too large (max %" PRIu64 ")",
1628 (uint64_t) packed_size,
1629 (uint64_t) INT32_MAX);
1630 psize = (int32_t) packed_size;
1631
1632 if (part->storage_mode == EXR_STORAGE_DEEP_TILED &&
1633 (!sample_data || sample_data_size == 0))
1634 return pctxt->print_error (
1635 pctxt,
1636 EXR_ERR_INVALID_ARGUMENT,
1637 "Invalid sample count data argument size %" PRIu64 " pointer %p",
1638 (uint64_t) sample_data_size,
1639 sample_data);
1640
1641 if (!part->tiles || part->num_tile_levels_x <= 0 ||
1642 part->num_tile_levels_y <= 0 || !part->tile_level_tile_count_x ||
1643 !part->tile_level_tile_count_y)
1644 {
1645 return pctxt->print_error (
1646 pctxt,
1647 EXR_ERR_MISSING_REQ_ATTR,
1648 "Attempting to write tiled part, but tile data missing or corrupt");
1649 }
1650
1651 cidx = -1;
1652 rv = compute_tile_chunk_off (
1653 pctxt, part, tilex, tiley, levelx, levely, &cidx);
1654 if (rv != EXR_ERR_SUCCESS) return rv;
1655
1656 if (cidx < 0 || cidx >= part->chunk_count)
1657 {
1658 return pctxt->print_error (
1659 pctxt,
1660 EXR_ERR_INVALID_ARGUMENT,
1661 "Chunk index for tile (%d, %d) at level (%d, %d) %d outside chunk count %d",
1662 tilex,
1663 tiley,
1664 levelx,
1665 levely,
1666 cidx,
1667 part->chunk_count);
1668 }
1669
1670 if (part->lineorder != EXR_LINEORDER_RANDOM_Y &&
1671 pctxt->last_output_chunk != (cidx - 1))
1672 {
1673 return pctxt->print_error (
1674 pctxt,
1675 EXR_ERR_INCORRECT_CHUNK,
1676 "Chunk index %d is not the next chunk to be written (last %d)",
1677 cidx,
1678 pctxt->last_output_chunk);
1679 }
1680
1681 wrcnt = 0;
1682 if (pctxt->is_multipart) { data[wrcnt++] = part_index; }
1683 data[wrcnt++] = tilex;
1684 data[wrcnt++] = tiley;
1685 data[wrcnt++] = levelx;
1686 data[wrcnt++] = levely;
1687 if (part->storage_mode != EXR_STORAGE_DEEP_TILED) data[wrcnt++] = psize;
1688
1689 priv_from_native32 (data, wrcnt);
1690
1691 rv = alloc_chunk_table (pctxt, part, &ctable);
1692 if (rv != EXR_ERR_SUCCESS) return rv;
1693
1694 ctable[cidx] = pctxt->output_file_offset;
1695 rv = pctxt->do_write (
1696 pctxt,
1697 data,
1698 (uint64_t) (wrcnt) * sizeof (int32_t),
1699 &(pctxt->output_file_offset));
1700 if (rv == EXR_ERR_SUCCESS && part->storage_mode == EXR_STORAGE_DEEP_TILED)
1701 {
1702 int64_t ddata[3];
1703 ddata[0] = (int64_t) sample_data_size;
1704 ddata[1] = (int64_t) packed_size;
1705 ddata[2] = (int64_t) unpacked_size;
1706 rv = pctxt->do_write (
1707 pctxt, ddata, 3 * sizeof (uint64_t), &(pctxt->output_file_offset));
1708
1709 if (rv == EXR_ERR_SUCCESS)
1710 rv = pctxt->do_write (
1711 pctxt,
1712 sample_data,
1713 sample_data_size,
1714 &(pctxt->output_file_offset));
1715 }
1716 if (rv == EXR_ERR_SUCCESS)
1717 rv = pctxt->do_write (
1718 pctxt, packed_data, packed_size, &(pctxt->output_file_offset));
1719
1720 if (rv == EXR_ERR_SUCCESS)
1721 {
1722 ++(pctxt->output_chunk_count);
1723 if (pctxt->output_chunk_count == part->chunk_count)
1724 {
1725 uint64_t chunkoff = part->chunk_table_offset;
1726
1727 ++(pctxt->cur_output_part);
1728 if (pctxt->cur_output_part == pctxt->num_parts)
1729 pctxt->mode = EXR_CONTEXT_WRITE_FINISHED;
1730 pctxt->last_output_chunk = -1;
1731 pctxt->output_chunk_count = 0;
1732
1733 priv_from_native64 (ctable, part->chunk_count);
1734 rv = pctxt->do_write (
1735 pctxt,
1736 ctable,
1737 sizeof (uint64_t) * (uint64_t) (part->chunk_count),
1738 &chunkoff);
1739 /* just in case we look at it again? */
1740 priv_to_native64 (ctable, part->chunk_count);
1741 }
1742 else
1743 {
1744 pctxt->last_output_chunk = cidx;
1745 }
1746 }
1747
1748 return rv;
1749 }
1750
1751 /**************************************/
1752
1753 exr_result_t
exr_write_tile_chunk(exr_context_t ctxt,int part_index,int tilex,int tiley,int levelx,int levely,const void * packed_data,uint64_t packed_size)1754 exr_write_tile_chunk (
1755 exr_context_t ctxt,
1756 int part_index,
1757 int tilex,
1758 int tiley,
1759 int levelx,
1760 int levely,
1761 const void* packed_data,
1762 uint64_t packed_size)
1763 {
1764 exr_result_t rv;
1765 EXR_PROMOTE_LOCKED_CONTEXT_AND_PART_OR_ERROR (ctxt, part_index);
1766
1767 if (part->storage_mode == EXR_STORAGE_DEEP_TILED)
1768 return EXR_UNLOCK_AND_RETURN_PCTXT (
1769 pctxt->standard_error (pctxt, EXR_ERR_USE_TILE_DEEP_WRITE));
1770
1771 rv = write_tile_chunk (
1772 pctxt,
1773 part_index,
1774 part,
1775 tilex,
1776 tiley,
1777 levelx,
1778 levely,
1779 packed_data,
1780 packed_size,
1781 0,
1782 NULL,
1783 0);
1784 return EXR_UNLOCK_AND_RETURN_PCTXT (rv);
1785 }
1786
1787 /**************************************/
1788
1789 exr_result_t
exr_write_deep_tile_chunk(exr_context_t ctxt,int part_index,int tilex,int tiley,int levelx,int levely,const void * packed_data,uint64_t packed_size,uint64_t unpacked_size,const void * sample_data,uint64_t sample_data_size)1790 exr_write_deep_tile_chunk (
1791 exr_context_t ctxt,
1792 int part_index,
1793 int tilex,
1794 int tiley,
1795 int levelx,
1796 int levely,
1797 const void* packed_data,
1798 uint64_t packed_size,
1799 uint64_t unpacked_size,
1800 const void* sample_data,
1801 uint64_t sample_data_size)
1802 {
1803 exr_result_t rv;
1804 EXR_PROMOTE_LOCKED_CONTEXT_AND_PART_OR_ERROR (ctxt, part_index);
1805
1806 if (part->storage_mode == EXR_STORAGE_TILED)
1807 return EXR_UNLOCK_AND_RETURN_PCTXT (
1808 pctxt->standard_error (pctxt, EXR_ERR_USE_TILE_NONDEEP_WRITE));
1809
1810 rv = write_tile_chunk (
1811 pctxt,
1812 part_index,
1813 part,
1814 tilex,
1815 tiley,
1816 levelx,
1817 levely,
1818 packed_data,
1819 packed_size,
1820 unpacked_size,
1821 sample_data,
1822 sample_data_size);
1823 return EXR_UNLOCK_AND_RETURN_PCTXT (rv);
1824 }
1825
1826 /**************************************/
1827
1828 exr_result_t
internal_validate_next_chunk(exr_encode_pipeline_t * encode,const struct _internal_exr_context * pctxt,const struct _internal_exr_part * part)1829 internal_validate_next_chunk (
1830 exr_encode_pipeline_t* encode,
1831 const struct _internal_exr_context* pctxt,
1832 const struct _internal_exr_part* part)
1833 {
1834 exr_result_t rv = EXR_ERR_SUCCESS;
1835 int cidx, lpc;
1836
1837 if (pctxt->cur_output_part != encode->part_index)
1838 return pctxt->standard_error (pctxt, EXR_ERR_INCORRECT_PART);
1839
1840 cidx = -1;
1841
1842 if (part->storage_mode == EXR_STORAGE_TILED ||
1843 part->storage_mode == EXR_STORAGE_DEEP_TILED)
1844 {
1845 rv = compute_tile_chunk_off (
1846 pctxt,
1847 part,
1848 encode->chunk.start_x,
1849 encode->chunk.start_y,
1850 encode->chunk.level_x,
1851 encode->chunk.level_y,
1852 &cidx);
1853 }
1854 else
1855 {
1856 lpc = part->lines_per_chunk;
1857 cidx = (encode->chunk.start_y - part->data_window.min.y);
1858 if (lpc > 1) cidx /= lpc;
1859
1860 //if (part->lineorder == EXR_LINEORDER_DECREASING_Y)
1861 //{
1862 // cidx = part->chunk_count - (cidx + 1);
1863 //}
1864 }
1865
1866 if (rv == EXR_ERR_SUCCESS)
1867 {
1868 if (cidx < 0 || cidx >= part->chunk_count)
1869 {
1870 rv = pctxt->print_error (
1871 pctxt,
1872 EXR_ERR_INVALID_ARGUMENT,
1873 "Chunk index for scanline %d in chunk %d outside chunk count %d",
1874 encode->chunk.start_y,
1875 cidx,
1876 part->chunk_count);
1877 }
1878 else if (
1879 part->lineorder != EXR_LINEORDER_RANDOM_Y &&
1880 pctxt->last_output_chunk != (cidx - 1))
1881 {
1882 rv = pctxt->print_error (
1883 pctxt,
1884 EXR_ERR_INCORRECT_CHUNK,
1885 "Attempt to write chunk %d, but last output chunk is %d",
1886 cidx,
1887 pctxt->last_output_chunk);
1888 }
1889 }
1890 return rv;
1891 }
1892