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