///|
/// One presented animation sample with timing in the primary track's units.
/// Color and alpha retain their original precision, range and prem association.
/// Its buffers are independent of every other decoded sample and input data.
pub struct AvifNativeAnimationFrame {
index : Int
timestamp : Int
duration : Int
timescale : Int
image : AvifNativeImage
}
///|
/// A completely decoded AVIF animation in presentation order. On return,
/// frames is nonempty, index equals the array position, durations are positive,
/// and timestamps are contiguous from zero, in units of timescale per second.
/// The total duration fits in a positive Int. All frames share this timescale.
pub struct AvifNativeAnimation {
timescale : Int
frames : FixedArray[AvifNativeAnimationFrame]
}
///|
/// Decode the entire AVIF animation at 8/10/12-bit native precision.
/// Each color/alpha track keeps its own AV1 reference state. Track association,
/// exact time alignment, metadata and rejection follow avif_decode_animation;
/// color conversion and alpha range expansion/unpremultiplication are omitted.
/// None rejects the whole animation, including a malformed later sample.
fn avif_decode_animation_native_limited(
data : Array[Byte],
limits? : DecodeLimits? = None,
) -> AvifNativeAnimation? {
let decoded = match avif_decode_animation_planes(data, limits~) {
Some(value) => value
None => return None
}
let frames = FixedArray::makei(decoded.color.length(), fn(i) {
let timing = decoded.descriptor.frames[i]
AvifNativeAnimationFrame::{
index: i,
timestamp: timing.timestamp,
duration: timing.duration,
timescale: timing.timescale,
image: {
color: av1_frame_to_native(decoded.color[i]),
alpha: decoded.alpha.map(planes => av1_frame_to_native(planes[i])),
premultiplied: decoded.premultiplied,
},
}
})
Some({ timescale: decoded.descriptor.timescale, frames, })
}
///|
/// Select a frame without decoding again. timestamp uses this animation's
/// timescale, not milliseconds, and loops modulo the total duration.
/// Intervals include their start and exclude their end; negative timestamps
/// or caller-reordered/noncontiguous timing return None.
/// Returns the stored frame: its buffers alias frames[index], with no copy.
pub fn AvifNativeAnimation::frame_at(
self : AvifNativeAnimation,
timestamp : Int,
) -> AvifNativeAnimationFrame? {
if timestamp < 0 || self.timescale <= 0 || self.frames.length() == 0 {
return None
}
let mut total = 0
for i in 0.. 0x7FFFFFFF - total {
return None
}
total = total + frame.duration
}
let t = timestamp % total
for frame in self.frames {
if t < frame.timestamp + frame.duration {
return Some(frame)
}
}
None
}