Files
armorpaint/paint/sources/export_exr.c
T
2026-03-09 13:17:15 +01:00

332 lines
8.4 KiB
C

#include "global.h"
// Based on miniexr.cpp - public domain - 2013 Aras Pranckevicius / Unity Technologies
// https://github.com/aras-p/miniexr
// https://www.openexr.com/documentation/openexrfilelayout.pdf
void export_exr_write_string(u8_array_t *out, char *str) {
for (i32 i = 0; i < string_length(str); ++i) {
u8_array_push(out, char_code_at(str, i));
}
}
void export_exr_write_line16(i32 byte_pos) {
for (i32 x = 0; x < _export_exr_width; ++x) {
u8_array_push(_export_exr_out, buffer_get_u8(_export_exr_src_view, byte_pos));
u8_array_push(_export_exr_out, buffer_get_u8(_export_exr_src_view, byte_pos + 1));
byte_pos += _export_exr_stride;
}
}
void export_exr_write_line32(i32 byte_pos) {
for (i32 x = 0; x < _export_exr_width; ++x) {
u8_array_push(_export_exr_out, buffer_get_u8(_export_exr_src_view, byte_pos));
u8_array_push(_export_exr_out, buffer_get_u8(_export_exr_src_view, byte_pos + 1));
u8_array_push(_export_exr_out, buffer_get_u8(_export_exr_src_view, byte_pos + 2));
u8_array_push(_export_exr_out, buffer_get_u8(_export_exr_src_view, byte_pos + 3));
byte_pos += _export_exr_stride;
}
}
void export_exr_write_bgr(i32 off, i32 pos, i32 byte_size) {
_export_exr_write_line(pos + byte_size * 2);
_export_exr_write_line(pos + byte_size);
_export_exr_write_line(pos);
}
void export_exr_write_single(i32 off, i32 pos, i32 byte_size) {
_export_exr_write_line(pos + off * byte_size);
_export_exr_write_line(pos + off * byte_size);
_export_exr_write_line(pos + off * byte_size);
}
buffer_t *export_exr_run(i32 width, i32 height, buffer_t *src, i32 bits, i32 type, i32 off) {
u8_array_t *out = u8_array_create_from_raw((u8[]){}, 0);
u8_array_push(out, 0x76); // magic
u8_array_push(out, 0x2f);
u8_array_push(out, 0x31);
u8_array_push(out, 0x01);
u8_array_push(out, 2); // version, scanline
u8_array_push(out, 0);
u8_array_push(out, 0);
u8_array_push(out, 0);
export_exr_write_string(out, "channels");
u8_array_push(out, 0);
export_exr_write_string(out, "chlist");
u8_array_push(out, 0);
u8_array_push(out, 55);
u8_array_push(out, 0);
u8_array_push(out, 0);
u8_array_push(out, 0);
i32 attrib = bits == 16 ? 1 : 2; // half, float
u8_array_push(out, char_code_at("B", 0)); // B
u8_array_push(out, 0);
u8_array_push(out, attrib);
u8_array_push(out, 0);
u8_array_push(out, 0);
u8_array_push(out, 0);
u8_array_push(out, 0);
u8_array_push(out, 0);
u8_array_push(out, 0);
u8_array_push(out, 0);
u8_array_push(out, 1);
u8_array_push(out, 0);
u8_array_push(out, 0);
u8_array_push(out, 0);
u8_array_push(out, 1);
u8_array_push(out, 0);
u8_array_push(out, 0);
u8_array_push(out, 0);
u8_array_push(out, char_code_at("G", 0)); // G
u8_array_push(out, 0);
u8_array_push(out, attrib);
u8_array_push(out, 0);
u8_array_push(out, 0);
u8_array_push(out, 0);
u8_array_push(out, 0);
u8_array_push(out, 0);
u8_array_push(out, 0);
u8_array_push(out, 0);
u8_array_push(out, 1);
u8_array_push(out, 0);
u8_array_push(out, 0);
u8_array_push(out, 0);
u8_array_push(out, 1);
u8_array_push(out, 0);
u8_array_push(out, 0);
u8_array_push(out, 0);
u8_array_push(out, char_code_at("R", 0)); // R
u8_array_push(out, 0);
u8_array_push(out, attrib);
u8_array_push(out, 0);
u8_array_push(out, 0);
u8_array_push(out, 0);
u8_array_push(out, 0);
u8_array_push(out, 0);
u8_array_push(out, 0);
u8_array_push(out, 0);
u8_array_push(out, 1);
u8_array_push(out, 0);
u8_array_push(out, 0);
u8_array_push(out, 0);
u8_array_push(out, 1);
u8_array_push(out, 0);
u8_array_push(out, 0);
u8_array_push(out, 0);
u8_array_push(out, 0);
export_exr_write_string(out, "compression");
u8_array_push(out, 0);
export_exr_write_string(out, "compression");
u8_array_push(out, 0);
u8_array_push(out, 1);
u8_array_push(out, 0);
u8_array_push(out, 0);
u8_array_push(out, 0);
u8_array_push(out, 0); // no compression
export_exr_write_string(out, "dataWindow");
u8_array_push(out, 0);
export_exr_write_string(out, "box2i");
u8_array_push(out, 0);
u8_array_push(out, 16);
u8_array_push(out, 0);
u8_array_push(out, 0);
u8_array_push(out, 0);
u8_array_push(out, 0);
u8_array_push(out, 0);
u8_array_push(out, 0);
u8_array_push(out, 0);
u8_array_push(out, 0);
u8_array_push(out, 0);
u8_array_push(out, 0);
u8_array_push(out, 0);
i32 ww = width - 1;
i32 hh = height - 1;
u8_array_push(out, ww & 0xff);
u8_array_push(out, (ww >> 8) & 0xff);
u8_array_push(out, (ww >> 16) & 0xff);
u8_array_push(out, (ww >> 24) & 0xff);
u8_array_push(out, hh & 0xff);
u8_array_push(out, (hh >> 8) & 0xff);
u8_array_push(out, (hh >> 16) & 0xff);
u8_array_push(out, (hh >> 24) & 0xff);
export_exr_write_string(out, "displayWindow");
u8_array_push(out, 0);
export_exr_write_string(out, "box2i");
u8_array_push(out, 0);
u8_array_push(out, 16);
u8_array_push(out, 0);
u8_array_push(out, 0);
u8_array_push(out, 0);
u8_array_push(out, 0);
u8_array_push(out, 0);
u8_array_push(out, 0);
u8_array_push(out, 0);
u8_array_push(out, 0);
u8_array_push(out, 0);
u8_array_push(out, 0);
u8_array_push(out, 0);
u8_array_push(out, ww & 0xff);
u8_array_push(out, (ww >> 8) & 0xff);
u8_array_push(out, (ww >> 16) & 0xff);
u8_array_push(out, (ww >> 24) & 0xff);
u8_array_push(out, hh & 0xff);
u8_array_push(out, (hh >> 8) & 0xff);
u8_array_push(out, (hh >> 16) & 0xff);
u8_array_push(out, (hh >> 24) & 0xff);
export_exr_write_string(out, "lineOrder");
u8_array_push(out, 0);
export_exr_write_string(out, "lineOrder");
u8_array_push(out, 0);
u8_array_push(out, 1);
u8_array_push(out, 0);
u8_array_push(out, 0);
u8_array_push(out, 0);
u8_array_push(out, 0); // increasing Y
export_exr_write_string(out, "pixelAspectRatio");
u8_array_push(out, 0);
export_exr_write_string(out, "float");
u8_array_push(out, 0);
u8_array_push(out, 4);
u8_array_push(out, 0);
u8_array_push(out, 0);
u8_array_push(out, 0);
u8_array_push(out, 0); // 1.0f
u8_array_push(out, 0);
u8_array_push(out, 0x80);
u8_array_push(out, 0x3f);
export_exr_write_string(out, "screenWindowCenter");
u8_array_push(out, 0);
export_exr_write_string(out, "v2f");
u8_array_push(out, 0);
u8_array_push(out, 8);
u8_array_push(out, 0);
u8_array_push(out, 0);
u8_array_push(out, 0);
u8_array_push(out, 0);
u8_array_push(out, 0);
u8_array_push(out, 0);
u8_array_push(out, 0);
u8_array_push(out, 0);
u8_array_push(out, 0);
u8_array_push(out, 0);
u8_array_push(out, 0);
export_exr_write_string(out, "screenWindowWidth");
u8_array_push(out, 0);
export_exr_write_string(out, "float");
u8_array_push(out, 0);
u8_array_push(out, 4);
u8_array_push(out, 0);
u8_array_push(out, 0);
u8_array_push(out, 0);
u8_array_push(out, 0); // 1.0f
u8_array_push(out, 0);
u8_array_push(out, 0x80);
u8_array_push(out, 0x3f);
u8_array_push(out, 0); // end of header
i32 channels = 4;
i32 byte_size = bits == 16 ? 2 : 4;
i32 k_header_size = out->length;
i32 k_scanline_table_size = 8 * height;
i32 pixel_row_size = width * 3 * byte_size;
i32 full_row_size = pixel_row_size + 8;
// line offset table
i32 ofs = k_header_size + k_scanline_table_size;
for (i32 y = 0; y < height; ++y) {
u8_array_push(out, ofs & 0xff);
u8_array_push(out, (ofs >> 8) & 0xff);
u8_array_push(out, (ofs >> 16) & 0xff);
u8_array_push(out, (ofs >> 24) & 0xff);
u8_array_push(out, 0);
u8_array_push(out, 0);
u8_array_push(out, 0);
u8_array_push(out, 0);
ofs += full_row_size;
}
// scanline data
i32 stride = channels * byte_size;
i32 pos = 0;
_export_exr_width = width;
_export_exr_stride = stride;
gc_unroot(_export_exr_out);
_export_exr_out = out;
gc_root(_export_exr_out);
gc_unroot(_export_exr_src_view);
_export_exr_src_view = src;
gc_root(_export_exr_src_view);
gc_unroot(_export_exr_write_line);
_export_exr_write_line = bits == 16 ? export_exr_write_line16 : export_exr_write_line32;
gc_root(_export_exr_write_line);
void (*write_data)(i32, i32, i32) = type == 1 ? export_exr_write_bgr : export_exr_write_single;
for (i32 y = 0; y < height; ++y) {
// coordinate
u8_array_push(out, y & 0xff);
u8_array_push(out, (y >> 8) & 0xff);
u8_array_push(out, (y >> 16) & 0xff);
u8_array_push(out, (y >> 24) & 0xff);
// data size
u8_array_push(out, pixel_row_size & 0xff);
u8_array_push(out, (pixel_row_size >> 8) & 0xff);
u8_array_push(out, (pixel_row_size >> 16) & 0xff);
u8_array_push(out, (pixel_row_size >> 24) & 0xff);
// data
write_data(off, pos, byte_size);
pos += width * stride;
}
return out;
}