///|
pub(all) enum PixelType {
U32
F16
F32
} derive(Debug, Eq)
///|
pub extend PixelType with @debug.Debug::{to_repr}
///|
pub extend PixelType with Eq::{equal, not_equal}
///|
pub fn PixelType::bytes(self : PixelType) -> Int32 {
match self {
F16 => 2
U32 | F32 => 4
}
}
///|
fn PixelType::code(self : PixelType) -> Int32 {
match self {
U32 => 0
F16 => 1
F32 => 2
}
}
///|
fn pixel_type(code : Int32) -> PixelType raise ExrError {
match code {
0 => U32
1 => F16
2 => F32
_ => raise Unsupported("pixel representation")
}
}
///|
pub(all) struct Channel {
name : String
kind : PixelType
linear : Bool
} derive(Debug, Eq)
///|
pub extend Channel with @debug.Debug::{to_repr}
///|
pub extend Channel with Eq::{equal, not_equal}
///|
fn valid_name(name : String) -> Unit raise ExrError {
let bytes = @utf8.encode(name)
if bytes.length() == 0 || bytes.length() > 255 {
raise Invalid("name length must be 1..255 UTF-8 bytes", 0)
}
for b in bytes {
if b == 0 {
raise Invalid("NUL in name", 0)
}
}
}
///|
fn name_compare(a : String, b : String) -> Int32 {
let a = @utf8.encode(a)
let b = @utf8.encode(b)
for i = 0; i < imin(a.length(), b.length()); i = i + 1 {
if a[i] != b[i] {
return a[i].to_int() - b[i].to_int()
}
}
a.length() - b.length()
}
///|
pub struct Plane {
priv channel : Channel
priv window : Window
priv data : Bytes
}
///|
pub fn Plane::from_bits(
channel : Channel,
window : Window,
bits : Bytes,
limits~ : Limits = Limits::default(),
) -> Plane raise ExrError {
limits.validate()
valid_name(channel.name)
let pixels = window.pixels()
if pixels > limits.max_pixels {
raise Limit("plane pixel count")
}
let size = mul(pixels, channel.kind.bytes())
if size > limits.max_decoded_bytes {
raise Limit("plane byte budget")
}
if size != bits.length() {
raise Invalid("plane sample byte count", 0)
}
{ channel, window, data: bits, }
}
///|
pub fn Plane::channel(self : Plane) -> Channel {
self.channel
}
///|
pub fn Plane::window(self : Plane) -> Window {
self.window
}
///|
pub fn Plane::sample_bits(self : Plane) -> Bytes {
self.data
}
///|
pub fn Plane::bits_at(
self : Plane,
x : Int32,
y : Int32,
) -> UInt32 raise ExrError {
let width = self.channel.kind.bytes()
let start = mul(self.window.offset(x, y), width)
let mut bits = 0U
for i = 0; i < width; i = i + 1 {
bits = bits | (self.data[start + i].to_uint() << (8 * i))
}
bits
}
///|
pub fn Plane::value_at(
self : Plane,
x : Int32,
y : Int32,
) -> Double raise ExrError {
let bits = self.bits_at(x, y)
match self.channel.kind {
U32 => bits.to_double()
F16 => half_value(bits.reinterpret_as_int())
F32 => Float::reinterpret_from_uint(bits).to_double()
}
}
///|
pub fn Plane::from_values(
channel : Channel,
window : Window,
values : Array[Double],
limits~ : Limits = Limits::default(),
) -> Plane raise ExrError {
limits.validate()
let count = window.pixels()
if count > limits.max_pixels ||
mul(count, channel.kind.bytes()) > limits.max_decoded_bytes {
raise Limit("plane allocation")
}
if values.length() != count {
raise Invalid("plane value count", 0)
}
let out = sink()
for v in values {
match channel.kind {
U32 => {
if !finite(v) || v < 0.0 || v > 4294967295.0 || v != v.trunc() {
raise Invalid("UINT sample must be exact integer in 0..2^32-1", 0)
}
out.uint(v.to_uint64(), 4)
}
F32 => out.f32(v)
F16 =>
out.uint(
float_bits_to_half(Float::from_double(v).reinterpret_as_uint()).to_uint64(),
2,
)
}
}
Plane::from_bits(channel, window, out.finish(), limits~)
}
///|
test "typed planes preserve integer precision and bit payloads" {
let w = Window::new(-1, 7, 1, 7)
let p = Plane::from_values(
{ name: "object.id", kind: U32, linear: true, },
w,
[0.0, 16777217.0, 4294967295.0],
)
assert_eq(p.value_at(0, 7), 16777217.0)
assert_eq(p.bits_at(1, 7), 4294967295U)
assert_true(
try {
ignore(p.value_at(2, 7))
false
} catch {
_ => true
},
)
let raw = b"\x01\x00\xc0\x7f\x00\x00\x00\x80\x00\x00\x80\x7f"
let q = Plane::from_bits({ name: "Z", kind: F32, linear: false, }, w, raw)
assert_eq(q.sample_bits(), raw)
assert_true(q.value_at(-1, 7).is_nan())
assert_eq(q.bits_at(0, 7), 2147483648U)
}
///|
test "planes reject invalid byte shape names and UINT values" {
let w = Window::new(0, 0, 0, 0)
for value in [-1.0, 0.1, 4294967296.0] {
assert_true(
try {
ignore(
Plane::from_values({ name: "id", kind: U32, linear: true, }, w, [
value,
]),
)
false
} catch {
_ => true
},
)
}
assert_true(
try {
ignore(
Plane::from_bits(
{ name: "", kind: F16, linear: false, },
w,
b"\x00\x00",
),
)
false
} catch {
_ => true
},
)
assert_true(
try {
ignore(
Plane::from_bits({ name: "x", kind: F16, linear: false, }, w, b"\x00"),
)
false
} catch {
_ => true
},
)
}