///|
/// Encode velocity magnitude as a small RGB PNG image.
pub fn Lattice::to_png(self : Lattice) -> Bytes {
let raw = self.png_raw_rgb()
let idat = zlib_store(raw)
let out : Array[Byte] = [137, 80, 78, 71, 13, 10, 26, 10]
append_chunk(out, "IHDR", ihdr(self.size.width, self.size.height))
append_chunk(out, "IDAT", idat)
append_chunk(out, "IEND", [])
Bytes::from_array(out)
}
///|
fn Lattice::png_raw_rgb(self : Lattice) -> Array[Byte] {
let raw : Array[Byte] = Array::new(
capacity=self.size.height * (1 + self.size.width * 3),
)
let stable = self.stability()
let denom = if stable.max_speed <= 0.0 { 1.0 } else { stable.max_speed }
for y in 0.. Array[Byte] {
let out = Array::new(capacity=13)
append_u32(out, width)
append_u32(out, height)
out.push(8)
out.push(2)
out.push(0)
out.push(0)
out.push(0)
out
}
///|
fn zlib_store(data : Array[Byte]) -> Array[Byte] {
let out : Array[Byte] = [0x78, 0x01]
let mut pos = 0
while pos < data.length() {
let remain = data.length() - pos
let len = remain.min(65535)
let final_block = pos + len == data.length()
out.push(if final_block { 1 } else { 0 })
append_u16_le(out, len)
append_u16_le(out, 0xffff - len)
for i in pos..<(pos + len) {
out.push(data[i])
}
pos += len
}
append_u32(out, adler32(data).reinterpret_as_int())
out
}
///|
fn append_chunk(out : Array[Byte], kind : String, data : Array[Byte]) -> Unit {
append_u32(out, data.length())
let start = out.length()
append_ascii(out, kind)
out.append(data)
let crc_data = out[start:]
append_u32(out, crc32(crc_data).reinterpret_as_int())
}
///|
fn append_ascii(out : Array[Byte], s : String) -> Unit {
for ch in s {
out.push(ch.to_int().to_byte())
}
}
///|
fn append_u16_le(out : Array[Byte], value : Int) -> Unit {
out.push((value & 0xff).to_byte())
out.push(((value >> 8) & 0xff).to_byte())
}
///|
fn append_u32(out : Array[Byte], value : Int) -> Unit {
out.push(((value >> 24) & 0xff).to_byte())
out.push(((value >> 16) & 0xff).to_byte())
out.push(((value >> 8) & 0xff).to_byte())
out.push((value & 0xff).to_byte())
}
///|
fn adler32(data : Array[Byte]) -> UInt {
let mut a : UInt = 1
let mut b : UInt = 0
for byte in data {
a = (a + byte.to_uint()) % 65521
b = (b + a) % 65521
}
(b << 16) | a
}
///|
fn crc32(data : ArrayView[Byte]) -> UInt {
let mut crc : UInt = 0xffffffff
for byte in data {
crc = crc ^ byte.to_uint()
for _ in 0..<8 {
let mask : UInt = if (crc & 1) == 1 { 0xedb88320 } else { 0 }
crc = (crc >> 1) ^ mask
}
}
crc ^ 0xffffffff
}