1#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
7pub enum VideoFit {
8 #[default]
10 Contain,
11 Cover,
13 Stretch,
15}
16
17pub fn fit_rect(src: (f32, f32), dst: (f32, f32), fit: VideoFit) -> (f32, f32, f32, f32) {
20 let (sw, sh) = src;
21 let (dw, dh) = dst;
22 if sw <= 0.0 || sh <= 0.0 || dw <= 0.0 || dh <= 0.0 {
23 return (0.0, 0.0, 0.0, 0.0);
24 }
25 let (w, h) = match fit {
26 VideoFit::Stretch => (dw, dh),
27 VideoFit::Contain => {
28 let scale = (dw / sw).min(dh / sh);
29 (sw * scale, sh * scale)
30 }
31 VideoFit::Cover => {
32 let scale = (dw / sw).max(dh / sh);
33 (sw * scale, sh * scale)
34 }
35 };
36 ((dw - w) * 0.5, (dh - h) * 0.5, w, h)
37}
38
39#[cfg(test)]
40mod tests {
41 use super::*;
42
43 #[test]
44 fn contain_letterboxes_a_wide_frame_in_a_square() {
45 let (x, y, w, h) = fit_rect((200.0, 100.0), (100.0, 100.0), VideoFit::Contain);
46 assert_eq!((x, y, w, h), (0.0, 25.0, 100.0, 50.0));
47 }
48
49 #[test]
50 fn cover_crops_a_wide_frame_in_a_square() {
51 let (x, y, w, h) = fit_rect((200.0, 100.0), (100.0, 100.0), VideoFit::Cover);
52 assert_eq!((x, y, w, h), (-50.0, 0.0, 200.0, 100.0));
53 }
54
55 #[test]
56 fn stretch_ignores_the_aspect_ratio() {
57 let r = fit_rect((200.0, 100.0), (100.0, 300.0), VideoFit::Stretch);
58 assert_eq!(r, (0.0, 0.0, 100.0, 300.0));
59 }
60
61 #[test]
62 fn a_matching_box_is_left_alone() {
63 let r = fit_rect((640.0, 360.0), (640.0, 360.0), VideoFit::Contain);
64 assert_eq!(r, (0.0, 0.0, 640.0, 360.0));
65 }
66
67 #[test]
68 fn degenerate_sizes_paint_nothing() {
69 let empty = (0.0, 0.0, 0.0, 0.0);
70 assert_eq!(
71 fit_rect((0.0, 10.0), (10.0, 10.0), VideoFit::Contain),
72 empty
73 );
74 assert_eq!(fit_rect((10.0, 10.0), (0.0, 10.0), VideoFit::Cover), empty);
75 }
76}