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 pub fn new(iterator: Box<dyn Iterator<Item = ImageResult<Frame>> + 'a>) -> Self {
15 Frames { iterator }
16 }
17
18 pub fn collect_frames(self) -> ImageResult<Vec<Frame>> {
24 self.collect()
25 }
26}
27
28impl<'a> Iterator for Frames<'a> {
29 type Item = ImageResult<Frame>;
30 fn next(&mut self) -> Option<ImageResult<Frame>> {
31 self.iterator.next()
32 }
33}
34
35#[derive(Clone)]
37pub struct Frame {
38 delay: Delay,
40 left: u32,
42 top: u32,
44 buffer: RgbaImage,
45}
46
47#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd)]
49pub struct Delay {
50 ratio: Ratio,
51}
52
53impl Frame {
54 pub fn new(buffer: RgbaImage) -> Frame {
56 Frame {
57 delay: Delay::from_ratio(Ratio { numer: 0, denom: 1 }),
58 left: 0,
59 top: 0,
60 buffer,
61 }
62 }
63
64 pub fn from_parts(buffer: RgbaImage, left: u32, top: u32, delay: Delay) -> Frame {
66 Frame {
67 delay,
68 left,
69 top,
70 buffer,
71 }
72 }
73
74 pub fn delay(&self) -> Delay {
76 self.delay
77 }
78
79 pub fn buffer(&self) -> &RgbaImage {
81 &self.buffer
82 }
83
84 pub fn buffer_mut(&mut self) -> &mut RgbaImage {
86 &mut self.buffer
87 }
88
89 pub fn into_buffer(self) -> RgbaImage {
91 self.buffer
92 }
93
94 pub fn left(&self) -> u32 {
96 self.left
97 }
98
99 pub fn top(&self) -> u32 {
101 self.top
102 }
103}
104
105impl Delay {
106 pub fn from_numer_denom_ms(numerator: u32, denominator: u32) -> Self {
115 Delay {
116 ratio: Ratio::new(numerator, denominator),
117 }
118 }
119
120 pub fn from_saturating_duration(duration: Duration) -> Self {
135 const MILLIS_BOUND: u128 = u32::max_value() as u128;
144
145 let millis = duration.as_millis().min(MILLIS_BOUND);
146 let submillis = (duration.as_nanos() % 1_000_000) as u32;
147
148 let max_b = if millis > 0 {
149 ((MILLIS_BOUND + 1) / (millis + 1)) as u32
150 } else {
151 MILLIS_BOUND as u32
152 };
153 let millis = millis as u32;
154
155 let (a, b) = Self::closest_bounded_fraction(max_b, submillis, 1_000_000);
156 Self::from_numer_denom_ms(a + b * millis, b)
157 }
158
159 pub fn numer_denom_ms(self) -> (u32, u32) {
164 (self.ratio.numer, self.ratio.denom)
165 }
166
167 pub(crate) fn from_ratio(ratio: Ratio) -> Self {
168 Delay { ratio }
169 }
170
171 pub(crate) fn into_ratio(self) -> Ratio {
172 self.ratio
173 }
174
175 fn closest_bounded_fraction(denom_bound: u32, nom: u32, denom: u32) -> (u32, u32) {
180 use std::cmp::Ordering::*;
181 assert!(0 < denom);
182 assert!(0 < denom_bound);
183 assert!(nom < denom);
184
185 fn compare_fraction((an, ad): (u64, u64), (bn, bd): (u64, u64)) -> Ordering {
191 (an * bd).cmp(&(bn * ad))
192 }
193
194 fn abs_diff_nom((an, ad): (u64, u64), (bn, bd): (u64, u64)) -> u64 {
196 let c0 = an * bd;
197 let c1 = ad * bn;
198
199 let d0 = c0.max(c1);
200 let d1 = c0.min(c1);
201 d0 - d1
202 }
203
204 let exact = (u64::from(nom), u64::from(denom));
205 let mut lower = (0u64, 1u64);
207 let mut upper = (1u64, 1u64);
209 let mut guess = (u64::from(nom * 2 > denom), 1u64);
211
212 loop {
215 if compare_fraction(guess, exact) == Equal {
217 break;
218 }
219
220 if u64::from(denom_bound) - lower.1 < upper.1 {
222 break;
223 }
224
225 let next = (lower.0 + upper.0, lower.1 + upper.1);
227 if compare_fraction(exact, next) == Less {
229 upper = next;
230 } else {
231 lower = next;
232 }
233
234 let g_diff_nom = abs_diff_nom(guess, exact);
240 let n_diff_nom = abs_diff_nom(next, exact);
242
243 if match (n_diff_nom / next.1).cmp(&(g_diff_nom / guess.1)) {
247 Less => true,
248 Greater => false,
249 Equal => {
254 compare_fraction(
255 (n_diff_nom % next.1, next.1),
256 (g_diff_nom % guess.1, guess.1),
257 ) == Less
258 }
259 } {
260 guess = next;
261 }
262 }
263
264 (guess.0 as u32, guess.1 as u32)
265 }
266}
267
268impl From<Delay> for Duration {
269 fn from(delay: Delay) -> Self {
270 let ratio = delay.into_ratio();
271 let ms = ratio.to_integer();
272 let rest = ratio.numer % ratio.denom;
273 let nanos = (u64::from(rest) * 1_000_000) / u64::from(ratio.denom);
274 Duration::from_millis(ms.into()) + Duration::from_nanos(nanos)
275 }
276}
277
278#[derive(Copy, Clone, Debug)]
279pub(crate) struct Ratio {
280 numer: u32,
281 denom: u32,
282}
283
284impl Ratio {
285 #[inline]
286 pub(crate) fn new(numerator: u32, denominator: u32) -> Self {
287 assert_ne!(denominator, 0);
288 Self {
289 numer: numerator,
290 denom: denominator,
291 }
292 }
293
294 #[inline]
295 pub(crate) fn to_integer(&self) -> u32 {
296 self.numer / self.denom
297 }
298}
299
300impl PartialEq for Ratio {
301 fn eq(&self, other: &Self) -> bool {
302 self.cmp(other) == Ordering::Equal
303 }
304}
305
306impl Eq for Ratio {}
307
308impl PartialOrd for Ratio {
309 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
310 Some(self.cmp(other))
311 }
312}
313
314impl Ord for Ratio {
315 fn cmp(&self, other: &Self) -> Ordering {
316 let a: u32 = self.numer;
324 let b: u32 = self.denom;
325 let c: u32 = other.numer;
326 let d: u32 = other.denom;
327
328 (a as u64 * d as u64).cmp(&(c as u64 * b as u64))
332 }
333}
334
335#[cfg(test)]
336mod tests {
337 use super::{Delay, Duration, Ratio};
338
339 #[test]
340 fn simple() {
341 let second = Delay::from_numer_denom_ms(1000, 1);
342 assert_eq!(Duration::from(second), Duration::from_secs(1));
343 }
344
345 #[test]
346 fn fps_30() {
347 let thirtieth = Delay::from_numer_denom_ms(1000, 30);
348 let duration = Duration::from(thirtieth);
349 assert_eq!(duration.as_secs(), 0);
350 assert_eq!(duration.subsec_millis(), 33);
351 assert_eq!(duration.subsec_nanos(), 33_333_333);
352 }
353
354 #[test]
355 fn duration_outlier() {
356 let oob = Duration::from_secs(0xFFFF_FFFF);
357 let delay = Delay::from_saturating_duration(oob);
358 assert_eq!(delay.numer_denom_ms(), (0xFFFF_FFFF, 1));
359 }
360
361 #[test]
362 fn duration_approx() {
363 let oob = Duration::from_millis(0xFFFF_FFFF) + Duration::from_micros(1);
364 let delay = Delay::from_saturating_duration(oob);
365 assert_eq!(delay.numer_denom_ms(), (0xFFFF_FFFF, 1));
366
367 let inbounds = Duration::from_millis(0xFFFF_FFFF) - Duration::from_micros(1);
368 let delay = Delay::from_saturating_duration(inbounds);
369 assert_eq!(delay.numer_denom_ms(), (0xFFFF_FFFF, 1));
370
371 let fine =
372 Duration::from_millis(0xFFFF_FFFF / 1000) + Duration::from_micros(0xFFFF_FFFF % 1000);
373 let delay = Delay::from_saturating_duration(fine);
374 assert_eq!(delay.into_ratio(), Ratio::new(0xFFFF_FFFF, 1000));
376 }
377
378 #[test]
379 fn precise() {
380 let exceed = Duration::from_secs(333) + Duration::from_nanos(333_333_333);
383 let delay = Delay::from_saturating_duration(exceed);
384 assert_eq!(Duration::from(delay), exceed);
385 }
386
387 #[test]
388 fn small() {
389 let delay = Delay::from_numer_denom_ms(1 << 16, (1 << 16) + 1);
391 let duration = Duration::from(delay);
392 assert_eq!(duration.as_millis(), 0);
393 let delay = Delay::from_saturating_duration(duration);
395 assert_eq!(delay.into_ratio().to_integer(), 0);
396 }
397}