///|
fn image_attributes(
image : Image,
codec : Compression,
) -> Array[Attribute] raise ExrError {
let attrs = header_attributes(
image.data_window,
image.display_window,
image.channels(),
codec,
)
for i = 0; i < attrs.length(); i = i + 1 {
if attrs[i].name == "pixelAspectRatio" {
attrs[i] = Attribute::real("pixelAspectRatio", image.pixel_aspect)
}
if attrs[i].name == "screenWindowWidth" {
attrs[i] = Attribute::real("screenWindowWidth", image.screen_width)
}
if attrs[i].name == "screenWindowCenter" {
let s = sink()
s.f32(image.screen_x)
s.f32(image.screen_y)
attrs[i] = { name: "screenWindowCenter", kind: "v2f", value: s.finish(), }
}
}
let metadata = image.metadata.copy()
metadata.sort_by((a, b) => name_compare(a.name, b.name))
for attr in metadata {
attrs.push(attr)
}
attrs
}
///|
/// Deterministic scanline output; compression is lossless on sample bits.
pub fn encode(
image : Image,
compression~ : Compression = Zip,
limits~ : Limits = Limits::default(),
) -> Bytes raise ExrError {
encode_sources(
[{ name: "", compression, tiles: None, images: [image], }],
false,
limits,
)
}
///|
test "EXR multichannel lossless codecs crop projection and metadata roundtrip" {
let w = Window::new(-5, -3, 7, 15)
let count = w.pixels()
let image = Image::new(
w,
[
Plane::from_values(
{ name: "R", kind: F16, linear: false, },
w,
Array::makei(count, i => (i % 31).to_double() / 8.0),
),
Plane::from_values(
{ name: "Z", kind: F32, linear: true, },
w,
Array::makei(count, i => (i - 20).to_double()),
),
Plane::from_values(
{ name: "id", kind: U32, linear: true, },
w,
Array::makei(count, i => 4294967295.0 - i.to_double()),
),
],
metadata=[
Attribute::text("comments", "synthetic"),
{ name: "opaque", kind: "bytes", value: b"\xff\x00", },
],
pixel_aspect=2.0,
screen_center=(0.25, -0.5),
)
for codec in [None, Rle, Zips, Zip] {
let encoded = encode(image, compression=codec)
let file = ExrFile::open(encoded)
let restored = file.read()
assert_eq(restored.channels(), image.channels())
for ch in image.channels() {
assert_eq(
restored.plane(ch.name).sample_bits(),
image.plane(ch.name).sample_bits(),
)
}
assert_eq(restored.metadata(), image.metadata())
assert_eq(restored.pixel_aspect(), 2.0)
assert_eq(restored.screen(), (0.25, -0.5, 1.0))
let region = Window::new(-2, 0, 3, 3)
let result = file.read_with_stats(
region=Some(region),
channels=Some(["id"]),
)
assert_eq(result.image.window(), region)
assert_eq(result.image.channels().length(), 1)
assert_eq(
result.image.plane("id").bits_at(0, 2),
image.plane("id").bits_at(0, 2),
)
assert_eq(result.chunks_decoded, if codec == Zip { 1 } else { 4 })
assert_eq(encode(restored, compression=codec), encoded)
}
}