1type Dims = (u32, u32);
3
4#[derive(Debug, Copy, Clone, PartialEq, Eq)]
6pub enum Resize {
7 Exact(u32, u32),
10 Fit(u32, u32),
13 FitEven(u32, u32),
20}
21
22impl Resize {
23 pub fn compute_for(self, dims: Dims) -> Option<Dims> {
33 match self {
34 Resize::Exact(w, h) => Some((w, h)),
35 Resize::Fit(w, h) => calculate_fit_dims(dims, (w, h)),
36 Resize::FitEven(w, h) => calculate_fit_dims_even(dims, (w, h)),
37 }
38 }
39}
40
41fn calculate_fit_dims(dims: (u32, u32), fit_dims: (u32, u32)) -> Option<(u32, u32)> {
53 let (w, h) = dims;
54 let (w_max, h_max) = fit_dims;
55 if w_max >= w && h_max >= h {
56 Some((w, h))
57 } else {
58 let wf = w_max as f32 / w as f32;
59 let hf = h_max as f32 / h as f32;
60 let f = wf.min(hf);
61 let (w_out, h_out) = ((w as f32 * f) as u32, (h as f32 * f) as u32);
62 if (w_out > 0) && (h_out > 0) {
63 Some((w_out, h_out))
64 } else {
65 None
66 }
67 }
68}
69
70fn calculate_fit_dims_even(dims: (u32, u32), fit_dims: (u32, u32)) -> Option<(u32, u32)> {
85 let (w, h) = dims;
86 let (mut w_max, mut h_max) = fit_dims;
87 while w_max > 0 && h_max > 0 {
88 let wf = w_max as f32 / w as f32;
89 let hf = h_max as f32 / h as f32;
90 let f = wf.min(hf).min(1.0);
91 let out_w = (w as f32 * f).round() as u32;
92 let out_h = (h as f32 * f).round() as u32;
93 if (out_w > 0) && (out_h > 0) {
94 if (out_w % 2 == 0) && (out_h % 2 == 0) {
95 return Some((out_w, out_h));
96 } else if wf < hf {
97 w_max -= 1;
98 } else {
99 h_max -= 1;
100 }
101 } else {
102 break;
103 }
104 }
105 None
106}
107
108#[cfg(test)]
109mod tests {
110 use super::*;
111
112 const TESTING_DIM_CANDIDATES: [u32; 8] = [0, 1, 2, 3, 8, 111, 256, 1000];
113
114 #[test]
115 fn calculate_fit_dims_works() {
116 let testset = generate_testset();
117 for ((w, h), (fit_w, fit_h)) in testset {
118 let out = calculate_fit_dims((w, h), (fit_w, fit_h));
119 if let Some((out_w, out_h)) = out {
120 let input_dim_zero = w == 0 || h == 0 || fit_w == 0 || fit_h == 0;
121 let output_dim_zero = out_w == 0 || out_h == 0;
122 assert!(
123 (input_dim_zero && output_dim_zero) || (!input_dim_zero && !output_dim_zero),
124 "computed dims are never zero unless the inputs dims were",
125 );
126 assert!(
127 (out_w <= fit_w) && (out_h <= fit_h),
128 "computed dims fit inside provided dims",
129 );
130 }
131 }
132 }
133
134 #[test]
135 fn calculate_fit_dims_even_works() {
136 let testset = generate_testset();
137 for ((w, h), (fit_w, fit_h)) in testset {
138 let out = calculate_fit_dims_even((w, h), (fit_w, fit_h));
139 if let Some((out_w, out_h)) = out {
140 let input_dim_zero = w == 0 || h == 0 || fit_w == 0 || fit_h == 0;
141 let output_dim_zero = out_w == 0 || out_h == 0;
142 assert!(
143 (input_dim_zero && output_dim_zero) || (!input_dim_zero && !output_dim_zero),
144 "computed dims are never zero unless the inputs dims were",
145 );
146 assert!(
147 (out_w % 2 == 0) && (out_h % 2 == 0),
148 "computed dims are even",
149 );
150 assert!(
151 (out_w <= fit_w) && (out_h <= fit_h),
152 "computed dims fit inside provided dims",
153 );
154 }
155 }
156 }
157
158 fn generate_testset() -> Vec<((u32, u32), (u32, u32))> {
159 let testing_dims = generate_testing_dims();
160 testing_dims
161 .iter()
162 .flat_map(|dims| testing_dims.iter().map(|fit_dims| (*dims, *fit_dims)))
163 .collect()
164 }
165
166 fn generate_testing_dims() -> Vec<(u32, u32)> {
167 TESTING_DIM_CANDIDATES
168 .iter()
169 .flat_map(|a| TESTING_DIM_CANDIDATES.iter().map(|b| (*a, *b)))
170 .collect()
171 }
172}