Coverage Report

Created: 2025-07-11 07:25

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