1use alloc::{boxed::Box, vec::Vec};
2use core::cmp::Ordering;
3use core::time::Duration;
4
5use crate::error::ImageResult;
6use crate::RgbaImage;
7
8pub struct Frames<'a> {
10 iterator: Box<dyn Iterator<Item = ImageResult<Frame>> + 'a>,
11}
12
13impl<'a> Frames<'a> {
14 #[must_use]
16 pub fn new(iterator: Box<dyn Iterator<Item = ImageResult<Frame>> + 'a>) -> Self {
17 Frames { iterator }
18 }
19
20 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
38pub struct Frame {
40 delay: Delay,
42 left: u32,
44 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#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd)]
82pub struct Delay {
83 ratio: Ratio,
84}
85
86impl Frame {
87 #[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 #[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 #[must_use]
111 pub fn delay(&self) -> Delay {
112 self.delay
113 }
114
115 #[must_use]
117 pub fn buffer(&self) -> &RgbaImage {
118 &self.buffer
119 }
120
121 pub fn buffer_mut(&mut self) -> &mut RgbaImage {
123 &mut self.buffer
124 }
125
126 #[must_use]
128 pub fn into_buffer(self) -> RgbaImage {
129 self.buffer
130 }
131
132 #[must_use]
134 pub fn left(&self) -> u32 {
135 self.left
136 }
137
138 #[must_use]
140 pub fn top(&self) -> u32 {
141 self.top
142 }
143}
144
145impl Delay {
146 #[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 #[must_use]
176 pub fn from_saturating_duration(duration: Duration) -> Self {
177 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 #[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 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 fn compare_fraction((an, ad): (u64, u64), (bn, bd): (u64, u64)) -> Ordering {
234 (an * bd).cmp(&(bn * ad))
235 }
236
237 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 let mut lower = (0u64, 1u64);
250 let mut upper = (1u64, 1u64);
252 let mut guess = (u64::from(nom * 2 > denom), 1u64);
254
255 loop {
258 if compare_fraction(guess, exact) == Equal {
260 break;
261 }
262
263 if u64::from(denom_bound) - lower.1 < upper.1 {
265 break;
266 }
267
268 let next = (lower.0 + upper.0, lower.1 + upper.1);
270 if compare_fraction(exact, next) == Less {
272 upper = next;
273 } else {
274 lower = next;
275 }
276
277 let g_diff_nom = abs_diff_nom(guess, exact);
283 let n_diff_nom = abs_diff_nom(next, exact);
285
286 if match (n_diff_nom / next.1).cmp(&(g_diff_nom / guess.1)) {
290 Less => true,
291 Greater => false,
292 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 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 (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 assert_eq!(delay.into_ratio(), Ratio::new(0xFFFF_FFFF, 1000));
428 }
429
430 #[test]
431 fn precise() {
432 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 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 let delay = Delay::from_saturating_duration(duration);
447 assert_eq!(delay.into_ratio().to_integer(), 0);
448 }
449}