Skip to main content

ai_image/
animation.rs

1use alloc::{boxed::Box, vec::Vec};
2use core::cmp::Ordering;
3use core::time::Duration;
4
5use crate::error::ImageResult;
6use crate::RgbaImage;
7
8/// An implementation dependent iterator, reading the frames as requested
9pub struct Frames<'a> {
10    iterator: Box<dyn Iterator<Item = ImageResult<Frame>> + 'a>,
11}
12
13impl<'a> Frames<'a> {
14    /// Creates a new `Frames` from an implementation specific iterator.
15    #[must_use]
16    pub fn new(iterator: Box<dyn Iterator<Item = ImageResult<Frame>> + 'a>) -> Self {
17        Frames { iterator }
18    }
19
20    /// Steps through the iterator from the current frame until the end and pushes each frame into
21    /// a `Vec`.
22    /// If en error is encountered that error is returned instead.
23    ///
24    /// Note: This is equivalent to `Frames::collect::<ImageResult<Vec<Frame>>>()`
25    pub fn collect_frames(self) -> ImageResult<Vec<Frame>> {
26        self.collect()
27    }
28}
29
30impl Iterator for Frames<'_> {
31    type Item = ImageResult<Frame>;
32
33    fn next(&mut self) -> Option<ImageResult<Frame>> {
34        self.iterator.next()
35    }
36}
37
38/// A single animation frame
39pub struct Frame {
40    /// Delay between the frames in milliseconds
41    delay: Delay,
42    /// x offset
43    left: u32,
44    /// y offset
45    top: u32,
46    buffer: RgbaImage,
47}
48
49impl Clone for Frame {
50    fn clone(&self) -> Self {
51        Self {
52            delay: self.delay,
53            left: self.left,
54            top: self.top,
55            buffer: self.buffer.clone(),
56        }
57    }
58
59    fn clone_from(&mut self, source: &Self) {
60        self.delay = source.delay;
61        self.left = source.left;
62        self.top = source.top;
63        self.buffer.clone_from(&source.buffer);
64    }
65}
66
67/// The delay of a frame relative to the previous one.
68///
69/// The ratio is reduced on construction which means equality comparisons is reliable even when
70/// mixing different bases. Note however that there is an upper limit to the delays that can be
71/// represented exactly when using [`Self::from_saturating_duration`] which depends on the
72/// granularity of the interval.
73///
74/// ```
75/// use ai_image::Delay;
76/// let delay_10ms = Delay::from_numer_denom_ms(10, 1);
77/// let delay_10000us = Delay::from_numer_denom_ms(10_000, 1_000);
78///
79/// assert_eq!(delay_10ms, delay_10000us);
80/// ```
81#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd)]
82pub struct Delay {
83    ratio: Ratio,
84}
85
86impl Frame {
87    /// Constructs a new frame without any delay.
88    #[must_use]
89    pub fn new(buffer: RgbaImage) -> Frame {
90        Frame {
91            delay: Delay::from_ratio(Ratio { numer: 0, denom: 1 }),
92            left: 0,
93            top: 0,
94            buffer,
95        }
96    }
97
98    /// Constructs a new frame
99    #[must_use]
100    pub fn from_parts(buffer: RgbaImage, left: u32, top: u32, delay: Delay) -> Frame {
101        Frame {
102            delay,
103            left,
104            top,
105            buffer,
106        }
107    }
108
109    /// Delay of this frame
110    #[must_use]
111    pub fn delay(&self) -> Delay {
112        self.delay
113    }
114
115    /// Returns the image buffer
116    #[must_use]
117    pub fn buffer(&self) -> &RgbaImage {
118        &self.buffer
119    }
120
121    /// Returns a mutable image buffer
122    pub fn buffer_mut(&mut self) -> &mut RgbaImage {
123        &mut self.buffer
124    }
125
126    /// Returns the image buffer
127    #[must_use]
128    pub fn into_buffer(self) -> RgbaImage {
129        self.buffer
130    }
131
132    /// Returns the x offset
133    #[must_use]
134    pub fn left(&self) -> u32 {
135        self.left
136    }
137
138    /// Returns the y offset
139    #[must_use]
140    pub fn top(&self) -> u32 {
141        self.top
142    }
143}
144
145impl Delay {
146    /// Create a delay from a ratio of milliseconds.
147    ///
148    /// # Examples
149    ///
150    /// ```
151    /// use ai_image::Delay;
152    /// let delay_10ms = Delay::from_numer_denom_ms(10, 1);
153    /// ```
154    #[must_use]
155    pub fn from_numer_denom_ms(numerator: u32, denominator: u32) -> Self {
156        Delay {
157            ratio: Ratio::new(numerator, denominator),
158        }
159    }
160
161    /// Convert from a duration, clamped between 0 and an implemented defined maximum.
162    ///
163    /// The maximum is *at least* `i32::MAX` milliseconds. It should be noted that the accuracy of
164    /// the result may be relative and very large delays have a coarse resolution.
165    ///
166    /// # Examples
167    ///
168    /// ```
169    /// use core::time::Duration;
170    /// use ai_image::Delay;
171    ///
172    /// let duration = Duration::from_millis(20);
173    /// let delay = Delay::from_saturating_duration(duration);
174    /// ```
175    #[must_use]
176    pub fn from_saturating_duration(duration: Duration) -> Self {
177        // A few notes: The largest number we can represent as a ratio is u32::MAX but we can
178        // sometimes represent much smaller numbers.
179        //
180        // We can represent duration as `millis+a/b` (where a < b, b > 0).
181        // We must thus bound b with `b·millis + (b-1) <= u32::MAX` or
182        // > `0 < b <= (u32::MAX + 1)/(millis + 1)`
183        // Corollary: millis <= u32::MAX
184
185        const MILLIS_BOUND: u128 = u32::MAX as u128;
186
187        let millis = duration.as_millis().min(MILLIS_BOUND);
188        let submillis = (duration.as_nanos() % 1_000_000) as u32;
189
190        let max_b = if millis > 0 {
191            ((MILLIS_BOUND + 1) / (millis + 1)) as u32
192        } else {
193            MILLIS_BOUND as u32
194        };
195        let millis = millis as u32;
196
197        let (a, b) = Self::closest_bounded_fraction(max_b, submillis, 1_000_000);
198        Self::from_numer_denom_ms(a + b * millis, b)
199    }
200
201    /// The numerator and denominator of the delay in milliseconds.
202    ///
203    /// This is guaranteed to be an exact conversion if the `Delay` was previously created with the
204    /// `from_numer_denom_ms` constructor.
205    #[must_use]
206    pub fn numer_denom_ms(self) -> (u32, u32) {
207        (self.ratio.numer, self.ratio.denom)
208    }
209
210    pub(crate) fn from_ratio(ratio: Ratio) -> Self {
211        Delay { ratio }
212    }
213
214    pub(crate) fn into_ratio(self) -> Ratio {
215        self.ratio
216    }
217
218    /// Given some fraction, compute an approximation with denominator bounded.
219    ///
220    /// Note that `denom_bound` bounds nominator and denominator of all intermediate
221    /// approximations and the end result.
222    fn closest_bounded_fraction(denom_bound: u32, nom: u32, denom: u32) -> (u32, u32) {
223        use core::cmp::Ordering::*;
224        assert!(0 < denom);
225        assert!(0 < denom_bound);
226        assert!(nom < denom);
227
228        // Avoid a few type troubles. All intermediate results are bounded by `denom_bound` which
229        // is in turn bounded by u32::MAX. Representing with u64 allows multiplication of any two
230        // values without fears of overflow.
231
232        // Compare two fractions whose parts fit into a u32.
233        fn compare_fraction((an, ad): (u64, u64), (bn, bd): (u64, u64)) -> Ordering {
234            (an * bd).cmp(&(bn * ad))
235        }
236
237        // Computes the nominator of the absolute difference between two such fractions.
238        fn abs_diff_nom((an, ad): (u64, u64), (bn, bd): (u64, u64)) -> u64 {
239            let c0 = an * bd;
240            let c1 = ad * bn;
241
242            let d0 = c0.max(c1);
243            let d1 = c0.min(c1);
244            d0 - d1
245        }
246
247        let exact = (u64::from(nom), u64::from(denom));
248        // The lower bound fraction, numerator and denominator.
249        let mut lower = (0u64, 1u64);
250        // The upper bound fraction, numerator and denominator.
251        let mut upper = (1u64, 1u64);
252        // The closest approximation for now.
253        let mut guess = (u64::from(nom * 2 > denom), 1u64);
254
255        // loop invariant: ad, bd <= denom_bound
256        // iterates the Farey sequence.
257        loop {
258            // Break if we are done.
259            if compare_fraction(guess, exact) == Equal {
260                break;
261            }
262
263            // Break if next Farey number is out-of-range.
264            if u64::from(denom_bound) - lower.1 < upper.1 {
265                break;
266            }
267
268            // Next Farey approximation n between a and b
269            let next = (lower.0 + upper.0, lower.1 + upper.1);
270            // if F < n then replace the upper bound, else replace lower.
271            if compare_fraction(exact, next) == Less {
272                upper = next;
273            } else {
274                lower = next;
275            }
276
277            // Now correct the closest guess.
278            // In other words, if |c - f| > |n - f| then replace it with the new guess.
279            // This favors the guess with smaller denominator on equality.
280
281            // |g - f| = |g_diff_nom|/(gd*fd);
282            let g_diff_nom = abs_diff_nom(guess, exact);
283            // |n - f| = |n_diff_nom|/(nd*fd);
284            let n_diff_nom = abs_diff_nom(next, exact);
285
286            // The difference |n - f| is smaller than |g - f| if either the integral part of the
287            // fraction |n_diff_nom|/nd is smaller than the one of |g_diff_nom|/gd or if they are
288            // the same but the fractional part is larger.
289            if match (n_diff_nom / next.1).cmp(&(g_diff_nom / guess.1)) {
290                Less => true,
291                Greater => false,
292                // Note that the nominator for the fractional part is smaller than its denominator
293                // which is smaller than u32 and can't overflow the multiplication with the other
294                // denominator, that is we can compare these fractions by multiplication with the
295                // respective other denominator.
296                Equal => {
297                    compare_fraction(
298                        (n_diff_nom % next.1, next.1),
299                        (g_diff_nom % guess.1, guess.1),
300                    ) == Less
301                }
302            } {
303                guess = next;
304            }
305        }
306
307        (guess.0 as u32, guess.1 as u32)
308    }
309}
310
311impl From<Delay> for Duration {
312    fn from(delay: Delay) -> Self {
313        let ratio = delay.into_ratio();
314        let ms = ratio.to_integer();
315        let rest = ratio.numer % ratio.denom;
316        let nanos = (u64::from(rest) * 1_000_000) / u64::from(ratio.denom);
317        Duration::from_millis(ms.into()) + Duration::from_nanos(nanos)
318    }
319}
320
321#[inline]
322const fn gcd(mut a: u32, mut b: u32) -> u32 {
323    while b != 0 {
324        (a, b) = (b, a.rem_euclid(b));
325    }
326    a
327}
328
329#[derive(Copy, Clone, Debug)]
330pub(crate) struct Ratio {
331    numer: u32,
332    denom: u32,
333}
334
335impl Ratio {
336    #[inline]
337    pub(crate) fn new(numerator: u32, denominator: u32) -> Self {
338        assert_ne!(denominator, 0);
339        let divisor = gcd(numerator, denominator);
340        Self {
341            numer: numerator / divisor,
342            denom: denominator / divisor,
343        }
344    }
345
346    #[inline]
347    pub(crate) fn to_integer(self) -> u32 {
348        self.numer / self.denom
349    }
350}
351
352impl PartialEq for Ratio {
353    fn eq(&self, other: &Self) -> bool {
354        self.cmp(other) == Ordering::Equal
355    }
356}
357
358impl Eq for Ratio {}
359
360impl PartialOrd for Ratio {
361    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
362        Some(self.cmp(other))
363    }
364}
365
366impl Ord for Ratio {
367    fn cmp(&self, other: &Self) -> Ordering {
368        // The following comparison can be simplified:
369        // a / b <cmp> c / d
370        // We multiply both sides by `b`:
371        // a <cmp> c * b / d
372        // We multiply both sides by `d`:
373        // a * d <cmp> c * b
374
375        let a: u32 = self.numer;
376        let b: u32 = self.denom;
377        let c: u32 = other.numer;
378        let d: u32 = other.denom;
379
380        // We cast the types from `u32` to `u64` in order
381        // to not overflow the multiplications.
382
383        (u64::from(a) * u64::from(d)).cmp(&(u64::from(c) * u64::from(b)))
384    }
385}
386
387#[cfg(test)]
388mod tests {
389    use super::{Delay, Duration, Ratio};
390
391    #[test]
392    fn simple() {
393        let second = Delay::from_numer_denom_ms(1000, 1);
394        assert_eq!(Duration::from(second), Duration::from_secs(1));
395    }
396
397    #[test]
398    fn fps_30() {
399        let thirtieth = Delay::from_numer_denom_ms(1000, 30);
400        let duration = Duration::from(thirtieth);
401        assert_eq!(duration.as_secs(), 0);
402        assert_eq!(duration.subsec_millis(), 33);
403        assert_eq!(duration.subsec_nanos(), 33_333_333);
404    }
405
406    #[test]
407    fn duration_outlier() {
408        let oob = Duration::from_secs(0xFFFF_FFFF);
409        let delay = Delay::from_saturating_duration(oob);
410        assert_eq!(delay.numer_denom_ms(), (0xFFFF_FFFF, 1));
411    }
412
413    #[test]
414    fn duration_approx() {
415        let oob = Duration::from_millis(0xFFFF_FFFF) + Duration::from_micros(1);
416        let delay = Delay::from_saturating_duration(oob);
417        assert_eq!(delay.numer_denom_ms(), (0xFFFF_FFFF, 1));
418
419        let inbounds = Duration::from_millis(0xFFFF_FFFF) - Duration::from_micros(1);
420        let delay = Delay::from_saturating_duration(inbounds);
421        assert_eq!(delay.numer_denom_ms(), (0xFFFF_FFFF, 1));
422
423        let fine =
424            Duration::from_millis(0xFFFF_FFFF / 1000) + Duration::from_micros(0xFFFF_FFFF % 1000);
425        let delay = Delay::from_saturating_duration(fine);
426        // Funnily, 0xFFFF_FFFF is divisble by 5, thus we compare with a `Ratio`.
427        assert_eq!(delay.into_ratio(), Ratio::new(0xFFFF_FFFF, 1000));
428    }
429
430    #[test]
431    fn precise() {
432        // The ratio has only 32 bits in the numerator, too imprecise to get more than 11 digits
433        // correct. But it may be expressed as 1_000_000/3 instead.
434        let exceed = Duration::from_secs(333) + Duration::from_nanos(333_333_333);
435        let delay = Delay::from_saturating_duration(exceed);
436        assert_eq!(Duration::from(delay), exceed);
437    }
438
439    #[test]
440    fn small() {
441        // Not quite a delay of `1 ms`.
442        let delay = Delay::from_numer_denom_ms(1 << 16, (1 << 16) + 1);
443        let duration = Duration::from(delay);
444        assert_eq!(duration.as_millis(), 0);
445        // Not precisely the original but should be smaller than 0.
446        let delay = Delay::from_saturating_duration(duration);
447        assert_eq!(delay.into_ratio().to_integer(), 0);
448    }
449}