1use std::cmp::Ordering;
2use std::time::Duration;
3
4use crate::error::ImageResult;
5use crate::RgbaImage;
6
7pub struct Frames<'a> {
9 iterator: Box<dyn Iterator<Item = ImageResult<Frame>> + 'a>,
10}
11
12impl<'a> Frames<'a> {
13 #[must_use]
15 pub fn new(iterator: Box<dyn Iterator<Item = ImageResult<Frame>> + 'a>) -> Self {
16 Frames { iterator }
17 }
18
19 pub fn collect_frames(self) -> ImageResult<Vec<Frame>> {
25 self.collect()
26 }
27}
28
29impl Iterator for Frames<'_> {
30 type Item = ImageResult<Frame>;
31 fn next(&mut self) -> Option<ImageResult<Frame>> {
32 self.iterator.next()
33 }
34}
35
36pub struct Frame {
38 delay: Delay,
40 left: u32,
42 top: u32,
44 buffer: RgbaImage,
45}
46
47impl Clone for Frame {
48 fn clone(&self) -> Self {
49 Self {
50 delay: self.delay,
51 left: self.left,
52 top: self.top,
53 buffer: self.buffer.clone(),
54 }
55 }
56
57 fn clone_from(&mut self, source: &Self) {
58 self.delay = source.delay;
59 self.left = source.left;
60 self.top = source.top;
61 self.buffer.clone_from(&source.buffer);
62 }
63}
64
65#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd)]
67pub struct Delay {
68 ratio: Ratio,
69}
70
71impl Frame {
72 #[must_use]
74 pub fn new(buffer: RgbaImage) -> Frame {
75 Frame {
76 delay: Delay::from_ratio(Ratio { numer: 0, denom: 1 }),
77 left: 0,
78 top: 0,
79 buffer,
80 }
81 }
82
83 #[must_use]
85 pub fn from_parts(buffer: RgbaImage, left: u32, top: u32, delay: Delay) -> Frame {
86 Frame {
87 delay,
88 left,
89 top,
90 buffer,
91 }
92 }
93
94 #[must_use]
96 pub fn delay(&self) -> Delay {
97 self.delay
98 }
99
100 #[must_use]
102 pub fn buffer(&self) -> &RgbaImage {
103 &self.buffer
104 }
105
106 pub fn buffer_mut(&mut self) -> &mut RgbaImage {
108 &mut self.buffer
109 }
110
111 #[must_use]
113 pub fn into_buffer(self) -> RgbaImage {
114 self.buffer
115 }
116
117 #[must_use]
119 pub fn left(&self) -> u32 {
120 self.left
121 }
122
123 #[must_use]
125 pub fn top(&self) -> u32 {
126 self.top
127 }
128}
129
130impl Delay {
131 #[must_use]
140 pub fn from_numer_denom_ms(numerator: u32, denominator: u32) -> Self {
141 Delay {
142 ratio: Ratio::new(numerator, denominator),
143 }
144 }
145
146 #[must_use]
161 pub fn from_saturating_duration(duration: Duration) -> Self {
162 const MILLIS_BOUND: u128 = u32::MAX as u128;
171
172 let millis = duration.as_millis().min(MILLIS_BOUND);
173 let submillis = (duration.as_nanos() % 1_000_000) as u32;
174
175 let max_b = if millis > 0 {
176 ((MILLIS_BOUND + 1) / (millis + 1)) as u32
177 } else {
178 MILLIS_BOUND as u32
179 };
180 let millis = millis as u32;
181
182 let (a, b) = Self::closest_bounded_fraction(max_b, submillis, 1_000_000);
183 Self::from_numer_denom_ms(a + b * millis, b)
184 }
185
186 #[must_use]
191 pub fn numer_denom_ms(self) -> (u32, u32) {
192 (self.ratio.numer, self.ratio.denom)
193 }
194
195 pub(crate) fn from_ratio(ratio: Ratio) -> Self {
196 Delay { ratio }
197 }
198
199 pub(crate) fn into_ratio(self) -> Ratio {
200 self.ratio
201 }
202
203 fn closest_bounded_fraction(denom_bound: u32, nom: u32, denom: u32) -> (u32, u32) {
208 use std::cmp::Ordering::*;
209 assert!(0 < denom);
210 assert!(0 < denom_bound);
211 assert!(nom < denom);
212
213 fn compare_fraction((an, ad): (u64, u64), (bn, bd): (u64, u64)) -> Ordering {
219 (an * bd).cmp(&(bn * ad))
220 }
221
222 fn abs_diff_nom((an, ad): (u64, u64), (bn, bd): (u64, u64)) -> u64 {
224 let c0 = an * bd;
225 let c1 = ad * bn;
226
227 let d0 = c0.max(c1);
228 let d1 = c0.min(c1);
229 d0 - d1
230 }
231
232 let exact = (u64::from(nom), u64::from(denom));
233 let mut lower = (0u64, 1u64);
235 let mut upper = (1u64, 1u64);
237 let mut guess = (u64::from(nom * 2 > denom), 1u64);
239
240 loop {
243 if compare_fraction(guess, exact) == Equal {
245 break;
246 }
247
248 if u64::from(denom_bound) - lower.1 < upper.1 {
250 break;
251 }
252
253 let next = (lower.0 + upper.0, lower.1 + upper.1);
255 if compare_fraction(exact, next) == Less {
257 upper = next;
258 } else {
259 lower = next;
260 }
261
262 let g_diff_nom = abs_diff_nom(guess, exact);
268 let n_diff_nom = abs_diff_nom(next, exact);
270
271 if match (n_diff_nom / next.1).cmp(&(g_diff_nom / guess.1)) {
275 Less => true,
276 Greater => false,
277 Equal => {
282 compare_fraction(
283 (n_diff_nom % next.1, next.1),
284 (g_diff_nom % guess.1, guess.1),
285 ) == Less
286 }
287 } {
288 guess = next;
289 }
290 }
291
292 (guess.0 as u32, guess.1 as u32)
293 }
294}
295
296impl From<Delay> for Duration {
297 fn from(delay: Delay) -> Self {
298 let ratio = delay.into_ratio();
299 let ms = ratio.to_integer();
300 let rest = ratio.numer % ratio.denom;
301 let nanos = (u64::from(rest) * 1_000_000) / u64::from(ratio.denom);
302 Duration::from_millis(ms.into()) + Duration::from_nanos(nanos)
303 }
304}
305
306#[derive(Copy, Clone, Debug)]
307pub(crate) struct Ratio {
308 numer: u32,
309 denom: u32,
310}
311
312impl Ratio {
313 #[inline]
314 pub(crate) fn new(numerator: u32, denominator: u32) -> Self {
315 assert_ne!(denominator, 0);
316 Self {
317 numer: numerator,
318 denom: denominator,
319 }
320 }
321
322 #[inline]
323 pub(crate) fn to_integer(self) -> u32 {
324 self.numer / self.denom
325 }
326}
327
328impl PartialEq for Ratio {
329 fn eq(&self, other: &Self) -> bool {
330 self.cmp(other) == Ordering::Equal
331 }
332}
333
334impl Eq for Ratio {}
335
336impl PartialOrd for Ratio {
337 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
338 Some(self.cmp(other))
339 }
340}
341
342impl Ord for Ratio {
343 fn cmp(&self, other: &Self) -> Ordering {
344 let a: u32 = self.numer;
352 let b: u32 = self.denom;
353 let c: u32 = other.numer;
354 let d: u32 = other.denom;
355
356 (a as u64 * d as u64).cmp(&(c as u64 * b as u64))
360 }
361}
362
363#[cfg(test)]
364mod tests {
365 use super::{Delay, Duration, Ratio};
366
367 #[test]
368 fn simple() {
369 let second = Delay::from_numer_denom_ms(1000, 1);
370 assert_eq!(Duration::from(second), Duration::from_secs(1));
371 }
372
373 #[test]
374 fn fps_30() {
375 let thirtieth = Delay::from_numer_denom_ms(1000, 30);
376 let duration = Duration::from(thirtieth);
377 assert_eq!(duration.as_secs(), 0);
378 assert_eq!(duration.subsec_millis(), 33);
379 assert_eq!(duration.subsec_nanos(), 33_333_333);
380 }
381
382 #[test]
383 fn duration_outlier() {
384 let oob = Duration::from_secs(0xFFFF_FFFF);
385 let delay = Delay::from_saturating_duration(oob);
386 assert_eq!(delay.numer_denom_ms(), (0xFFFF_FFFF, 1));
387 }
388
389 #[test]
390 fn duration_approx() {
391 let oob = Duration::from_millis(0xFFFF_FFFF) + Duration::from_micros(1);
392 let delay = Delay::from_saturating_duration(oob);
393 assert_eq!(delay.numer_denom_ms(), (0xFFFF_FFFF, 1));
394
395 let inbounds = Duration::from_millis(0xFFFF_FFFF) - Duration::from_micros(1);
396 let delay = Delay::from_saturating_duration(inbounds);
397 assert_eq!(delay.numer_denom_ms(), (0xFFFF_FFFF, 1));
398
399 let fine =
400 Duration::from_millis(0xFFFF_FFFF / 1000) + Duration::from_micros(0xFFFF_FFFF % 1000);
401 let delay = Delay::from_saturating_duration(fine);
402 assert_eq!(delay.into_ratio(), Ratio::new(0xFFFF_FFFF, 1000));
404 }
405
406 #[test]
407 fn precise() {
408 let exceed = Duration::from_secs(333) + Duration::from_nanos(333_333_333);
411 let delay = Delay::from_saturating_duration(exceed);
412 assert_eq!(Duration::from(delay), exceed);
413 }
414
415 #[test]
416 fn small() {
417 let delay = Delay::from_numer_denom_ms(1 << 16, (1 << 16) + 1);
419 let duration = Duration::from(delay);
420 assert_eq!(duration.as_millis(), 0);
421 let delay = Delay::from_saturating_duration(duration);
423 assert_eq!(delay.into_ratio().to_integer(), 0);
424 }
425}