///|
pub struct ReadResult {
image : Image
chunks_decoded : Int32
bytes_decoded : Int32
}
///|
fn selected_channels(
channels : Array[Channel],
selection : Array[String]?,
) -> Array[Channel] raise ExrError {
match selection {
None => channels.copy()
Some(names) => {
if names.is_empty() {
raise Invalid("empty channel selection", 0)
}
let seen : Map[String, Bool] = Map([])
let selected : Array[Channel] = []
for name in names {
if seen.contains(name) {
raise Invalid("duplicate selected channel", 0)
}
seen[name] = true
let mut found = false
for ch in channels {
if ch.name == name {
selected.push(ch)
found = true
break
}
}
if !found {
raise Invalid("missing selected channel: " + name, 0)
}
}
selected.sort_by((a, b) => name_compare(a.name, b.name))
selected
}
}
}
///|
pub fn ExrFile::read(
self : ExrFile,
region~ : Window? = None,
channels~ : Array[String]? = None,
) -> Image raise ExrError {
self.read_with_stats(region~, channels~).image
}
///|
pub fn ExrFile::read_with_stats(
self : ExrFile,
region~ : Window? = None,
channels~ : Array[String]? = None,
) -> ReadResult raise ExrError {
let h = self.header
let window = match region {
None => h.data_window
Some(w) =>
match w.intersection(h.data_window) {
Some(inside) => inside
None => raise Invalid("region does not intersect data window", 0)
}
}
let selected = selected_channels(h.channels, channels)
let buffers : Array[Array[Byte]] = []
let destinations : Map[String, Int32] = Map([])
for i = 0; i < selected.length(); i = i + 1 {
destinations[selected[i].name] = i
buffers.push(
Array::make(mul(window.pixels(), selected[i].kind.bytes()), b'\x00'),
)
}
let chunks = self.plan(window)
let mut bytes_decoded = 0
for info in chunks {
let raw = self.unpack_chunk(info)
bytes_decoded = add(bytes_decoded, raw.length())
let r = reader(raw)
for row = 0; row < info.rows; row = row + 1 {
let y = (info.y.to_int64() + row.to_int64()).to_int()
for ch in h.channels {
let width = ch.kind.bytes()
let row_bytes = mul(h.data_window.width(), width)
match destinations.get(ch.name) {
Some(destination) if y >= window.y0 && y <= window.y1 => {
let start = add(r.pos, mul(window.x0 - h.data_window.x0, width))
let count = mul(window.width(), width)
let into = mul(mul(y - window.y0, window.width()), width)
for i = 0; i < count; i = i + 1 {
buffers[destination][into + i] = raw[start + i]
}
}
_ => ()
}
r.pos += row_bytes
}
}
if r.pos != r.end {
raise Invalid("chunk layout byte count", info.offset)
}
}
let planes = selected.mapi((i, ch) => {
Plane::from_bits(
ch,
window,
Bytes::from_array(buffers[i]),
limits=self.limits,
)
})
let center = reader(h.attribute("screenWindowCenter").unwrap().value)
let image = Image::new(
window,
planes,
display=h.display_window,
metadata=h.metadata(),
pixel_aspect=h.attribute("pixelAspectRatio").unwrap().as_real(),
screen_center=(center.f32(), center.f32()),
screen_width=h.attribute("screenWindowWidth").unwrap().as_real(),
limits=self.limits,
)
{ image, chunks_decoded: chunks.length(), bytes_decoded, }
}