Skip to content

Instantly share code, notes, and snippets.

@meshula
Created January 5, 2023 02:59
Show Gist options
  • Save meshula/b573e2d31d475e24a1a6d515cb5c79af to your computer and use it in GitHub Desktop.
Save meshula/b573e2d31d475e24a1a6d515cb5c79af to your computer and use it in GitHub Desktop.
minimal-exr.c
#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