Created
January 5, 2023 02:59
-
-
Save meshula/b573e2d31d475e24a1a6d515cb5c79af to your computer and use it in GitHub Desktop.
minimal-exr.c
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "OpenEXRCore/attributes.c" | |
#include "OpenEXRCore/base.c" | |
#include "OpenEXRCore/channel_list.c" | |
#include "OpenEXRCore/chunk.c" | |
#include "OpenEXRCore/coding.c" | |
#include "OpenEXRCore/context.c" | |
#include "OpenEXRCore/debug.c" | |
#include "OpenEXRCore/decoding.c" | |
#include "OpenEXRCore/encoding.c" | |
#include "OpenEXRCore/float_vector.c" | |
#include "OpenEXRCore/internal_b44_table.c" | |
#include "OpenEXRCore/internal_b44.c" | |
#include "OpenEXRCore/internal_dwa.c" | |
#include "OpenEXRCore/internal_huf.c" | |
#include "OpenEXRCore/internal_piz.c" | |
#include "OpenEXRCore/internal_pxr24.c" | |
#include "OpenEXRCore/internal_rle.c" | |
#include "OpenEXRCore/internal_structs.c" | |
#include "OpenEXRCore/internal_zip.c" | |
#include "OpenEXRCore/memory.c" | |
#include "OpenEXRCore/opaque.c" | |
#include "OpenEXRCore/pack.c" | |
#include "OpenEXRCore/parse_header.c" | |
#include "OpenEXRCore/part_attr.c" | |
#include "OpenEXRCore/part.c" | |
#include "OpenEXRCore/preview.c" | |
#include "OpenEXRCore/std_attr.c" | |
#include "OpenEXRCore/string_vector.c" | |
#include "OpenEXRCore/string.c" | |
#include "OpenEXRCore/unpack.c" | |
#include "OpenEXRCore/validation.c" | |
#include "OpenEXRCore/write_header.c" | |
#define EXR_FILE "StillLife.exr" | |
uint64_t gMaxBytesPerScanline = 8000000; | |
uint64_t gMaxTileBytes = 1000 * 1000; | |
int | |
readCoreScanlinePart( | |
exr_context_t f, int part, bool reduceMemory, bool reduceTime) | |
{ | |
exr_result_t rv; | |
exr_attr_box2i_t datawin; | |
rv = exr_get_data_window(f, part, &datawin); | |
if (rv != EXR_ERR_SUCCESS) return rv; | |
uint64_t width = | |
(uint64_t) ((int64_t) datawin.max.x - (int64_t) datawin.min.x + 1); | |
uint64_t height = | |
(uint64_t) ((int64_t) datawin.max.y - (int64_t) datawin.min.y + 1); | |
printf("Image size is %ld, %ld\n", width, height); | |
uint8_t* imgdata = NULL; | |
bool doread = false; | |
exr_decode_pipeline_t decoder = EXR_DECODE_PIPELINE_INITIALIZER; | |
int32_t lines_per_chunk; | |
rv = exr_get_scanlines_per_chunk(f, part, &lines_per_chunk); | |
if (rv != EXR_ERR_SUCCESS) return rv; | |
for (uint64_t chunk = 0; chunk < height; chunk += lines_per_chunk) | |
{ | |
exr_chunk_info_t cinfo = {0}; | |
int y = ((int) chunk) + datawin.min.y; | |
rv = exr_read_scanline_chunk_info(f, part, y, &cinfo); | |
if (rv != EXR_ERR_SUCCESS) | |
{ | |
if (reduceTime) break; | |
continue; | |
} | |
if (decoder.channels == NULL) | |
{ | |
rv = exr_decoding_initialize(f, part, &cinfo, &decoder); | |
if (rv != EXR_ERR_SUCCESS) break; | |
uint64_t bytes = 0; | |
for (int c = 0; c < decoder.channel_count; c++) | |
{ | |
exr_coding_channel_info_t* outc = &decoder.channels[c]; | |
// fake addr for default routines | |
outc->decode_to_ptr = (uint8_t*) 0x1000; | |
outc->user_pixel_stride = outc->user_bytes_per_element; | |
outc->user_line_stride = outc->user_pixel_stride * width; | |
bytes += width * (uint64_t) outc->user_bytes_per_element * | |
(uint64_t) lines_per_chunk; | |
} | |
// TODO: check whether we are supposed to multiply by lines per chunk above | |
doread = true; | |
if (reduceMemory && bytes >= gMaxBytesPerScanline) | |
doread = false; | |
if (doread) { | |
imgdata = (uint8_t*) malloc(bytes); | |
} | |
rv = exr_decoding_choose_default_routines(f, part, &decoder); | |
if (rv != EXR_ERR_SUCCESS) break; | |
} | |
else | |
{ | |
rv = exr_decoding_update(f, part, &cinfo, &decoder); | |
if (rv != EXR_ERR_SUCCESS) | |
{ | |
if (reduceTime) break; | |
continue; | |
} | |
} | |
if (doread) | |
{ | |
uint8_t* dptr = &(imgdata[0]); | |
for (int c = 0; c < decoder.channel_count; c++) | |
{ | |
exr_coding_channel_info_t* outc = &decoder.channels[c]; | |
outc->decode_to_ptr = dptr; | |
outc->user_pixel_stride = outc->user_bytes_per_element; | |
outc->user_line_stride = outc->user_pixel_stride * width; | |
dptr += width * (uint64_t) outc->user_bytes_per_element * | |
(uint64_t) lines_per_chunk; | |
} | |
rv = exr_decoding_run(f, part, &decoder); | |
if (rv != EXR_ERR_SUCCESS) | |
{ | |
if (reduceTime) break; | |
} | |
} | |
} | |
exr_decoding_destroy(f, &decoder); | |
if (imgdata != NULL) | |
free(imgdata); | |
return rv; | |
} | |
//////////////////////////////////////// | |
int | |
readCoreTiledPart ( | |
exr_context_t f, int part, bool reduceMemory, bool reduceTime) | |
{ | |
exr_result_t rv; | |
exr_attr_box2i_t datawin; | |
rv = exr_get_data_window(f, part, &datawin); | |
if (rv != EXR_ERR_SUCCESS) return rv; | |
uint32_t txsz, tysz; | |
exr_tile_level_mode_t levelmode; | |
exr_tile_round_mode_t roundingmode; | |
rv = exr_get_tile_descriptor( | |
f, part, &txsz, &tysz, &levelmode, &roundingmode); | |
if (rv != EXR_ERR_SUCCESS) return rv; | |
int32_t levelsx, levelsy; | |
rv = exr_get_tile_levels(f, part, &levelsx, &levelsy); | |
if (rv != EXR_ERR_SUCCESS) return rv; | |
bool keepgoing = true; | |
for (int32_t ylevel = 0; keepgoing && ylevel < levelsy; ++ylevel) | |
{ | |
for (int32_t xlevel = 0; keepgoing && xlevel < levelsx; ++xlevel) | |
{ | |
int32_t levw, levh; | |
rv = exr_get_level_sizes(f, part, xlevel, ylevel, &levw, &levh); | |
if (rv != EXR_ERR_SUCCESS) | |
{ | |
if (reduceTime) | |
{ | |
keepgoing = false; | |
break; | |
} | |
continue; | |
} | |
int32_t curtw, curth; | |
rv = exr_get_tile_sizes(f, part, xlevel, ylevel, &curtw, &curth); | |
if (rv != EXR_ERR_SUCCESS) | |
{ | |
if (reduceTime) | |
{ | |
keepgoing = false; | |
break; | |
} | |
continue; | |
} | |
// we could make this over all levels but then would have to | |
// re-check the allocation size, let's leave it here to check when | |
// tile size is < full / top level tile size | |
uint8_t* tiledata = NULL; | |
bool doread = false; | |
exr_chunk_info_t cinfo; | |
exr_decode_pipeline_t decoder = EXR_DECODE_PIPELINE_INITIALIZER; | |
int tx, ty; | |
ty = 0; | |
for (int64_t cury = 0; keepgoing && cury < levh; | |
cury += curth, ++ty) | |
{ | |
tx = 0; | |
for (int64_t curx = 0; keepgoing && curx < levw; | |
curx += curtw, ++tx) | |
{ | |
rv = exr_read_tile_chunk_info ( | |
f, part, tx, ty, xlevel, ylevel, &cinfo); | |
if (rv != EXR_ERR_SUCCESS) | |
{ | |
if (reduceTime) { | |
keepgoing = false; | |
break; | |
} | |
continue; | |
} | |
if (decoder.channels == NULL) | |
{ | |
rv = | |
exr_decoding_initialize (f, part, &cinfo, &decoder); | |
if (rv != EXR_ERR_SUCCESS) { | |
keepgoing = false; | |
break; | |
} | |
uint64_t bytes = 0; | |
for (int c = 0; c < decoder.channel_count; c++) { | |
exr_coding_channel_info_t* outc = | |
&decoder.channels[c]; | |
// fake addr for default routines | |
outc->decode_to_ptr = (uint8_t*) 0x1000 + bytes; | |
outc->user_pixel_stride = | |
outc->user_bytes_per_element; | |
outc->user_line_stride = | |
outc->user_pixel_stride * curtw; | |
bytes += (uint64_t) curtw * | |
(uint64_t) outc->user_bytes_per_element * | |
(uint64_t) curth; | |
} | |
doread = true; | |
if (reduceMemory && bytes >= gMaxTileBytes) | |
doread = false; | |
if (doread) { | |
tiledata = (uint8_t*) malloc(bytes); | |
} | |
rv = exr_decoding_choose_default_routines(f, part, &decoder); | |
if (rv != EXR_ERR_SUCCESS) { | |
keepgoing = false; | |
break; | |
} | |
} | |
else | |
{ | |
rv = exr_decoding_update(f, part, &cinfo, &decoder); | |
if (rv != EXR_ERR_SUCCESS) | |
{ | |
if (reduceTime) { | |
keepgoing = false; | |
break; | |
} | |
continue; | |
} | |
} | |
if (doread) | |
{ | |
uint8_t* dptr = &(tiledata[0]); | |
for (int c = 0; c < decoder.channel_count; c++) | |
{ | |
exr_coding_channel_info_t* outc = | |
&decoder.channels[c]; | |
outc->decode_to_ptr = dptr; | |
outc->user_pixel_stride = | |
outc->user_bytes_per_element; | |
outc->user_line_stride = | |
outc->user_pixel_stride * curtw; | |
dptr += (uint64_t) curtw * | |
(uint64_t) outc->user_bytes_per_element * | |
(uint64_t) curth; | |
} | |
rv = exr_decoding_run(f, part, &decoder); | |
if (rv != EXR_ERR_SUCCESS) { | |
if (reduceTime) { | |
keepgoing = false; | |
break; | |
} | |
} | |
} | |
} | |
} | |
exr_decoding_destroy(f, &decoder); | |
free(tiledata); | |
} | |
} | |
return rv; | |
} | |
static void | |
err_cb (exr_const_context_t f, int code, const char* msg) | |
{ | |
fprintf(stderr, "err_cb ERROR %d: %s\n", code, msg); | |
} | |
int main(int argc, char** argv) { | |
int maj, min, patch; | |
const char* extra; | |
exr_get_library_version (&maj, &min, &patch, &extra); | |
printf("OpenEXR %d.%d.%d %s\n", maj, min, patch, extra? extra: ""); | |
exr_context_t f; | |
exr_context_initializer_t cinit = EXR_DEFAULT_CONTEXT_INITIALIZER; | |
cinit.error_handler_fn = &err_cb; | |
char filename_buff[32768]; | |
snprintf(filename_buff, sizeof(filename_buff), "%s", EXR_FILE); | |
int rval = exr_test_file_header(filename_buff, &cinit); | |
if (rval != EXR_ERR_SUCCESS) { | |
fprintf(stderr, "could not open %s for reading because %d\n", filename_buff, rval); | |
goto err; | |
} | |
printf("rval is %d\n", rval); | |
rval = exr_start_read(&f, filename_buff, &cinit); | |
if (rval != EXR_ERR_SUCCESS) { | |
fprintf(stderr, "could not start reading %s because %d\n", filename_buff, rval); | |
goto err; | |
} | |
EXR_PROMOTE_CONST_CONTEXT_OR_ERROR(f); | |
printf ( | |
"File '%s': ver %d flags%s%s%s%s\n", | |
pctxt->filename.str, | |
(int) pctxt->version, | |
pctxt->is_singlepart_tiled ? " singletile" : "", | |
pctxt->max_name_length == EXR_LONGNAME_MAXLEN ? " longnames" | |
: " shortnames", | |
pctxt->has_nonimage_data ? " deep" : " not-deep ", | |
pctxt->is_multipart ? " multipart" : " not-multipart "); | |
printf (" parts: %d\n", pctxt->num_parts); | |
bool verbose = true; | |
for (int partidx = 0; partidx < pctxt->num_parts; ++partidx) | |
{ | |
const struct _internal_exr_part* curpart = pctxt->parts[partidx]; | |
if (pctxt->is_multipart || curpart->name) { | |
printf( | |
" part %d: %s\n", | |
partidx + 1, | |
curpart->name ? curpart->name->string->str : "<single>"); | |
for (int a = 0; a < curpart->attributes.num_attributes; ++a) | |
{ | |
if (a > 0) printf ("\n"); | |
printf(" "); | |
print_attr(curpart->attributes.entries[a], verbose); | |
} | |
printf("\n"); | |
} | |
if (curpart->type) | |
{ | |
printf(" "); | |
print_attr(curpart->type, verbose); | |
} | |
printf (" "); | |
print_attr(curpart->compression, verbose); | |
if (curpart->tiles) | |
{ | |
printf("\n "); | |
print_attr(curpart->tiles, verbose); | |
} | |
printf("\n "); | |
print_attr(curpart->displayWindow, verbose); | |
printf("\n "); | |
print_attr(curpart->dataWindow, verbose); | |
printf("\n "); | |
print_attr(curpart->channels, verbose); | |
printf("\n"); | |
if (curpart->tiles) | |
{ | |
printf( | |
" tiled image has levels: x %d y %d\n", | |
curpart->num_tile_levels_x, | |
curpart->num_tile_levels_y); | |
printf(" x tile count:"); | |
for (int l = 0; l < curpart->num_tile_levels_x; ++l) | |
printf( | |
" %d (sz %d)", | |
curpart->tile_level_tile_count_x[l], | |
curpart->tile_level_tile_size_x[l]); | |
printf("\n y tile count:"); | |
for (int l = 0; l < curpart->num_tile_levels_y; ++l) | |
printf( | |
" %d (sz %d)", | |
curpart->tile_level_tile_count_y[l], | |
curpart->tile_level_tile_size_y[l]); | |
printf("\n"); | |
} | |
else { | |
printf("This is a scanline encoded file\n"); | |
} | |
} | |
{ | |
// read using the real api, not by peeking under the hood | |
// as the info dump above does. | |
int numparts = 0; | |
rval = exr_get_count(f, &numparts); | |
if (rval != EXR_ERR_SUCCESS) { | |
fprintf(stderr, "could not fetch the number of parts\n"); | |
goto err; | |
} | |
for (int p = 0; p < numparts; ++p) | |
{ | |
exr_storage_t store; | |
rval = exr_get_storage(f, p, &store); | |
if (rval != EXR_ERR_SUCCESS) { | |
fprintf(stderr, "could not fetch the storage kind\n"); | |
goto err; | |
} | |
// not supporting deep images | |
if (store == EXR_STORAGE_DEEP_SCANLINE || store == EXR_STORAGE_DEEP_TILED) | |
continue; | |
bool reduceMemory = true; | |
bool reduceTime = true; | |
if (store == EXR_STORAGE_SCANLINE) | |
{ | |
if (readCoreScanlinePart(f, p, reduceMemory, reduceTime)) | |
return true; | |
} | |
else if (store == EXR_STORAGE_TILED) | |
{ | |
if (readCoreTiledPart(f, p, reduceMemory, reduceTime)) return true; | |
} | |
} | |
} | |
exr_finish(&f); | |
return 0; | |
err: | |
return 1; | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment