AcademySoftwareFoundation / AcademySoftwareFoundation/openexr

OpenEXRCore 3.1.1 - A detailed writing example

Open
#1,900 6 comments 0 reactions 0 assignees View on GitHub
needs attention
Dominant language
C
Stars
1.8k
Forks
700
Avg merge
3d 12h
Merged PRs (30d)
29

Description

I'm working on a function that writes EXRs using Core only and what I have below does the job except all pixels have the same value (i.e. we get a constant color image) eventually. The value in questions seems to be the the RGBA value of the upper left most pixel. I couldn't find a detailed exr write example in the documentation, so I looked at the src of the EXR tests which helped a lot to get to the function here:

```
void writeEXR(Image *image, char *filename) {
int pixel_count = image->width * image->height;
size_t total_size = pixel_count * 4 * sizeof(float);

// PREPARE RAW IMAGE DATA
uint8_t *data = malloc(total_size);
memset(data, 0, total_size);

for (int y = 0; y < image->height; y++) { // Iterate over the lines
for (int x = 0; x < image->width; x++) { // Iterate over the line pixels
unsigned index = (y * image->width + x) * sizeof(float);
printf("%d ", index);
// This should give us planar layout (e.g. 4x1 px):
// 0__________________ 1__________________ 2__________________ 3__________________
// 0___ 4___ 8___ 12__ 0___ 4___ 8___ 12__ 0___ 4___ 8___ 12__ 0___ 4___ 8___ 12__
// RRRR RRRR RRRR RRRR | GGGG GGGG GGGG GGGG | BBBB BBBB BBBB BBBB | AAAA AAAA AAAA AAAA

memcpy(data + (pixel_count * 0 * sizeof(float) + index), &image->pixels[y][x].color.r, sizeof(float));
memcpy(data + (pixel_count * 1 * sizeof(float) + index), &image->pixels[y][x].color.g, sizeof(float));
memcpy(data + (pixel_count * 2 * sizeof(float) + index), &image->pixels[y][x].color.b, sizeof(float));
memcpy(data + (pixel_count * 3 * sizeof(float) + index), &image->pixels[y][x].alpha, sizeof(float));
}
}

// INIT EXR
exr_context_initializer_t ctxinit = EXR_DEFAULT_CONTEXT_INITIALIZER;
ctxinit.max_image_width = image->width;
ctxinit.max_image_height = image->height;

exr_context_t out;
int partidx = 0;

exr_start_write(&out, filename, EXR_WRITE_FILE_DIRECTLY, &ctxinit);

exr_get_count(out, &partidx);
assert(partidx == 0);

exr_add_part(out, "Ci", EXR_STORAGE_SCANLINE, &partidx);
assert(partidx == 0);

exr_initialize_required_attr_simple(out, partidx, image->width, image->height, EXR_COMPRESSION_ZIPS);

exr_add_channel(out, partidx, "A", EXR_PIXEL_FLOAT, EXR_PERCEPTUALLY_LINEAR, 1, 1);
exr_add_channel(out, partidx, "B", EXR_PIXEL_FLOAT, EXR_PERCEPTUALLY_LOGARITHMIC, 1, 1);
exr_add_channel(out, partidx, "G", EXR_PIXEL_FLOAT, EXR_PERCEPTUALLY_LOGARITHMIC, 1, 1);
exr_add_channel(out, partidx, "R", EXR_PIXEL_FLOAT, EXR_PERCEPTUALLY_LOGARITHMIC, 1, 1);

exr_attr_set_user(out, partidx, "user", "mytype", 4, "foo");
exr_write_header(out);

int nchunks = 0;
exr_get_chunk_count(out, partidx, &nchunks);

exr_chunk_info_t cinfo;
exr_encode_pipeline_t encoder;

exr_write_scanline_chunk_info(out, partidx, 0, &cinfo);
exr_encoding_initialize(out, partidx, &cinfo, &encoder);

encoder.channels[0].channel_name = "A";
encoder.channels[0].encode_from_ptr = &data[pixel_count * sizeof(float) * 3];
encoder.channels[0].user_data_type = EXR_PIXEL_FLOAT;
encoder.channels[0].user_pixel_stride = 1 * sizeof(float);
encoder.channels[0].user_line_stride = sizeof(float) * image->width;
encoder.channels[0].user_bytes_per_element = sizeof(float);

encoder.channels[1].channel_name = "B";
encoder.channels[1].encode_from_ptr = &data[pixel_count * sizeof(float) * 2];
encoder.channels[1].user_data_type = EXR_PIXEL_FLOAT;
encoder.channels[1].user_pixel_stride = 1 * sizeof(float);
encoder.channels[1].user_line_stride = sizeof(float) * image->width;
encoder.channels[1].user_bytes_per_element = sizeof(float);

encoder.channels[2].channel_name = "G";
encoder.channels[2].encode_from_ptr = &data[pixel_count * sizeof(float) * 1];
encoder.channels[2].user_data_type = EXR_PIXEL_FLOAT;
encoder.channels[2].user_pixel_stride = 1 * sizeof(float);
encoder.channels[2].user_line_stride = sizeof(float) * image->width;
encoder.channels[2].user_bytes_per_element = sizeof(float);

encoder.channels[3].channel_name = "R";
encoder.channels[3].encode_from_ptr = &data[pixel_count * sizeof(float) * 0];
encoder.channels[3].user_data_type = EXR_PIXEL_FLOAT;
encoder.channels[3].user_pixel_stride = 1 * sizeof(float);
encoder.channels[3].user_line_stride = sizeof(float) * image->width;
encoder.channels[3].user_bytes_per_element = 4;

exr_encoding_choose_default_routines(out, partidx, &encoder);

for (int y = 0; y < nchunks; y++) {
if (y > 0) {
exr_write_scanline_chunk_info(out, partidx, y, &cinfo);
exr_encoding_update(out, partidx, &cinfo, &encoder);
}
exr_encoding_run(out, partidx, &encoder);
}

exr_encoding_destroy(out, &encoder);
exr_finish(&out);
}
```

Yet, I must be doing something wrong. Can anybody spot the problem? A nudge in the right direction would be awesome! Thanks!

Contributor guide

Open the contributing guide

Research direction

Start with the EXR tests referenced in the issue and compare their OpenEXRCore write flow with the supplied writeEXR function, especially the channel buffers and scanline handling. Done means providing a detailed, verified writing example in the documentation that produces varying pixel values rather than a constant-color image.

Written by the indexing model from the issue text.

Assessment

Tech stack
c
Domain
computer-graphics
Issue type
Documentation
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.